[fusion-commits] r1499 - in trunk: . lib/OpenLayers
lib/OpenLayers/Lang lib/OpenLayers/theme/default
svn_fusion at osgeo.org
svn_fusion at osgeo.org
Fri Sep 5 10:39:22 EDT 2008
Author: madair
Date: 2008-09-05 10:39:19 -0400 (Fri, 05 Sep 2008)
New Revision: 1499
Modified:
trunk/fusion.cfg
trunk/lib/OpenLayers/Lang/en.js
trunk/lib/OpenLayers/Lang/fr.js
trunk/lib/OpenLayers/OpenLayers.js
trunk/lib/OpenLayers/OpenLayersCompressed.js
trunk/lib/OpenLayers/OpenLayersUncompressed.js
trunk/lib/OpenLayers/theme/default/style.css
Log:
re #119: interim commit to bring OL to trunk version prior to v2.7 release
Modified: trunk/fusion.cfg
===================================================================
--- trunk/fusion.cfg 2008-09-05 14:31:06 UTC (rev 1498)
+++ trunk/fusion.cfg 2008-09-05 14:39:19 UTC (rev 1499)
@@ -4,11 +4,10 @@
[first]
OpenLayers/SingleFile.js
OpenLayers.js
+OpenLayers/Util.js
+OpenLayers/Console.js
OpenLayers/BaseTypes.js
OpenLayers/BaseTypes/Class.js
-OpenLayers/Util.js
-OpenLayers/Ajax.js
-OpenLayers/Console.js
OpenLayers/BaseTypes/Size.js
OpenLayers/BaseTypes/Bounds.js
OpenLayers/BaseTypes/Element.js
@@ -18,6 +17,8 @@
[last]
[include]
+OpenLayers/Request.js
+OpenLayers/Ajax.js
OpenLayers/Events.js
OpenLayers/Map.js
OpenLayers/Layer.js
@@ -26,22 +27,24 @@
OpenLayers/Layer/WMS.js
OpenLayers/Layer/MapGuide.js
OpenLayers/Layer/MapServer.js
+OpenLayers/Layer/Vector.js
OpenLayers/Tile.js
OpenLayers/Tile/Image.js
OpenLayers/Control/OverviewMap.js
OpenLayers/Control/Navigation.js
OpenLayers/Control/PanZoom.js
OpenLayers/Control/ArgParser.js
-OpenLayers/Control/DragPan.js
-OpenLayers/Control/MousePosition.js
-OpenLayers/Control/ScaleLine.js
-OpenLayers/Handler/Box.js
+OpenLayers/Control/DrawFeature.js
OpenLayers/Handler/Click.js
-OpenLayers/Handler/MouseWheel.js
+OpenLayers/Handler/Point.js
+OpenLayers/Handler/Path.js
+OpenLayers/Handler/Polygon.js
OpenLayers/Projection.js
OpenLayers/Marker.js
OpenLayers/Icon.js
OpenLayers/Marker/Box.js
+OpenLayers/Tween.js
+OpenLayers/Lang.js
[exclude]
Firebug/firebug.js
Modified: trunk/lib/OpenLayers/Lang/en.js
===================================================================
--- trunk/lib/OpenLayers/Lang/en.js 2008-09-05 14:31:06 UTC (rev 1498)
+++ trunk/lib/OpenLayers/Lang/en.js 2008-09-05 14:39:19 UTC (rev 1499)
@@ -75,7 +75,7 @@
"To get rid of this message, select a new BaseLayer " +
"in the layer switcher in the upper-right corner.<br><br>" +
"Most likely, this is because the ${layerLib} library " +
- "script was either not correctly included.<br><br>" +
+ "script was not correctly included.<br><br>" +
"Developers: For help getting this working correctly, " +
"<a href='http://trac.openlayers.org/wiki/${layerLib}' " +
"target='_blank'>click here</a>",
Modified: trunk/lib/OpenLayers/Lang/fr.js
===================================================================
--- trunk/lib/OpenLayers/Lang/fr.js 2008-09-05 14:31:06 UTC (rev 1498)
+++ trunk/lib/OpenLayers/Lang/fr.js 2008-09-05 14:39:19 UTC (rev 1499)
@@ -14,6 +14,108 @@
*/
OpenLayers.Lang.fr = {
- 'overlays': "Couches de superposition"
+ 'unhandledRequest': "Requête non gérée, retournant ${statusText}",
+ 'permalink': "Permalien",
+
+ 'overlays': "Calques",
+
+ 'baseLayer': "Calque de base",
+
+ 'sameProjection':
+ "La carte de situation ne fonctionne que lorsque sa projection est la même que celle de la carte principale",
+
+ 'readNotImplemented': "Lecture non implémentée.",
+
+ 'writeNotImplemented': "Ecriture non implémentée.",
+
+ 'noFID': "Impossible de mettre à jour un objet sans identifiant (fid).",
+
+ 'errorLoadingGML': "Erreur au chargement du fichier GML ${url}",
+
+ 'browserNotSupported':
+ "Votre navigateur ne supporte pas le rendu vectoriel. Les renderers actuellement supportés sont : \n${renderers}",
+
+ 'componentShouldBe': "addFeatures : le composant devrait être de type ${geomType}",
+
+ // console message
+ 'getFeatureError':
+ "getFeatureFromEvent a été appelé sur un calque sans renderer. Cela signifie généralement que vous " +
+ "avez détruit cette couche, mais que vous avez conservé un handler qui lui était associé.",
+
+ // console message
+ 'minZoomLevelError':
+ "La propriété minZoomLevel doit seulement être utilisée " +
+ "pour des couches FixedZoomLevels-descendent. Le fait que " +
+ "cette couche WFS vérifie la présence de minZoomLevel " +
+ "est une relique du passé. Nous ne pouvons toutefois la " +
+ "supprimer sans casser des applications qui pourraient en dépendre." +
+ " C'est pourquoi nous la déprécions -- la vérification du minZoomLevel " +
+ "sera supprimée en version 3.0. A la place, merci d'utiliser " +
+ "les paramètres de résolutions min/max tel que décrit sur : " +
+ "http://trac.openlayers.org/wiki/SettingZoomLevels",
+
+ 'commitSuccess': "Transaction WFS : SUCCES ${response}",
+
+ 'commitFailed': "Transaction WFS : ECHEC ${response}",
+
+ 'googleWarning':
+ "La couche Google n'a pas été en mesure de se charger correctement.<br><br>" +
+ "Pour supprimer ce message, choisissez une nouvelle BaseLayer " +
+ "dans le sélecteur de couche en haut à droite.<br><br>" +
+ "Cela est possiblement causé par la non-inclusion de la " +
+ "librairie Google Maps, ou alors parce que la clé de l'API " +
+ "ne correspond pas à votre site.<br><br>" +
+ "Développeurs : pour savoir comment corriger ceci, " +
+ "<a href='http://trac.openlayers.org/wiki/Google' " +
+ "target='_blank'>cliquez ici</a>",
+
+ 'getLayerWarning':
+ "La couche ${layerType} n'est pas en mesure de se charger correctement.<br><br>" +
+ "Pour supprimer ce message, choisissez une nouvelle BaseLayer " +
+ "dans le sélecteur de couche en haut à droite.<br><br>" +
+ "Cela est possiblement causé par la non-inclusion de la " +
+ "librairie ${layerLib}.<br><br>" +
+ "Développeurs : pour savoir comment corriger ceci, " +
+ "<a href='http://trac.openlayers.org/wiki/${layerLib}' " +
+ "target='_blank'>cliquez ici</a>",
+
+ 'scale': "Echelle ~ 1 : ${scaleDenom}",
+
+ // console message
+ 'layerAlreadyAdded':
+ "Vous avez essayé d'ajouter à la carte le calque : ${layerName}, mais il est déjà présent",
+
+ // console message
+ 'reprojectDeprecated':
+ "Vous utilisez l'option 'reproject' " +
+ "sur la couche ${layerName}. Cette option est dépréciée : " +
+ "Son usage permettait d'afficher des données au dessus de couches raster commerciales." +
+ "Cette fonctionalité est maintenant supportée en utilisant le support de la projection " +
+ "Mercator Sphérique. Plus d'information est disponible sur " +
+ "http://trac.openlayers.org/wiki/SphericalMercator.",
+
+ // console message
+ 'methodDeprecated':
+ "Cette méthode est dépréciée, et sera supprimée à la version 3.0. " +
+ "Merci d'utiliser ${newMethod} à la place.",
+
+ // console message
+ 'boundsAddError': "Vous devez passer les deux valeurs x et y à la fonction add.",
+
+ // console message
+ 'lonlatAddError': "Vous devez passer les deux valeurs lon et lat à la fonction add.",
+
+ // console message
+ 'pixelAddError': "Vous devez passer les deux valeurs x et y à la fonction add.",
+
+ // console message
+ 'unsupportedGeometryType': "Type de géométrie non supporté : ${geomType}",
+
+ // console message
+ 'pagePositionFailed':
+ "OpenLayers.Util.pagePosition a échoué: l'élément d'id ${elemId} pourrait être mal positionné.",
+
+ 'end': ''
+
};
Modified: trunk/lib/OpenLayers/OpenLayers.js
===================================================================
--- trunk/lib/OpenLayers/OpenLayers.js 2008-09-05 14:31:06 UTC (rev 1498)
+++ trunk/lib/OpenLayers/OpenLayers.js 2008-09-05 14:39:19 UTC (rev 1499)
@@ -43,6 +43,15 @@
*
**/
+/**
+ * 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
+ */
/* ======================================================================
OpenLayers/SingleFile.js
====================================================================== */
@@ -102,7 +111,7 @@
var scriptName = OpenLayers._scriptName;
var scripts = document.getElementsByTagName('script');
- for (var i = 0; i < scripts.length; i++) {
+ for (var i=0, len=scripts.length; i<len; i++) {
var src = scripts[i].getAttribute('src');
if (src) {
var index = src.lastIndexOf(scriptName);
@@ -146,6 +155,8 @@
"Rico/Corner.js",
"Rico/Color.js",
"OpenLayers/Ajax.js",
+ "OpenLayers/Request.js",
+ "OpenLayers/Request/XMLHttpRequest.js",
"OpenLayers/Events.js",
"OpenLayers/Projection.js",
"OpenLayers/Map.js",
@@ -238,8 +249,12 @@
"OpenLayers/Renderer.js",
"OpenLayers/Renderer/Elements.js",
"OpenLayers/Renderer/SVG.js",
+ "OpenLayers/Renderer/Canvas.js",
"OpenLayers/Renderer/VML.js",
"OpenLayers/Layer/Vector.js",
+ "OpenLayers/Strategy.js",
+ "OpenLayers/Strategy/Fixed.js",
+ "OpenLayers/Protocol.js",
"OpenLayers/Layer/PointTrack.js",
"OpenLayers/Layer/GML.js",
"OpenLayers/Style.js",
@@ -257,9 +272,14 @@
"OpenLayers/Format/WFS.js",
"OpenLayers/Format/WKT.js",
"OpenLayers/Format/OSM.js",
+ "OpenLayers/Format/GPX.js",
"OpenLayers/Format/SLD.js",
"OpenLayers/Format/SLD/v1.js",
"OpenLayers/Format/SLD/v1_0_0.js",
+ "OpenLayers/Format/SLD/v1.js",
+ "OpenLayers/Format/Filter.js",
+ "OpenLayers/Format/Filter/v1.js",
+ "OpenLayers/Format/Filter/v1_0_0.js",
"OpenLayers/Format/Text.js",
"OpenLayers/Format/JSON.js",
"OpenLayers/Format/GeoJSON.js",
@@ -281,7 +301,7 @@
var allScriptTags = new Array(jsfiles.length);
}
var host = OpenLayers._getScriptLocation() + "lib/";
- for (var i = 0; i < jsfiles.length; i++) {
+ for (var i=0, len=jsfiles.length; i<len; i++) {
if (docWrite) {
allScriptTags[i] = "<script src='" + host + jsfiles[i] +
"'></script>";
@@ -394,7 +414,7 @@
camelize: function(str) {
var oStringList = str.split('-');
var camelizedString = oStringList[0];
- for (var i = 1; i < oStringList.length; i++) {
+ for (var i=1, len=oStringList.length; i<len; i++) {
var s = oStringList[i];
camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
}
@@ -428,7 +448,7 @@
}
var tokens = template.split("${");
var item, last, replacement;
- for(var i=1; i<tokens.length; i++) {
+ for(var i=1, len=tokens.length; i<len; i++) {
item = tokens[i];
last = item.indexOf("}");
if(last > 0) {
@@ -444,6 +464,31 @@
}
}
return tokens.join("");
+ },
+
+ /**
+ * Property: OpenLayers.String.numberRegEx
+ * Used to test strings as numbers.
+ */
+ numberRegEx: /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/,
+
+ /**
+ * APIFunction: OpenLayers.String.isNumeric
+ * Determine whether a string contains only a numeric value.
+ *
+ * Examples:
+ * (code)
+ * OpenLayers.String.isNumeric("6.02e23") // true
+ * OpenLayers.String.isNumeric("12 dozen") // false
+ * OpenLayers.String.isNumeric("4") // true
+ * OpenLayers.String.isNumeric(" 4 ") // false
+ * (end)
+ *
+ * Returns:
+ * {Boolean} String contains only a number.
+ */
+ isNumeric: function(value) {
+ return OpenLayers.String.numberRegEx.test(value);
}
};
@@ -812,7 +857,7 @@
};
var extended = {};
var parent;
- for(var i=0; i<arguments.length; ++i) {
+ for(var i=0, len=arguments.length; i<len; ++i) {
if(typeof arguments[i] == "function") {
// get the prototype of the superclass
parent = arguments[i].prototype;
@@ -863,7 +908,7 @@
OpenLayers.Class.inherit = function () {
var superClass = arguments[0];
var proto = new superClass(OpenLayers.Class.isPrototype);
- for (var i = 1; i < arguments.length; i++) {
+ for (var i=1, len=arguments.length; i<len; i++) {
if (typeof arguments[i] == "function") {
var mixin = arguments[i];
arguments[i] = new mixin(OpenLayers.Class.isPrototype);
@@ -893,7 +938,7 @@
OpenLayers.Util.getElement = function() {
var elements = [];
- for (var i = 0; i < arguments.length; i++) {
+ for (var i=0, len=arguments.length; i<len; i++) {
var element = arguments[i];
if (typeof element == 'string') {
element = document.getElementById(element);
@@ -927,7 +972,8 @@
* {Object} The destination object.
*/
OpenLayers.Util.extend = function(destination, source) {
- if(destination && source) {
+ destination = destination || {};
+ if(source) {
for(var property in source) {
var value = source[property];
if(value !== undefined) {
@@ -1012,7 +1058,7 @@
*/
OpenLayers.Util.indexOf = function(array, obj) {
- for(var i=0; i < array.length; i++) {
+ for(var i=0, len=array.length; i<len; i++) {
if (array[i] == obj) {
return i;
}
@@ -1075,10 +1121,9 @@
* Function: createDiv
* Creates a new div and optionally set some standard attributes.
* Null may be passed to each parameter if you do not wish to
- * set a particular attribute.d
+ * set a particular attribute.
+ * Note - zIndex is NOT set on the resulting div.
*
- * Note: zIndex is NOT set
- *
* Parameters:
* id - {String} An identifier for this element. If no id is
* passed an identifier will be created
@@ -1241,8 +1286,27 @@
*/
OpenLayers.Util.onImageLoadError = function() {
this._attempts = (this._attempts) ? (this._attempts + 1) : 1;
- if(this._attempts <= OpenLayers.IMAGE_RELOAD_ATTEMPTS) {
- this.src = this.src;
+ if (this._attempts <= OpenLayers.IMAGE_RELOAD_ATTEMPTS) {
+ var urls = this.urls;
+ if (urls && urls instanceof Array && urls.length > 1){
+ var src = this.src.toString();
+ var current_url, k;
+ for (k = 0; current_url = urls[k]; k++){
+ if(src.indexOf(current_url) != -1){
+ break;
+ }
+ }
+ var guess = Math.floor(urls.length * Math.random())
+ var new_url = urls[guess];
+ k = 0;
+ while(new_url == current_url && k++ < 4){
+ guess = Math.floor(urls.length * Math.random())
+ new_url = urls[guess];
+ }
+ this.src = src.replace(current_url, new_url);
+ } else {
+ this.src = this.src;
+ }
} else {
this.style.backgroundColor = OpenLayers.Util.onImageLoadErrorColor;
}
@@ -1294,8 +1358,8 @@
position, border, sizing,
opacity) {
- OpenLayers.Util.modifyDOMElement(div, id, px, sz,
- null, null, null, opacity);
+ OpenLayers.Util.modifyDOMElement(div, id, px, sz, position,
+ null, null, opacity);
var img = div.childNodes[0];
@@ -1306,8 +1370,9 @@
"relative", border);
if (OpenLayers.Util.alphaHack()) {
-
- div.style.display = "inline-block";
+ if(div.style.display != "none") {
+ div.style.display = "inline-block";
+ }
if (sizing == null) {
sizing = "scale";
}
@@ -1400,7 +1465,7 @@
* in place and returned by this function.
*/
OpenLayers.Util.applyDefaults = function (to, from) {
-
+ to = to || {};
/*
* FF/Windows < 2.0.0.13 reports "Illegal operation on WrappedNative
* prototype object" when calling hawOwnProperty if the source object is an
@@ -1453,7 +1518,7 @@
if (typeof value == 'object' && value.constructor == Array) {
/* value is an array; encode items and separate with "," */
var encodedItemArray = [];
- for (var itemIndex=0; itemIndex<value.length; itemIndex++) {
+ for (var itemIndex=0, len=value.length; itemIndex<len; itemIndex++) {
encodedItemArray.push(encodeURIComponent(value[itemIndex]));
}
encodedValue = encodedItemArray.join(",");
@@ -1504,7 +1569,7 @@
OpenLayers.Util.Try = function() {
var returnValue = null;
- for (var i = 0; i < arguments.length; i++) {
+ for (var i=0, len=arguments.length; i<len; i++) {
var lambda = arguments[i];
try {
returnValue = lambda();
@@ -1553,7 +1618,7 @@
*/
OpenLayers.Util._getNodes=function(nodes, tagName) {
var retArray = [];
- for (var i=0;i<nodes.length;i++) {
+ for (var i=0, len=nodes.length; i<len; i++) {
if (nodes[i].nodeName==tagName) {
retArray.push(nodes[i]);
}
@@ -1729,7 +1794,7 @@
var parameters = {};
var pairs = paramsString.split(/[&;]/);
- for(var i = 0; i < pairs.length; ++i) {
+ for(var i=0, len=pairs.length; i<len; ++i) {
var keyValue = pairs[i].split('=');
if (keyValue[0]) {
var key = decodeURIComponent(keyValue[0]);
@@ -1737,7 +1802,7 @@
//decode individual values
value = value.split(",");
- for(var j=0; j < value.length; j++) {
+ for(var j=0, jlen=value.length; j<jlen; j++) {
value[j] = decodeURIComponent(value[j]);
}
@@ -1924,12 +1989,7 @@
while(element) {
if(element == document.body) {
- // FIXME: IE, when passed 'window' as the forElement, treats it as
- // equal to document.body, but window.style fails, so getStyle
- // fails, so we are paranoid and check this here. This check should
- // probably move into element.getStyle in 2.6.
- if(child && child.style &&
- OpenLayers.Element.getStyle(child, 'position') == 'absolute') {
+ if(OpenLayers.Element.getStyle(child, 'position') == 'absolute') {
break;
}
}
@@ -1999,7 +2059,7 @@
//compare all keys (host, port, etc)
for(var key in urlObj1) {
if (options.test) {
- alert(key + "\n1:" + urlObj1[key] + "\n2:" + urlObj2[key]);
+ OpenLayers.Console.userError(key + "\n1:" + urlObj1[key] + "\n2:" + urlObj2[key]);
}
var val1 = urlObj1[key];
var val2 = urlObj2[key];
@@ -2232,17 +2292,21 @@
* scrollbars do not flicker
*
* Parameters:
+ * contentHTML
* size - {<OpenLayers.Size>} If either the 'w' or 'h' properties is
* specified, we fix that dimension of the div to be measured. This is
* useful in the case where we have a limit in one dimension and must
* therefore meaure the flow in the other dimension.
+ * options - {Object}
+ * displayClass - {String} Optional parameter. A CSS class name(s) string
+ * to provide the CSS context of the rendered content.
*
* Returns:
* {OpenLayers.Size}
*/
-OpenLayers.Util.getRenderedDimensions = function(contentHTML, size) {
+OpenLayers.Util.getRenderedDimensions = function(contentHTML, size, options) {
- var w = h = null;
+ var w, h;
// create temp container div with restricted size
var container = document.createElement("div");
@@ -2253,11 +2317,18 @@
//fix a dimension, if specified.
if (size) {
if (size.w) {
- w = container.style.width = size.w;
+ w = size.w;
+ container.style.width = w + "px";
} else if (size.h) {
- h = container.style.height = size.h;
+ h = size.h
+ container.style.height = h + "px";
}
}
+
+ //add css classes, if specified
+ if (options && options.displayClass) {
+ container.className = options.displayClass;
+ }
// create temp content div and assign content
var content = document.createElement("div");
@@ -2352,1003 +2423,340 @@
return scrollbarWidth;
};
/* ======================================================================
- OpenLayers/Ajax.js
+ Rico/Corner.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. */
-
-
-OpenLayers.ProxyHost = "";
-//OpenLayers.ProxyHost = "examples/proxy.cgi?url=";
-
-/**
- * Ajax reader for OpenLayers
- *
- * @uri url to do remote XML http get
- * @param {String} 'get' format params (x=y&a=b...)
- * @who object to handle callbacks for this request
- * @complete the function to be called on success
- * @failure the function to be called on failure
+/*
+ * This file has been edited substantially from the Rico-released
+ * version by the OpenLayers development team.
*
- * example usage from a caller:
+ * Copyright 2005 Sabre Airline Solutions
*
- * caps: function(request) {
- * -blah-
- * },
+ * 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
*
- * OpenLayers.loadURL(url,params,this,caps);
+ * 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.
*
- * Notice the above example does not provide an error handler; a default empty
- * handler is provided which merely logs the error if a failure handler is not
- * supplied
- *
- */
+ */
+OpenLayers.Rico = new Object();
+OpenLayers.Rico.Corner = {
+ round: function(e, options) {
+ e = OpenLayers.Util.getElement(e);
+ this._setOptions(options);
-/**
-* @param {} request
-*/
-OpenLayers.nullHandler = function(request) {
- alert(OpenLayers.i18n("unhandledRequest", {'statusText':request.statusText}));
-};
-
-/**
- * Function: loadURL
- * Background load a document.
- *
- * Parameters:
- * uri - {String} URI of source doc
- * params - {String} Params on get (doesnt seem to work)
- * caller - {Object} object which gets callbacks
- * onComplete - {Function} Optional callback for success. The callback
- * will be called with this set to caller and will receive the request
- * object as an argument.
- * onFailure - {Function} Optional callback for failure. In the event of
- * a failure, the callback will be called with this set to caller and will
- * receive the request object as an argument.
- *
- * Returns:
- * {XMLHttpRequest} The request object. To abort loading, call
- * request.abort().
- */
-OpenLayers.loadURL = function(uri, params, caller,
- onComplete, onFailure) {
-
- var success = (onComplete) ? OpenLayers.Function.bind(onComplete, caller)
- : OpenLayers.nullHandler;
-
- var failure = (onFailure) ? OpenLayers.Function.bind(onFailure, caller)
- : OpenLayers.nullHandler;
-
- // from prototype.js
- var request = new OpenLayers.Ajax.Request(
- uri,
- {
- method: 'get',
- parameters: params,
- onComplete: success,
- onFailure: failure
+ var color = this.options.color;
+ if ( this.options.color == "fromElement" ) {
+ color = this._background(e);
}
- );
- return request.transport;
-};
-
-/**
- * Function: parseXMLString
- * Parse XML into a doc structure
- *
- * Parameters:
- * text - {String}
- *
- * Returns:
- * {?} Parsed AJAX Responsev
- */
-OpenLayers.parseXMLString = function(text) {
-
- //MS sucks, if the server is bad it dies
- var index = text.indexOf('<');
- if (index > 0) {
- text = text.substring(index);
- }
-
- var ajaxResponse = OpenLayers.Util.Try(
- function() {
- var xmldom = new ActiveXObject('Microsoft.XMLDOM');
- xmldom.loadXML(text);
- return xmldom;
- },
- function() {
- return new DOMParser().parseFromString(text, 'text/xml');
- },
- function() {
- var req = new XMLHttpRequest();
- req.open("GET", "data:" + "text/xml" +
- ";charset=utf-8," + encodeURIComponent(text), false);
- if (req.overrideMimeType) {
- req.overrideMimeType("text/xml");
- }
- req.send(null);
- return req.responseXML;
+ var bgColor = this.options.bgColor;
+ if ( this.options.bgColor == "fromParent" ) {
+ bgColor = this._background(e.offsetParent);
}
- );
-
- return ajaxResponse;
-};
-
-
-/**
- * Namespace: OpenLayers.Ajax
- */
-OpenLayers.Ajax = {
-
- /**
- * Method: emptyFunction
- */
- emptyFunction: function () {},
-
- /**
- * Method: getTransport
- *
- * Returns:
- * {Object} Transport mechanism for whichever browser we're in, or false if
- * none available.
- */
- getTransport: function() {
- return OpenLayers.Util.Try(
- function() {return new XMLHttpRequest();},
- function() {return new ActiveXObject('Msxml2.XMLHTTP');},
- function() {return new ActiveXObject('Microsoft.XMLHTTP');}
- ) || false;
+ this._roundCornersImpl(e, color, bgColor);
},
- /**
- * Property: activeRequestCount
- * {Integer}
- */
- activeRequestCount: 0
-};
+ /** This is a helper function to change the background
+ * color of <div> that has had Rico rounded corners added.
+ *
+ * It seems we cannot just set the background color for the
+ * outer <div> so each <span> element used to create the
+ * corners must have its background color set individually.
+ *
+ * @param {DOM} theDiv - A child of the outer <div> that was
+ * supplied to the `round` method.
+ *
+ * @param {String} newColor - The new background color to use.
+ */
+ changeColor: function(theDiv, newColor) {
+
+ theDiv.style.backgroundColor = newColor;
-/**
- * Namespace: OpenLayers.Ajax.Responders
- * {Object}
- */
-OpenLayers.Ajax.Responders = {
-
- /**
- * Property: responders
- * {Array}
- */
- responders: [],
-
- /**
- * Method: register
- *
- * Parameters:
- * responderToAdd - {?}
- */
- register: function(responderToAdd) {
- for (var i = 0; i < this.responders.length; i++){
- if (responderToAdd == this.responders[i]){
- return;
- }
- }
- this.responders.push(responderToAdd);
- },
-
- /**
- * Method: unregister
- *
- * Parameters:
- * responderToRemove - {?}
- */
- unregister: function(responderToRemove) {
- OpenLayers.Util.removeItem(this.reponders, responderToRemove);
- },
-
- /**
- * Method: dispatch
- *
- * Parameters:
- * callback - {?}
- * request - {?}
- * transport - {?}
- */
- dispatch: function(callback, request, transport) {
- var responder;
- for (var i = 0; i < this.responders.length; i++) {
- responder = this.responders[i];
-
- if (responder[callback] &&
- typeof responder[callback] == 'function') {
- try {
- responder[callback].apply(responder,
- [request, transport]);
- } catch (e) {}
- }
- }
- }
-};
-
-OpenLayers.Ajax.Responders.register({
- /**
- * Function: onCreate
- */
- onCreate: function() {
- OpenLayers.Ajax.activeRequestCount++;
- },
-
- /**
- * Function: onComplete
- */
- onComplete: function() {
- OpenLayers.Ajax.activeRequestCount--;
- }
-});
-
-/**
- * Class: OpenLayers.Ajax.Base
- */
-OpenLayers.Ajax.Base = OpenLayers.Class({
-
- /**
- * Constructor: OpenLayers.Ajax.Base
- *
- * Parameters:
- * options - {Object}
- */
- initialize: function(options) {
- this.options = {
- method: 'post',
- asynchronous: true,
- contentType: 'application/xml',
- parameters: ''
- };
- OpenLayers.Util.extend(this.options, options || {});
+ var spanElements = theDiv.parentNode.getElementsByTagName("span");
- this.options.method = this.options.method.toLowerCase();
-
- if (typeof this.options.parameters == 'string') {
- this.options.parameters =
- OpenLayers.Util.getParameters(this.options.parameters);
+ for (var currIdx = 0; currIdx < spanElements.length; currIdx++) {
+ spanElements[currIdx].style.backgroundColor = newColor;
}
- }
-});
+ },
-/**
- * Class: OpenLayers.Ajax.Request
- *
- * Inherit:
- * - <OpenLayers.Ajax.Base>
- */
-OpenLayers.Ajax.Request = OpenLayers.Class(OpenLayers.Ajax.Base, {
- /**
- * Property: _complete
- *
- * {Boolean}
- */
- _complete: false,
-
- /**
- * Constructor: OpenLayers.Ajax.Request
- *
- * Parameters:
- * url - {String}
- * options - {Object}
- */
- initialize: function(url, options) {
- OpenLayers.Ajax.Base.prototype.initialize.apply(this, [options]);
+ /** This is a helper function to change the background
+ * opacity of <div> that has had Rico rounded corners added.
+ *
+ * See changeColor (above) for algorithm explanation
+ *
+ * @param {DOM} theDiv A child of the outer <div> that was
+ * supplied to the `round` method.
+ *
+ * @param {int} newOpacity The new opacity to use (0-1).
+ */
+ changeOpacity: function(theDiv, newOpacity) {
+
+ var mozillaOpacity = newOpacity;
+ var ieOpacity = 'alpha(opacity=' + newOpacity * 100 + ')';
- if (OpenLayers.ProxyHost && OpenLayers.String.startsWith(url, "http")) {
- url = OpenLayers.ProxyHost + encodeURIComponent(url);
- }
-
- this.transport = OpenLayers.Ajax.getTransport();
- this.request(url);
- },
+ theDiv.style.opacity = mozillaOpacity;
+ theDiv.style.filter = ieOpacity;
- /**
- * Method: request
- *
- * Parameters:
- * url - {String}
- */
- request: function(url) {
- this.url = url;
- this.method = this.options.method;
- var params = OpenLayers.Util.extend({}, this.options.parameters);
+ var spanElements = theDiv.parentNode.getElementsByTagName("span");
- if (this.method != 'get' && this.method != 'post') {
- // simulate other verbs over post
- params['_method'] = this.method;
- this.method = 'post';
+ for (var currIdx = 0; currIdx < spanElements.length; currIdx++) {
+ spanElements[currIdx].style.opacity = mozillaOpacity;
+ spanElements[currIdx].style.filter = ieOpacity;
}
- this.parameters = params;
-
- if (params = OpenLayers.Util.getParameterString(params)) {
- // when GET, append parameters to URL
- if (this.method == 'get') {
- this.url += ((this.url.indexOf('?') > -1) ? '&' : '?') + params;
- } else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
- params += '&_=';
- }
- }
- try {
- var response = new OpenLayers.Ajax.Response(this);
- if (this.options.onCreate) {
- this.options.onCreate(response);
- }
-
- OpenLayers.Ajax.Responders.dispatch('onCreate',
- this,
- response);
-
- this.transport.open(this.method.toUpperCase(),
- this.url,
- this.options.asynchronous);
-
- if (this.options.asynchronous) {
- window.setTimeout(
- OpenLayers.Function.bind(this.respondToReadyState, this, 1),
- 10);
- }
-
- this.transport.onreadystatechange =
- OpenLayers.Function.bind(this.onStateChange, this);
- this.setRequestHeaders();
-
- this.body = this.method == 'post' ?
- (this.options.postBody || params) : null;
- this.transport.send(this.body);
-
- // Force Firefox to handle ready state 4 for synchronous requests
- if (!this.options.asynchronous &&
- this.transport.overrideMimeType) {
- this.onStateChange();
- }
- } catch (e) {
- this.dispatchException(e);
- }
},
- /**
- * Method: onStateChange
- */
- onStateChange: function() {
- var readyState = this.transport.readyState;
- if (readyState > 1 && !((readyState == 4) && this._complete)) {
- this.respondToReadyState(this.transport.readyState);
- }
- },
-
- /**
- * Method: setRequestHeaders
- */
- setRequestHeaders: function() {
- var headers = {
- 'X-Requested-With': 'XMLHttpRequest',
- 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*',
- 'OpenLayers': true
- };
+ /** this function takes care of redoing the rico cornering
+ *
+ * you can't just call updateRicoCorners() again and pass it a
+ * new options string. you have to first remove the divs that
+ * rico puts on top and below the content div.
+ *
+ * @param {DOM} theDiv - A child of the outer <div> that was
+ * supplied to the `round` method.
+ *
+ * @param {Object} options - list of options
+ */
+ reRound: function(theDiv, options) {
- if (this.method == 'post') {
- headers['Content-type'] = this.options.contentType +
- (this.options.encoding ? '; charset=' + this.options.encoding : '');
-
- /* Force "Connection: close" for older Mozilla browsers to work
- * around a bug where XMLHttpRequest sends an incorrect
- * Content-length header. See Mozilla Bugzilla #246651.
- */
- if (this.transport.overrideMimeType &&
- (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005) {
- headers['Connection'] = 'close';
- }
- }
- // user-defined headers
- if (typeof this.options.requestHeaders == 'object') {
- var extras = this.options.requestHeaders;
-
- if (typeof extras.push == 'function') {
- for (var i = 0, length = extras.length; i < length; i += 2) {
- headers[extras[i]] = extras[i+1];
- }
- } else {
- for (var i in extras) {
- headers[i] = pair[i];
- }
- }
- }
-
- for (var name in headers) {
- this.transport.setRequestHeader(name, headers[name]);
- }
- },
-
- /**
- * Method: success
- *
- * Returns:
- * {Boolean} -
- */
- success: function() {
- var status = this.getStatus();
- return !status || (status >=200 && status < 300);
- },
-
- /**
- * Method: getStatus
- *
- * Returns:
- * {Integer} - Status
- */
- getStatus: function() {
- try {
- return this.transport.status || 0;
- } catch (e) {
- return 0;
- }
- },
+ var topRico = theDiv.parentNode.childNodes[0];
+ //theDiv would be theDiv.parentNode.childNodes[1]
+ var bottomRico = theDiv.parentNode.childNodes[2];
+
+ theDiv.parentNode.removeChild(topRico);
+ theDiv.parentNode.removeChild(bottomRico);
- /**
- * Method: respondToReadyState
- *
- * Parameters:
- * readyState - {?}
- */
- respondToReadyState: function(readyState) {
- var state = OpenLayers.Ajax.Request.Events[readyState];
- var response = new OpenLayers.Ajax.Response(this);
-
- if (state == 'Complete') {
- try {
- this._complete = true;
- (this.options['on' + response.status] ||
- this.options['on' + (this.success() ? 'Success' : 'Failure')] ||
- OpenLayers.Ajax.emptyFunction)(response);
- } catch (e) {
- this.dispatchException(e);
- }
-
- var contentType = response.getHeader('Content-type');
- }
-
- try {
- (this.options['on' + state] ||
- OpenLayers.Ajax.emptyFunction)(response);
- OpenLayers.Ajax.Responders.dispatch('on' + state,
- this,
- response);
- } catch (e) {
- this.dispatchException(e);
- }
-
- if (state == 'Complete') {
- // avoid memory leak in MSIE: clean up
- this.transport.onreadystatechange = OpenLayers.Ajax.emptyFunction;
- }
- },
-
- /**
- * Method: getHeader
- *
- * Parameters:
- * name - {String} Header name
- *
- * Returns:
- * {?} - response header for the given name
- */
- getHeader: function(name) {
- try {
- return this.transport.getResponseHeader(name);
- } catch (e) {
- return null;
- }
- },
+ this.round(theDiv.parentNode, options);
+ },
- /**
- * Method: dispatchException
- * If the optional onException function is set, execute it
- * and then dispatch the call to any other listener registered
- * for onException.
- *
- * If no optional onException function is set, we suspect that
- * the user may have also not used
- * OpenLayers.Ajax.Responders.register to register a listener
- * for the onException call. To make sure that something
- * gets done with this exception, only dispatch the call if there
- * are listeners.
- *
- * If you explicitly want to swallow exceptions, set
- * request.options.onException to an empty function (function(){})
- * or register an empty function with <OpenLayers.Ajax.Responders>
- * for onException.
- *
- * Parameters:
- * exception - {?}
- */
- dispatchException: function(exception) {
- var handler = this.options.onException;
- if(handler) {
- // call options.onException and alert any other listeners
- handler(this, exception);
- OpenLayers.Ajax.Responders.dispatch('onException', this, exception);
- } else {
- // check if there are any other listeners
- var listener = false;
- var responders = OpenLayers.Ajax.Responders.responders;
- for (var i = 0; i < responders.length; i++) {
- if(responders[i].onException) {
- listener = true;
- break;
- }
- }
- if(listener) {
- // call all listeners
- OpenLayers.Ajax.Responders.dispatch('onException', this, exception);
- } else {
- // let the exception through
- throw exception;
- }
- }
- }
-});
+ _roundCornersImpl: function(e, color, bgColor) {
+ if(this.options.border) {
+ this._renderBorder(e,bgColor);
+ }
+ if(this._isTopRounded()) {
+ this._roundTopCorners(e,color,bgColor);
+ }
+ if(this._isBottomRounded()) {
+ this._roundBottomCorners(e,color,bgColor);
+ }
+ },
-/**
- * Property: Events
- * {Array(String)}
- */
-OpenLayers.Ajax.Request.Events =
- ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
+ _renderBorder: function(el,bgColor) {
+ var borderValue = "1px solid " + this._borderColor(bgColor);
+ var borderL = "border-left: " + borderValue;
+ var borderR = "border-right: " + borderValue;
+ var style = "style='" + borderL + ";" + borderR + "'";
+ el.innerHTML = "<div " + style + ">" + el.innerHTML + "</div>";
+ },
-/**
- * Class: OpenLayers.Ajax.Response
- */
-OpenLayers.Ajax.Response = OpenLayers.Class({
+ _roundTopCorners: function(el, color, bgColor) {
+ var corner = this._createCorner(bgColor);
+ for(var i=0 ; i < this.options.numSlices ; i++ ) {
+ corner.appendChild(this._createCornerSlice(color,bgColor,i,"top"));
+ }
+ el.style.paddingTop = 0;
+ el.insertBefore(corner,el.firstChild);
+ },
- /**
- * Property: status
- *
- * {Integer}
- */
- status: 0,
-
+ _roundBottomCorners: function(el, color, bgColor) {
+ var corner = this._createCorner(bgColor);
+ for(var i=(this.options.numSlices-1) ; i >= 0 ; i-- ) {
+ corner.appendChild(this._createCornerSlice(color,bgColor,i,"bottom"));
+ }
+ el.style.paddingBottom = 0;
+ el.appendChild(corner);
+ },
- /**
- * Property: statusText
- *
- * {String}
- */
- statusText: '',
-
- /**
- * Constructor: OpenLayers.Ajax.Response
- *
- * Parameters:
- * request - {Object}
- */
- initialize: function(request) {
- this.request = request;
- var transport = this.transport = request.transport,
- readyState = this.readyState = transport.readyState;
-
- if ((readyState > 2 &&
- !(!!(window.attachEvent && !window.opera))) ||
- readyState == 4) {
- this.status = this.getStatus();
- this.statusText = this.getStatusText();
- this.responseText = transport.responseText == null ?
- '' : String(transport.responseText);
- }
-
- if(readyState == 4) {
- var xml = transport.responseXML;
- this.responseXML = xml === undefined ? null : xml;
- }
- },
-
- /**
- * Method: getStatus
- */
- getStatus: OpenLayers.Ajax.Request.prototype.getStatus,
-
- /**
- * Method: getStatustext
- *
- * Returns:
- * {String} - statusText
- */
- getStatusText: function() {
- try {
- return this.transport.statusText || '';
- } catch (e) {
- return '';
- }
- },
-
- /**
- * Method: getHeader
- */
- getHeader: OpenLayers.Ajax.Request.prototype.getHeader,
-
- /**
- * Method: getResponseHeader
- *
- * Returns:
- * {?} - response header for given name
- */
- getResponseHeader: function(name) {
- return this.transport.getResponseHeader(name);
- }
-});
+ _createCorner: function(bgColor) {
+ var corner = document.createElement("div");
+ corner.style.backgroundColor = (this._isTransparent() ? "transparent" : bgColor);
+ return corner;
+ },
+ _createCornerSlice: function(color,bgColor, n, position) {
+ var slice = document.createElement("span");
-/**
- * Function: getElementsByTagNameNS
- *
- * Parameters:
- * parentnode - {?}
- * nsuri - {?}
- * nsprefix - {?}
- * tagname - {?}
- *
- * Returns:
- * {?}
- */
-OpenLayers.Ajax.getElementsByTagNameNS = function(parentnode, nsuri,
- nsprefix, tagname) {
- var elem = null;
- if (parentnode.getElementsByTagNameNS) {
- elem = parentnode.getElementsByTagNameNS(nsuri, tagname);
- } else {
- elem = parentnode.getElementsByTagName(nsprefix + ':' + tagname);
- }
- return elem;
-};
+ var inStyle = slice.style;
+ inStyle.backgroundColor = color;
+ inStyle.display = "block";
+ inStyle.height = "1px";
+ inStyle.overflow = "hidden";
+ inStyle.fontSize = "1px";
+ var borderColor = this._borderColor(color,bgColor);
+ if ( this.options.border && n == 0 ) {
+ inStyle.borderTopStyle = "solid";
+ inStyle.borderTopWidth = "1px";
+ inStyle.borderLeftWidth = "0px";
+ inStyle.borderRightWidth = "0px";
+ inStyle.borderBottomWidth = "0px";
+ inStyle.height = "0px"; // assumes css compliant box model
+ inStyle.borderColor = borderColor;
+ }
+ else if(borderColor) {
+ inStyle.borderColor = borderColor;
+ inStyle.borderStyle = "solid";
+ inStyle.borderWidth = "0px 1px";
+ }
-/**
- * Function: serializeXMLToString
- * Wrapper function around XMLSerializer, which doesn't exist/work in
- * IE/Safari. We need to come up with a way to serialize in those browser:
- * for now, these browsers will just fail. #535, #536
- *
- * Parameters:
- * xmldom {XMLNode} xml dom to serialize
- *
- * Returns:
- * {?}
- */
-OpenLayers.Ajax.serializeXMLToString = function(xmldom) {
- var serializer = new XMLSerializer();
- var data = serializer.serializeToString(xmldom);
- return data;
-};
-/* ======================================================================
- OpenLayers/Console.js
- ====================================================================== */
+ if ( !this.options.compact && (n == (this.options.numSlices-1)) ) {
+ inStyle.height = "2px";
+ }
+ this._setMargin(slice, n, position);
+ this._setBorder(slice, n, position);
+ return slice;
+ },
-/* 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. */
+ _setOptions: function(options) {
+ this.options = {
+ corners : "all",
+ color : "fromElement",
+ bgColor : "fromParent",
+ blend : true,
+ border : false,
+ compact : false
+ };
+ OpenLayers.Util.extend(this.options, options || {});
-/**
- * Namespace: OpenLayers.Console
- * The OpenLayers.Console namespace is used for debugging and error logging.
- * If the Firebug Lite (../Firebug/firebug.js) is included before this script,
- * calls to OpenLayers.Console methods will get redirected to window.console.
- * This makes use of the Firebug extension where available and allows for
- * cross-browser debugging Firebug style.
- *
- * Note:
- * Note that behavior will differ with the Firebug extention and Firebug Lite.
- * Most notably, the Firebug Lite console does not currently allow for
- * hyperlinks to code or for clicking on object to explore their properties.
- *
- */
-OpenLayers.Console = {
- /**
- * Create empty functions for all console methods. The real value of these
- * properties will be set if Firebug Lite (../Firebug/firebug.js script) is
- * included. We explicitly require the Firebug Lite script to trigger
- * functionality of the OpenLayers.Console methods.
- */
-
- /**
- * APIFunction: log
- * Log an object in the console. The Firebug Lite console logs string
- * representation of objects. Given multiple arguments, they will
- * be cast to strings and logged with a space delimiter. If the first
- * argument is a string with printf-like formatting, subsequent arguments
- * will be used in string substitution. Any additional arguments (beyond
- * the number substituted in a format string) will be appended in a space-
- * delimited line.
- *
- * Parameters:
- * object - {Object}
- */
- log: function() {},
+ this.options.numSlices = this.options.compact ? 2 : 4;
+ if ( this._isTransparent() ) {
+ this.options.blend = false;
+ }
+ },
- /**
- * APIFunction: debug
- * Writes a message to the console, including a hyperlink to the line
- * where it was called.
- *
- * May be called with multiple arguments as with OpenLayers.Console.log().
- *
- * Parameters:
- * object - {Object}
- */
- debug: function() {},
+ _whichSideTop: function() {
+ if ( this._hasString(this.options.corners, "all", "top") ) {
+ return "";
+ }
+ if ( this.options.corners.indexOf("tl") >= 0 && this.options.corners.indexOf("tr") >= 0 ) {
+ return "";
+ }
+ if (this.options.corners.indexOf("tl") >= 0) {
+ return "left";
+ } else if (this.options.corners.indexOf("tr") >= 0) {
+ return "right";
+ }
+ return "";
+ },
- /**
- * APIFunction: info
- * Writes a message to the console with the visual "info" icon and color
- * coding and a hyperlink to the line where it was called.
- *
- * May be called with multiple arguments as with OpenLayers.Console.log().
- *
- * Parameters:
- * object - {Object}
- */
- info: function() {},
+ _whichSideBottom: function() {
+ if ( this._hasString(this.options.corners, "all", "bottom") ) {
+ return "";
+ }
+ if ( this.options.corners.indexOf("bl")>=0 && this.options.corners.indexOf("br")>=0 ) {
+ return "";
+ }
- /**
- * APIFunction: warn
- * Writes a message to the console with the visual "warning" icon and
- * color coding and a hyperlink to the line where it was called.
- *
- * May be called with multiple arguments as with OpenLayers.Console.log().
- *
- * Parameters:
- * object - {Object}
- */
- warn: function() {},
+ if(this.options.corners.indexOf("bl") >=0) {
+ return "left";
+ } else if(this.options.corners.indexOf("br")>=0) {
+ return "right";
+ }
+ return "";
+ },
- /**
- * APIFunction: error
- * Writes a message to the console with the visual "error" icon and color
- * coding and a hyperlink to the line where it was called.
- *
- * May be called with multiple arguments as with OpenLayers.Console.log().
- *
- * Parameters:
- * object - {Object}
- */
- error: function() {},
+ _borderColor : function(color,bgColor) {
+ if ( color == "transparent" ) {
+ return bgColor;
+ } else if ( this.options.border ) {
+ return this.options.border;
+ } else if ( this.options.blend ) {
+ return this._blend( bgColor, color );
+ } else {
+ return "";
+ }
+ },
- /**
- * APIFunction: assert
- * Tests that an expression is true. If not, it will write a message to
- * the console and throw an exception.
- *
- * May be called with multiple arguments as with OpenLayers.Console.log().
- *
- * Parameters:
- * object - {Object}
- */
- assert: function() {},
- /**
- * APIFunction: dir
- * Prints an interactive listing of all properties of the object. This
- * looks identical to the view that you would see in the DOM tab.
- *
- * Parameters:
- * object - {Object}
- */
- dir: function() {},
+ _setMargin: function(el, n, corners) {
+ var marginSize = this._marginSize(n);
+ var whichSide = corners == "top" ? this._whichSideTop() : this._whichSideBottom();
- /**
- * APIFunction: dirxml
- * Prints the XML source tree of an HTML or XML element. This looks
- * identical to the view that you would see in the HTML tab. You can click
- * on any node to inspect it in the HTML tab.
- *
- * Parameters:
- * object - {Object}
- */
- dirxml: function() {},
+ if ( whichSide == "left" ) {
+ el.style.marginLeft = marginSize + "px"; el.style.marginRight = "0px";
+ }
+ else if ( whichSide == "right" ) {
+ el.style.marginRight = marginSize + "px"; el.style.marginLeft = "0px";
+ }
+ else {
+ el.style.marginLeft = marginSize + "px"; el.style.marginRight = marginSize + "px";
+ }
+ },
- /**
- * APIFunction: trace
- * Prints an interactive stack trace of JavaScript execution at the point
- * where it is called. The stack trace details the functions on the stack,
- * as well as the values that were passed as arguments to each function.
- * You can click each function to take you to its source in the Script tab,
- * and click each argument value to inspect it in the DOM or HTML tabs.
- *
- */
- trace: function() {},
+ _setBorder: function(el,n,corners) {
+ var borderSize = this._borderSize(n);
+ var whichSide = corners == "top" ? this._whichSideTop() : this._whichSideBottom();
+ if ( whichSide == "left" ) {
+ el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = "0px";
+ }
+ else if ( whichSide == "right" ) {
+ el.style.borderRightWidth = borderSize + "px"; el.style.borderLeftWidth = "0px";
+ }
+ else {
+ el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = borderSize + "px";
+ }
+ if (this.options.border != false) {
+ el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = borderSize + "px";
+ }
+ },
- /**
- * APIFunction: group
- * Writes a message to the console and opens a nested block to indent all
- * future messages sent to the console. Call OpenLayers.Console.groupEnd()
- * to close the block.
- *
- * May be called with multiple arguments as with OpenLayers.Console.log().
- *
- * Parameters:
- * object - {Object}
- */
- group: function() {},
+ _marginSize: function(n) {
+ if ( this._isTransparent() ) {
+ return 0;
+ }
+ var marginSizes = [ 5, 3, 2, 1 ];
+ var blendedMarginSizes = [ 3, 2, 1, 0 ];
+ var compactMarginSizes = [ 2, 1 ];
+ var smBlendedMarginSizes = [ 1, 0 ];
- /**
- * APIFunction: groupEnd
- * Closes the most recently opened block created by a call to
- * OpenLayers.Console.group
- */
- groupEnd: function() {},
-
- /**
- * APIFunction: time
- * Creates a new timer under the given name. Call
- * OpenLayers.Console.timeEnd(name)
- * with the same name to stop the timer and print the time elapsed.
- *
- * Parameters:
- * name - {String}
- */
- time: function() {},
+ if ( this.options.compact && this.options.blend ) {
+ return smBlendedMarginSizes[n];
+ } else if ( this.options.compact ) {
+ return compactMarginSizes[n];
+ } else if ( this.options.blend ) {
+ return blendedMarginSizes[n];
+ } else {
+ return marginSizes[n];
+ }
+ },
- /**
- * APIFunction: timeEnd
- * Stops a timer created by a call to OpenLayers.Console.time(name) and
- * writes the time elapsed.
- *
- * Parameters:
- * name - {String}
- */
- timeEnd: function() {},
+ _borderSize: function(n) {
+ var transparentBorderSizes = [ 5, 3, 2, 1 ];
+ var blendedBorderSizes = [ 2, 1, 1, 1 ];
+ var compactBorderSizes = [ 1, 0 ];
+ var actualBorderSizes = [ 0, 2, 0, 0 ];
- /**
- * APIFunction: profile
- * Turns on the JavaScript profiler. The optional argument title would
- * contain the text to be printed in the header of the profile report.
- *
- * This function is not currently implemented in Firebug Lite.
- *
- * Parameters:
- * title - {String} Optional title for the profiler
- */
- profile: function() {},
+ if ( this.options.compact && (this.options.blend || this._isTransparent()) ) {
+ return 1;
+ } else if ( this.options.compact ) {
+ return compactBorderSizes[n];
+ } else if ( this.options.blend ) {
+ return blendedBorderSizes[n];
+ } else if ( this.options.border ) {
+ return actualBorderSizes[n];
+ } else if ( this._isTransparent() ) {
+ return transparentBorderSizes[n];
+ }
+ return 0;
+ },
- /**
- * APIFunction: profileEnd
- * Turns off the JavaScript profiler and prints its report.
- *
- * This function is not currently implemented in Firebug Lite.
- */
- profileEnd: function() {},
-
- /**
- * APIFunction: count
- * Writes the number of times that the line of code where count was called
- * was executed. The optional argument title will print a message in
- * addition to the number of the count.
- *
- * This function is not currently implemented in Firebug Lite.
- *
- * Parameters:
- * title - {String} Optional title to be printed with count
- */
- count: function() {},
-
- CLASS_NAME: "OpenLayers.Console"
+ _hasString: function(str) { for(var i=1 ; i<arguments.length ; i++) if (str.indexOf(arguments[i]) >= 0) { return true; } return false; },
+ _blend: function(c1, c2) { var cc1 = OpenLayers.Rico.Color.createFromHex(c1); cc1.blend(OpenLayers.Rico.Color.createFromHex(c2)); return cc1; },
+ _background: function(el) { try { return OpenLayers.Rico.Color.createColorFromBackground(el).asHex(); } catch(err) { return "#ffffff"; } },
+ _isTransparent: function() { return this.options.color == "transparent"; },
+ _isTopRounded: function() { return this._hasString(this.options.corners, "all", "top", "tl", "tr"); },
+ _isBottomRounded: function() { return this._hasString(this.options.corners, "all", "bottom", "bl", "br"); },
+ _hasSingleTextChild: function(el) { return el.childNodes.length == 1 && el.childNodes[0].nodeType == 3; }
};
-
-/**
- * Execute an anonymous function to extend the OpenLayers.Console namespace
- * if the firebug.js script is included. This closure is used so that the
- * "scripts" and "i" variables don't pollute the global namespace.
- */
-(function() {
- /**
- * If Firebug Lite is included (before this script), re-route all
- * OpenLayers.Console calls to the console object.
- */
- if(window.console) {
- var scripts = document.getElementsByTagName("script");
- for(var i=0; i<scripts.length; ++i) {
- if(scripts[i].src.indexOf("firebug.js") != -1) {
- OpenLayers.Util.extend(OpenLayers.Console, console);
- break;
- }
- }
- }
-})();
/* ======================================================================
- OpenLayers/BaseTypes/Size.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. */
-
-/**
- * Class: OpenLayers.Size
- * Instances of this class represent a width/height pair
- */
-OpenLayers.Size = OpenLayers.Class({
-
- /**
- * APIProperty: w
- * {Number} width
- */
- w: 0.0,
-
- /**
- * APIProperty: h
- * {Number} height
- */
- h: 0.0,
-
-
- /**
- * Constructor: OpenLayers.Size
- * Create an instance of OpenLayers.Size
- *
- * Parameters:
- * w - {Number} width
- * h - {Number} height
- */
- initialize: function(w, h) {
- this.w = parseFloat(w);
- this.h = parseFloat(h);
- },
-
- /**
- * Method: toString
- * Return the string representation of a size object
- *
- * Returns:
- * {String} The string representation of OpenLayers.Size object.
- * (ex. <i>"w=55,h=66"</i>)
- */
- toString:function() {
- return ("w=" + this.w + ",h=" + this.h);
- },
-
- /**
- * APIMethod: clone
- * Create a clone of this size object
- *
- * Returns:
- * {<OpenLayers.Size>} A new OpenLayers.Size object with the same w and h
- * values
- */
- clone:function() {
- return new OpenLayers.Size(this.w, this.h);
- },
-
- /**
- *
- * APIMethod: equals
- * Determine where this size is equal to another
- *
- * Parameters:
- * sz - {<OpenLayers.Size>}
- *
- * Returns:
- * {Boolean} The passed in size has the same h and w properties as this one.
- * Note that if sz passed in is null, returns false.
- *
- */
- equals:function(sz) {
- var equals = false;
- if (sz != null) {
- equals = ((this.w == sz.w && this.h == sz.h) ||
- (isNaN(this.w) && isNaN(this.h) && isNaN(sz.w) && isNaN(sz.h)));
- }
- return equals;
- },
-
- CLASS_NAME: "OpenLayers.Size"
-});
-/* ======================================================================
OpenLayers/BaseTypes/Bounds.js
====================================================================== */
@@ -3574,6 +2982,48 @@
},
/**
+ * Method: scale
+ * Scales the bounds around a pixel or lonlat. Note that the new
+ * bounds may return non-integer properties, even if a pixel
+ * is passed.
+ *
+ * Parameters:
+ * ratio - {Float}
+ * origin - {<OpenLayers.Pixel> or <OpenLayers.LonLat>}
+ * Default is center.
+ *
+ * Returns:
+ * {<OpenLayers.Bound>} A new bounds that is scaled by ratio
+ * from origin.
+ */
+
+ scale: function(ratio, origin){
+ if(origin == null){
+ origin = this.getCenterLonLat();
+ }
+
+ var bounds = [];
+
+ var origx,origy;
+
+ // get origin coordinates
+ if(origin.CLASS_NAME == "OpenLayers.LonLat"){
+ origx = origin.lon;
+ origy = origin.lat;
+ } else {
+ origx = origin.x;
+ origy = origin.y;
+ }
+
+ var left = (this.left - origx) * ratio + origx;
+ 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);
+ },
+
+ /**
* APIMethod: add
*
* Parameters:
@@ -3987,7 +3437,7 @@
* element - {DOMElement} Actually user can pass any number of elements
*/
toggle: function() {
- for (var i = 0; i < arguments.length; i++) {
+ for (var i=0, len=arguments.length; i<len; i++) {
var element = OpenLayers.Util.getElement(arguments[i]);
var display = OpenLayers.Element.visible(element) ? 'hide'
: 'show';
@@ -4004,7 +3454,7 @@
* element - {DOMElement} Actually user can pass any number of elements
*/
hide: function() {
- for (var i = 0; i < arguments.length; i++) {
+ for (var i=0, len=arguments.length; i<len; i++) {
var element = OpenLayers.Util.getElement(arguments[i]);
element.style.display = 'none';
}
@@ -4018,7 +3468,7 @@
* element - {DOMElement} Actually user can pass any number of elements
*/
show: function() {
- for (var i = 0; i < arguments.length; i++) {
+ for (var i=0, len=arguments.length; i<len; i++) {
var element = OpenLayers.Util.getElement(arguments[i]);
element.style.display = '';
}
@@ -4083,6 +3533,86 @@
},
/**
+ * Function: hasClass
+ * Tests if an element has the given CSS class name.
+ *
+ * Parameters:
+ * element - {DOMElement} A DOM element node.
+ * name - {String} The CSS class name to search for.
+ *
+ * Returns:
+ * {Boolean} The element has the given class name.
+ */
+ hasClass: function(element, name) {
+ var names = element.className;
+ return (!!names && new RegExp("(^|\\s)" + name + "(\\s|$)").test(names));
+ },
+
+ /**
+ * Function: addClass
+ * Add a CSS class name to an element. Safe where element already has
+ * the class name.
+ *
+ * Parameters:
+ * element - {DOMElement} A DOM element node.
+ * name - {String} The CSS class name to add.
+ *
+ * Returns:
+ * {DOMElement} The element.
+ */
+ addClass: function(element, name) {
+ if(!OpenLayers.Element.hasClass(element, name)) {
+ element.className += (element.className ? " " : "") + name;
+ }
+ return element;
+ },
+
+ /**
+ * Function: removeClass
+ * Remove a CSS class name from an element. Safe where element does not
+ * have the class name.
+ *
+ * Parameters:
+ * element - {DOMElement} A DOM element node.
+ * name - {String} The CSS class name to remove.
+ *
+ * Returns:
+ * {DOMElement} The element.
+ */
+ removeClass: function(element, name) {
+ var names = element.className;
+ if(names) {
+ element.className = OpenLayers.String.trim(
+ names.replace(
+ new RegExp("(^|\\s+)" + name + "(\\s+|$)"), " "
+ )
+ );
+ }
+ return element;
+ },
+
+ /**
+ * Function: toggleClass
+ * Remove a CSS class name from an element if it exists. Add the class name
+ * if it doesn't exist.
+ *
+ * Parameters:
+ * element - {DOMElement} A DOM element node.
+ * name - {String} The CSS class name to toggle.
+ *
+ * Returns:
+ * {DOMElement} The element.
+ */
+ toggleClass: function(element, name) {
+ if(OpenLayers.Element.hasClass(element, name)) {
+ OpenLayers.Element.removeClass(element, name);
+ } else {
+ OpenLayers.Element.addClass(element, name);
+ }
+ return element;
+ },
+
+ /**
* APIFunction: getStyle
*
* Parameters:
@@ -4094,25 +3624,29 @@
*/
getStyle: function(element, style) {
element = OpenLayers.Util.getElement(element);
- var value = element.style[OpenLayers.String.camelize(style)];
- if (!value) {
- if (document.defaultView &&
- document.defaultView.getComputedStyle) {
-
- var css = document.defaultView.getComputedStyle(element, null);
- value = css ? css.getPropertyValue(style) : null;
- } else if (element.currentStyle) {
- value = element.currentStyle[OpenLayers.String.camelize(style)];
+
+ var value = null;
+ if (element && element.style) {
+ value = element.style[OpenLayers.String.camelize(style)];
+ if (!value) {
+ if (document.defaultView &&
+ document.defaultView.getComputedStyle) {
+
+ var css = document.defaultView.getComputedStyle(element, null);
+ value = css ? css.getPropertyValue(style) : null;
+ } else if (element.currentStyle) {
+ value = element.currentStyle[OpenLayers.String.camelize(style)];
+ }
}
+
+ var positions = ['left', 'top', 'right', 'bottom'];
+ if (window.opera &&
+ (OpenLayers.Util.indexOf(positions,style) != -1) &&
+ (OpenLayers.Element.getStyle(element, 'position') == 'static')) {
+ value = 'auto';
+ }
}
- var positions = ['left', 'top', 'right', 'bottom'];
- if (window.opera &&
- (OpenLayers.Util.indexOf(positions,style) != -1) &&
- (OpenLayers.Element.getStyle(element, 'position') == 'static')) {
- value = 'auto';
- }
-
return value == 'auto' ? null : value;
}
@@ -4237,7 +3771,8 @@
/**
* APIMethod: transform
- * Transform the LonLat object from source to dest.
+ * Transform the LonLat object from source to dest. This transformation is
+ * *in place*: if you want a *new* lonlat, use .clone() first.
*
* Parameters:
* source - {<OpenLayers.Projection>} Source projection.
@@ -4430,6 +3965,343 @@
CLASS_NAME: "OpenLayers.Pixel"
});
/* ======================================================================
+ OpenLayers/BaseTypes/Size.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. */
+
+/**
+ * Class: OpenLayers.Size
+ * Instances of this class represent a width/height pair
+ */
+OpenLayers.Size = OpenLayers.Class({
+
+ /**
+ * APIProperty: w
+ * {Number} width
+ */
+ w: 0.0,
+
+ /**
+ * APIProperty: h
+ * {Number} height
+ */
+ h: 0.0,
+
+
+ /**
+ * Constructor: OpenLayers.Size
+ * Create an instance of OpenLayers.Size
+ *
+ * Parameters:
+ * w - {Number} width
+ * h - {Number} height
+ */
+ initialize: function(w, h) {
+ this.w = parseFloat(w);
+ this.h = parseFloat(h);
+ },
+
+ /**
+ * Method: toString
+ * Return the string representation of a size object
+ *
+ * Returns:
+ * {String} The string representation of OpenLayers.Size object.
+ * (ex. <i>"w=55,h=66"</i>)
+ */
+ toString:function() {
+ return ("w=" + this.w + ",h=" + this.h);
+ },
+
+ /**
+ * APIMethod: clone
+ * Create a clone of this size object
+ *
+ * Returns:
+ * {<OpenLayers.Size>} A new OpenLayers.Size object with the same w and h
+ * values
+ */
+ clone:function() {
+ return new OpenLayers.Size(this.w, this.h);
+ },
+
+ /**
+ *
+ * APIMethod: equals
+ * Determine where this size is equal to another
+ *
+ * Parameters:
+ * sz - {<OpenLayers.Size>}
+ *
+ * Returns:
+ * {Boolean} The passed in size has the same h and w properties as this one.
+ * Note that if sz passed in is null, returns false.
+ *
+ */
+ equals:function(sz) {
+ var equals = false;
+ if (sz != null) {
+ equals = ((this.w == sz.w && this.h == sz.h) ||
+ (isNaN(this.w) && isNaN(this.h) && isNaN(sz.w) && isNaN(sz.h)));
+ }
+ return equals;
+ },
+
+ CLASS_NAME: "OpenLayers.Size"
+});
+/* ======================================================================
+ OpenLayers/Console.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. */
+
+/**
+ * Namespace: OpenLayers.Console
+ * The OpenLayers.Console namespace is used for debugging and error logging.
+ * If the Firebug Lite (../Firebug/firebug.js) is included before this script,
+ * calls to OpenLayers.Console methods will get redirected to window.console.
+ * This makes use of the Firebug extension where available and allows for
+ * cross-browser debugging Firebug style.
+ *
+ * Note:
+ * Note that behavior will differ with the Firebug extention and Firebug Lite.
+ * Most notably, the Firebug Lite console does not currently allow for
+ * hyperlinks to code or for clicking on object to explore their properties.
+ *
+ */
+OpenLayers.Console = {
+ /**
+ * Create empty functions for all console methods. The real value of these
+ * properties will be set if Firebug Lite (../Firebug/firebug.js script) is
+ * included. We explicitly require the Firebug Lite script to trigger
+ * functionality of the OpenLayers.Console methods.
+ */
+
+ /**
+ * APIFunction: log
+ * Log an object in the console. The Firebug Lite console logs string
+ * representation of objects. Given multiple arguments, they will
+ * be cast to strings and logged with a space delimiter. If the first
+ * argument is a string with printf-like formatting, subsequent arguments
+ * will be used in string substitution. Any additional arguments (beyond
+ * the number substituted in a format string) will be appended in a space-
+ * delimited line.
+ *
+ * Parameters:
+ * object - {Object}
+ */
+ log: function() {},
+
+ /**
+ * APIFunction: debug
+ * Writes a message to the console, including a hyperlink to the line
+ * where it was called.
+ *
+ * May be called with multiple arguments as with OpenLayers.Console.log().
+ *
+ * Parameters:
+ * object - {Object}
+ */
+ debug: function() {},
+
+ /**
+ * APIFunction: info
+ * Writes a message to the console with the visual "info" icon and color
+ * coding and a hyperlink to the line where it was called.
+ *
+ * May be called with multiple arguments as with OpenLayers.Console.log().
+ *
+ * Parameters:
+ * object - {Object}
+ */
+ info: function() {},
+
+ /**
+ * APIFunction: warn
+ * Writes a message to the console with the visual "warning" icon and
+ * color coding and a hyperlink to the line where it was called.
+ *
+ * May be called with multiple arguments as with OpenLayers.Console.log().
+ *
+ * Parameters:
+ * object - {Object}
+ */
+ warn: function() {},
+
+ /**
+ * APIFunction: error
+ * Writes a message to the console with the visual "error" icon and color
+ * coding and a hyperlink to the line where it was called.
+ *
+ * May be called with multiple arguments as with OpenLayers.Console.log().
+ *
+ * Parameters:
+ * object - {Object}
+ */
+ error: function() {},
+
+ /**
+ * APIFunction: userError
+ * A single interface for showing error messages to the user. The default
+ * behavior is a Javascript alert, though this can be overridden by
+ * reassigning OpenLayers.Console.userError to a different function.
+ *
+ * Expects a single error message
+ *
+ * Parameters:
+ * object - {Object}
+ */
+ userError: function(error) {
+ alert(error);
+ },
+
+ /**
+ * APIFunction: assert
+ * Tests that an expression is true. If not, it will write a message to
+ * the console and throw an exception.
+ *
+ * May be called with multiple arguments as with OpenLayers.Console.log().
+ *
+ * Parameters:
+ * object - {Object}
+ */
+ assert: function() {},
+
+ /**
+ * APIFunction: dir
+ * Prints an interactive listing of all properties of the object. This
+ * looks identical to the view that you would see in the DOM tab.
+ *
+ * Parameters:
+ * object - {Object}
+ */
+ dir: function() {},
+
+ /**
+ * APIFunction: dirxml
+ * Prints the XML source tree of an HTML or XML element. This looks
+ * identical to the view that you would see in the HTML tab. You can click
+ * on any node to inspect it in the HTML tab.
+ *
+ * Parameters:
+ * object - {Object}
+ */
+ dirxml: function() {},
+
+ /**
+ * APIFunction: trace
+ * Prints an interactive stack trace of JavaScript execution at the point
+ * where it is called. The stack trace details the functions on the stack,
+ * as well as the values that were passed as arguments to each function.
+ * You can click each function to take you to its source in the Script tab,
+ * and click each argument value to inspect it in the DOM or HTML tabs.
+ *
+ */
+ trace: function() {},
+
+ /**
+ * APIFunction: group
+ * Writes a message to the console and opens a nested block to indent all
+ * future messages sent to the console. Call OpenLayers.Console.groupEnd()
+ * to close the block.
+ *
+ * May be called with multiple arguments as with OpenLayers.Console.log().
+ *
+ * Parameters:
+ * object - {Object}
+ */
+ group: function() {},
+
+ /**
+ * APIFunction: groupEnd
+ * Closes the most recently opened block created by a call to
+ * OpenLayers.Console.group
+ */
+ groupEnd: function() {},
+
+ /**
+ * APIFunction: time
+ * Creates a new timer under the given name. Call
+ * OpenLayers.Console.timeEnd(name)
+ * with the same name to stop the timer and print the time elapsed.
+ *
+ * Parameters:
+ * name - {String}
+ */
+ time: function() {},
+
+ /**
+ * APIFunction: timeEnd
+ * Stops a timer created by a call to OpenLayers.Console.time(name) and
+ * writes the time elapsed.
+ *
+ * Parameters:
+ * name - {String}
+ */
+ timeEnd: function() {},
+
+ /**
+ * APIFunction: profile
+ * Turns on the JavaScript profiler. The optional argument title would
+ * contain the text to be printed in the header of the profile report.
+ *
+ * This function is not currently implemented in Firebug Lite.
+ *
+ * Parameters:
+ * title - {String} Optional title for the profiler
+ */
+ profile: function() {},
+
+ /**
+ * APIFunction: profileEnd
+ * Turns off the JavaScript profiler and prints its report.
+ *
+ * This function is not currently implemented in Firebug Lite.
+ */
+ profileEnd: function() {},
+
+ /**
+ * APIFunction: count
+ * Writes the number of times that the line of code where count was called
+ * was executed. The optional argument title will print a message in
+ * addition to the number of the count.
+ *
+ * This function is not currently implemented in Firebug Lite.
+ *
+ * Parameters:
+ * title - {String} Optional title to be printed with count
+ */
+ count: function() {},
+
+ CLASS_NAME: "OpenLayers.Console"
+};
+
+/**
+ * Execute an anonymous function to extend the OpenLayers.Console namespace
+ * if the firebug.js script is included. This closure is used so that the
+ * "scripts" and "i" variables don't pollute the global namespace.
+ */
+(function() {
+ /**
+ * If Firebug Lite is included (before this script), re-route all
+ * OpenLayers.Console calls to the console object.
+ */
+ if(window.console) {
+ var scripts = document.getElementsByTagName("script");
+ for(var i=0, len=scripts.length; i<len; ++i) {
+ if(scripts[i].src.indexOf("firebug.js") != -1) {
+ OpenLayers.Util.extend(OpenLayers.Console, console);
+ break;
+ }
+ }
+ }
+})();
+/* ======================================================================
OpenLayers/Control.js
====================================================================== */
@@ -4478,7 +4350,7 @@
* > },
* >
* > notice: function (bounds) {
- * > alert(bounds);
+ * > OpenLayers.Console.userError(bounds);
* > }
* > });
* > map.addControl(control);
@@ -4611,7 +4483,9 @@
if(this.eventListeners instanceof Object) {
this.events.on(this.eventListeners);
}
- this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
+ if (this.id == null) {
+ this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
+ }
},
/**
@@ -5100,6 +4974,1841 @@
*/
OpenLayers.i18n = OpenLayers.Lang.translate;
/* ======================================================================
+ OpenLayers/Popup.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. */
+
+
+/**
+ * Class: OpenLayers.Popup
+ * A popup is a small div that can opened and closed on the map.
+ * Typically opened in response to clicking on a marker.
+ * See <OpenLayers.Marker>. Popup's don't require their own
+ * layer and are added the the map using the <OpenLayers.Map.addPopup>
+ * method.
+ *
+ * Example:
+ * (code)
+ * popup = new OpenLayers.Popup("chicken",
+ * new OpenLayers.LonLat(5,40),
+ * new OpenLayers.Size(200,200),
+ * "example popup",
+ * true);
+ *
+ * map.addPopup(popup);
+ * (end)
+ */
+OpenLayers.Popup = OpenLayers.Class({
+
+ /**
+ * Property: events
+ * {<OpenLayers.Events>} custom event manager
+ */
+ events: null,
+
+ /** Property: id
+ * {String} the unique identifier assigned to this popup.
+ */
+ id: "",
+
+ /**
+ * Property: lonlat
+ * {<OpenLayers.LonLat>} the position of this popup on the map
+ */
+ lonlat: null,
+
+ /**
+ * Property: div
+ * {DOMElement} the div that contains this popup.
+ */
+ div: null,
+
+ /**
+ * Property: contentSize
+ * {<OpenLayers.Size>} the width and height of the content.
+ */
+ contentSize: null,
+
+ /**
+ * Property: size
+ * {<OpenLayers.Size>} the width and height of the popup.
+ */
+ size: null,
+
+ /**
+ * Property: contentHTML
+ * {String} An HTML string for this popup to display.
+ */
+ contentHTML: null,
+
+ /**
+ * Property: backgroundColor
+ * {String} the background color used by the popup.
+ */
+ backgroundColor: "",
+
+ /**
+ * Property: opacity
+ * {float} the opacity of this popup (between 0.0 and 1.0)
+ */
+ opacity: "",
+
+ /**
+ * Property: border
+ * {String} the border size of the popup. (eg 2px)
+ */
+ border: "",
+
+ /**
+ * Property: contentDiv
+ * {DOMElement} a reference to the element that holds the content of
+ * the div.
+ */
+ contentDiv: null,
+
+ /**
+ * Property: groupDiv
+ * {DOMElement} First and only child of 'div'. The group Div contains the
+ * 'contentDiv' and the 'closeDiv'.
+ */
+ groupDiv: null,
+
+ /**
+ * Property: closeDiv
+ * {DOMElement} the optional closer image
+ */
+ closeDiv: null,
+
+ /**
+ * APIProperty: autoSize
+ * {Boolean} Resize the popup to auto-fit the contents.
+ * Default is false.
+ */
+ autoSize: false,
+
+ /**
+ * APIProperty: minSize
+ * {<OpenLayers.Size>} Minimum size allowed for the popup's contents.
+ */
+ minSize: null,
+
+ /**
+ * APIProperty: maxSize
+ * {<OpenLayers.Size>} Maximum size allowed for the popup's contents.
+ */
+ maxSize: null,
+
+ /**
+ * Property: displayClass
+ * {String} The CSS class of the popup.
+ */
+ displayClass: "olPopup",
+
+ /**
+ * Property: contentDisplayClass
+ * {String} The CSS class of the popup content div.
+ */
+ contentDisplayClass: "olPopupContent",
+
+ /**
+ * Property: padding
+ * {int or <OpenLayers.Bounds>} An extra opportunity to specify internal
+ * padding of the content div inside the popup. This was originally
+ * confused with the css padding as specified in style.css's
+ * 'olPopupContent' class. We would like to get rid of this altogether,
+ * except that it does come in handy for the framed and anchoredbubble
+ * popups, who need to maintain yet another barrier between their
+ * content and the outer border of the popup itself.
+ *
+ * Note that in order to not break API, we must continue to support
+ * this property being set as an integer. Really, though, we'd like to
+ * have this specified as a Bounds object so that user can specify
+ * distinct left, top, right, bottom paddings. With the 3.0 release
+ * we can make this only a bounds.
+ */
+ padding: 0,
+
+ /**
+ * Method: fixPadding
+ * To be removed in 3.0, this function merely helps us to deal with the
+ * case where the user may have set an integer value for padding,
+ * instead of an <OpenLayers.Bounds> object.
+ */
+ fixPadding: function() {
+ if (typeof this.padding == "number") {
+ this.padding = new OpenLayers.Bounds(
+ this.padding, this.padding, this.padding, this.padding
+ );
+ }
+ },
+
+ /**
+ * APIProperty: panMapIfOutOfView
+ * {Boolean} When drawn, pan map such that the entire popup is visible in
+ * the current viewport (if necessary).
+ * Default is false.
+ */
+ panMapIfOutOfView: false,
+
+ /**
+ * Property: map
+ * {<OpenLayers.Map>} this gets set in Map.js when the popup is added to the map
+ */
+ map: null,
+
+ /**
+ * Constructor: OpenLayers.Popup
+ * Create a popup.
+ *
+ * Parameters:
+ * id - {String} a unqiue identifier for this popup. If null is passed
+ * an identifier will be automatically generated.
+ * lonlat - {<OpenLayers.LonLat>} The position on the map the popup will
+ * be shown.
+ * contentSize - {<OpenLayers.Size>} The size of the content.
+ * contentHTML - {String} An HTML string to display inside the
+ * popup.
+ * closeBox - {Boolean} Whether to display a close box inside
+ * the popup.
+ * closeBoxCallback - {Function} Function to be called on closeBox click.
+ */
+ initialize:function(id, lonlat, contentSize, contentHTML, closeBox, closeBoxCallback) {
+ if (id == null) {
+ id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
+ }
+
+ this.id = id;
+ this.lonlat = lonlat;
+
+ this.contentSize = (contentSize != null) ? contentSize
+ : new OpenLayers.Size(
+ OpenLayers.Popup.WIDTH,
+ OpenLayers.Popup.HEIGHT);
+ if (contentHTML != null) {
+ this.contentHTML = contentHTML;
+ }
+ this.backgroundColor = OpenLayers.Popup.COLOR;
+ this.opacity = OpenLayers.Popup.OPACITY;
+ this.border = OpenLayers.Popup.BORDER;
+
+ this.div = OpenLayers.Util.createDiv(this.id, null, null,
+ null, null, null, "hidden");
+ this.div.className = this.displayClass;
+
+ var groupDivId = this.id + "_GroupDiv";
+ this.groupDiv = OpenLayers.Util.createDiv(groupDivId, null, null,
+ null, "relative", null,
+ "hidden");
+
+ var id = this.div.id + "_contentDiv";
+ this.contentDiv = OpenLayers.Util.createDiv(id, null, this.contentSize.clone(),
+ null, "relative");
+ this.contentDiv.className = this.contentDisplayClass;
+ this.groupDiv.appendChild(this.contentDiv);
+ this.div.appendChild(this.groupDiv);
+
+ if (closeBox) {
+ this.addCloseBox(closeBoxCallback);
+ }
+
+ this.registerEvents();
+ },
+
+ /**
+ * Method: destroy
+ * nullify references to prevent circular references and memory leaks
+ */
+ destroy: function() {
+
+ this.id = null;
+ this.lonlat = null;
+ this.size = null;
+ this.contentHTML = null;
+
+ this.backgroundColor = null;
+ this.opacity = null;
+ this.border = null;
+
+ this.events.destroy();
+ this.events = null;
+
+ if (this.closeDiv) {
+ OpenLayers.Event.stopObservingElement(this.closeDiv);
+ this.groupDiv.removeChild(this.closeDiv);
+ }
+ this.closeDiv = null;
+
+ this.div.removeChild(this.groupDiv);
+ this.groupDiv = null;
+
+ if (this.map != null) {
+ this.map.removePopup(this);
+ }
+ this.map = null;
+ this.div = null;
+
+ this.autoSize = null;
+ this.minSize = null;
+ this.maxSize = null;
+ this.padding = null;
+ this.panMapIfOutOfView = null;
+ },
+
+ /**
+ * Method: draw
+ * Constructs the elements that make up the popup.
+ *
+ * Parameters:
+ * px - {<OpenLayers.Pixel>} the position the popup in pixels.
+ *
+ * Returns:
+ * {DOMElement} Reference to a div that contains the drawn popup
+ */
+ draw: function(px) {
+ if (px == null) {
+ if ((this.lonlat != null) && (this.map != null)) {
+ px = this.map.getLayerPxFromLonLat(this.lonlat);
+ }
+ }
+
+ //listen to movestart, moveend to disable overflow (FF bug)
+ if (OpenLayers.Util.getBrowserName() == 'firefox') {
+ this.map.events.register("movestart", this, function() {
+ var style = document.defaultView.getComputedStyle(
+ this.contentDiv, null
+ );
+ var currentOverflow = style.getPropertyValue("overflow");
+ if (currentOverflow != "hidden") {
+ this.contentDiv._oldOverflow = currentOverflow;
+ this.contentDiv.style.overflow = "hidden";
+ }
+ });
+ this.map.events.register("moveend", this, function() {
+ var oldOverflow = this.contentDiv._oldOverflow;
+ if (oldOverflow) {
+ this.contentDiv.style.overflow = oldOverflow;
+ this.contentDiv._oldOverflow = null;
+ }
+ });
+ }
+
+ this.moveTo(px);
+ if (!this.autoSize && !this.size) {
+ this.setSize(this.contentSize);
+ }
+ this.setBackgroundColor();
+ this.setOpacity();
+ this.setBorder();
+ this.setContentHTML();
+
+ if (this.panMapIfOutOfView) {
+ this.panIntoView();
+ }
+
+ return this.div;
+ },
+
+ /**
+ * Method: updatePosition
+ * if the popup has a lonlat and its map members set,
+ * then have it move itself to its proper position
+ */
+ updatePosition: function() {
+ if ((this.lonlat) && (this.map)) {
+ var px = this.map.getLayerPxFromLonLat(this.lonlat);
+ if (px) {
+ this.moveTo(px);
+ }
+ }
+ },
+
+ /**
+ * Method: moveTo
+ *
+ * Parameters:
+ * px - {<OpenLayers.Pixel>} the top and left position of the popup div.
+ */
+ moveTo: function(px) {
+ if ((px != null) && (this.div != null)) {
+ this.div.style.left = px.x + "px";
+ this.div.style.top = px.y + "px";
+ }
+ },
+
+ /**
+ * Method: visible
+ *
+ * Returns:
+ * {Boolean} Boolean indicating whether or not the popup is visible
+ */
+ visible: function() {
+ return OpenLayers.Element.visible(this.div);
+ },
+
+ /**
+ * Method: toggle
+ * Toggles visibility of the popup.
+ */
+ toggle: function() {
+ if (this.visible()) {
+ this.hide();
+ } else {
+ this.show();
+ }
+ },
+
+ /**
+ * Method: show
+ * Makes the popup visible.
+ */
+ show: function() {
+ OpenLayers.Element.show(this.div);
+
+ if (this.panMapIfOutOfView) {
+ this.panIntoView();
+ }
+ },
+
+ /**
+ * Method: hide
+ * Makes the popup invisible.
+ */
+ hide: function() {
+ OpenLayers.Element.hide(this.div);
+ },
+
+ /**
+ * Method: setSize
+ * Used to adjust the size of the popup.
+ *
+ * Parameters:
+ * contentSize - {<OpenLayers.Size>} the new size for the popup's
+ * contents div (in pixels).
+ */
+ setSize:function(contentSize) {
+ this.size = contentSize.clone();
+
+ // if our contentDiv has a css 'padding' set on it by a stylesheet, we
+ // must add that to the desired "size".
+ var contentDivPadding = this.getContentDivPadding();
+ var wPadding = contentDivPadding.left + contentDivPadding.right;
+ var hPadding = contentDivPadding.top + contentDivPadding.bottom;
+
+ // take into account the popup's 'padding' property
+ this.fixPadding();
+ wPadding += this.padding.left + this.padding.right;
+ hPadding += this.padding.top + this.padding.bottom;
+
+ // make extra space for the close div
+ if (this.closeDiv) {
+ var closeDivWidth = parseInt(this.closeDiv.style.width);
+ wPadding += closeDivWidth + contentDivPadding.right;
+ }
+
+ //increase size of the main popup div to take into account the
+ // users's desired padding and close div.
+ this.size.w += wPadding;
+ this.size.h += hPadding;
+
+ //now if our browser is IE, we need to actually make the contents
+ // div itself bigger to take its own padding into effect. this makes
+ // me want to shoot someone, but so it goes.
+ if (OpenLayers.Util.getBrowserName() == "msie") {
+ this.contentSize.w +=
+ contentDivPadding.left + contentDivPadding.right;
+ this.contentSize.h +=
+ contentDivPadding.bottom + contentDivPadding.top;
+ }
+
+ if (this.div != null) {
+ this.div.style.width = this.size.w + "px";
+ this.div.style.height = this.size.h + "px";
+ }
+ if (this.contentDiv != null){
+ this.contentDiv.style.width = contentSize.w + "px";
+ this.contentDiv.style.height = contentSize.h + "px";
+ }
+ },
+
+ /**
+ * APIMethod: updateSize
+ * Auto size the popup so that it precisely fits its contents (as
+ * determined by this.contentDiv.innerHTML). Popup size will, of
+ * course, be limited by the available space on the current map
+ */
+ updateSize: function() {
+
+ // determine actual render dimensions of the contents by putting its
+ // contents into a fake contentDiv (for the CSS) and then measuring it
+ var preparedHTML = "<div class='" + this.contentDisplayClass+ "'>" +
+ this.contentDiv.innerHTML +
+ "<div>";
+ var realSize = OpenLayers.Util.getRenderedDimensions(
+ preparedHTML, null, { displayClass: this.displayClass }
+ );
+
+ // is the "real" size of the div is safe to display in our map?
+ var safeSize = this.getSafeContentSize(realSize);
+
+ var newSize = null;
+ if (safeSize.equals(realSize)) {
+ //real size of content is small enough to fit on the map,
+ // so we use real size.
+ newSize = realSize;
+
+ } else {
+
+ //make a new OL.Size object with the clipped dimensions
+ // set or null if not clipped.
+ var fixedSize = new OpenLayers.Size();
+ fixedSize.w = (safeSize.w < realSize.w) ? safeSize.w : null;
+ fixedSize.h = (safeSize.h < realSize.h) ? safeSize.h : null;
+
+ if (fixedSize.w && fixedSize.h) {
+ //content is too big in both directions, so we will use
+ // max popup size (safeSize), knowing well that it will
+ // overflow both ways.
+ newSize = safeSize;
+ } else {
+ //content is clipped in only one direction, so we need to
+ // run getRenderedDimensions() again with a fixed dimension
+ var clippedSize = OpenLayers.Util.getRenderedDimensions(
+ preparedHTML, fixedSize,
+ { displayClass: this.contentDisplayClass }
+ );
+
+ //if the clipped size is still the same as the safeSize,
+ // that means that our content must be fixed in the
+ // offending direction. If overflow is 'auto', this means
+ // we are going to have a scrollbar for sure, so we must
+ // adjust for that.
+ //
+ var currentOverflow = OpenLayers.Element.getStyle(
+ this.contentDiv, "overflow"
+ );
+ if ( (currentOverflow != "hidden") &&
+ (clippedSize.equals(safeSize)) ) {
+ var scrollBar = OpenLayers.Util.getScrollbarWidth();
+ if (fixedSize.w) {
+ clippedSize.h += scrollBar;
+ } else {
+ clippedSize.w += scrollBar;
+ }
+ }
+
+ newSize = this.getSafeContentSize(clippedSize);
+ }
+ }
+ this.setSize(newSize);
+ },
+
+ /**
+ * Method: setBackgroundColor
+ * Sets the background color of the popup.
+ *
+ * Parameters:
+ * color - {String} the background color. eg "#FFBBBB"
+ */
+ setBackgroundColor:function(color) {
+ if (color != undefined) {
+ this.backgroundColor = color;
+ }
+
+ if (this.div != null) {
+ this.div.style.backgroundColor = this.backgroundColor;
+ }
+ },
+
+ /**
+ * Method: setOpacity
+ * Sets the opacity of the popup.
+ *
+ * Parameters:
+ * opacity - {float} A value between 0.0 (transparent) and 1.0 (solid).
+ */
+ setOpacity:function(opacity) {
+ if (opacity != undefined) {
+ this.opacity = opacity;
+ }
+
+ if (this.div != null) {
+ // for Mozilla and Safari
+ this.div.style.opacity = this.opacity;
+
+ // for IE
+ this.div.style.filter = 'alpha(opacity=' + this.opacity*100 + ')';
+ }
+ },
+
+ /**
+ * Method: setBorder
+ * Sets the border style of the popup.
+ *
+ * Parameters:
+ * border - {String} The border style value. eg 2px
+ */
+ setBorder:function(border) {
+ if (border != undefined) {
+ this.border = border;
+ }
+
+ if (this.div != null) {
+ this.div.style.border = this.border;
+ }
+ },
+
+ /**
+ * Method: setContentHTML
+ * Allows the user to set the HTML content of the popup.
+ *
+ * Parameters:
+ * contentHTML - {String} HTML for the div.
+ */
+ setContentHTML:function(contentHTML) {
+
+ if (contentHTML != null) {
+ this.contentHTML = contentHTML;
+ }
+
+ if ((this.contentDiv != null) &&
+ (this.contentHTML != null) &&
+ (this.contentHTML != this.contentDiv.innerHTML)) {
+
+ this.contentDiv.innerHTML = this.contentHTML;
+
+ if (this.autoSize) {
+
+ //if popup has images, listen for when they finish
+ // loading and resize accordingly
+ this.registerImageListeners();
+
+ //auto size the popup to its current contents
+ this.updateSize();
+ }
+ }
+
+ },
+
+ /**
+ * Method: registerImageListeners
+ * Called when an image contained by the popup loaded. this function
+ * updates the popup size, then unregisters the image load listener.
+ */
+ registerImageListeners: function() {
+
+ // As the images load, this function will call updateSize() to
+ // resize the popup to fit the content div (which presumably is now
+ // bigger than when the image was not loaded).
+ //
+ // If the 'panMapIfOutOfView' property is set, we will pan the newly
+ // resized popup back into view.
+ //
+ // Note that this function, when called, will have 'popup' and
+ // 'img' properties in the context.
+ //
+ var onImgLoad = function() {
+
+ this.popup.updateSize();
+
+ if ( this.popup.visible() && this.popup.panMapIfOutOfView ) {
+ this.popup.panIntoView();
+ }
+
+ OpenLayers.Event.stopObserving(
+ this.img, "load", this.img._onImageLoad
+ );
+
+ };
+
+ //cycle through the images and if their size is 0x0, that means that
+ // they haven't been loaded yet, so we attach the listener, which
+ // will fire when the images finish loading and will resize the
+ // popup accordingly to its new size.
+ var images = this.contentDiv.getElementsByTagName("img");
+ for (var i = 0, len = images.length; i < len; i++) {
+ var img = images[i];
+ if (img.width == 0 || img.height == 0) {
+
+ var context = {
+ 'popup': this,
+ 'img': img
+ };
+
+ //expando this function to the image itself before registering
+ // it. This way we can easily and properly unregister it.
+ img._onImgLoad = OpenLayers.Function.bind(onImgLoad, context);
+
+ OpenLayers.Event.observe(img, 'load', img._onImgLoad);
+ }
+ }
+ },
+
+ /**
+ * APIMethod: getSafeContentSize
+ *
+ * Parameters:
+ * size - {<OpenLayers.Size>} Desired size to make the popup.
+ *
+ * Returns:
+ * {<OpenLayers.Size>} A size to make the popup which is neither smaller
+ * than the specified minimum size, nor bigger than the maximum
+ * size (which is calculated relative to the size of the viewport).
+ */
+ getSafeContentSize: function(size) {
+
+ var safeContentSize = size.clone();
+
+ // if our contentDiv has a css 'padding' set on it by a stylesheet, we
+ // must add that to the desired "size".
+ var contentDivPadding = this.getContentDivPadding();
+ var wPadding = contentDivPadding.left + contentDivPadding.right;
+ var hPadding = contentDivPadding.top + contentDivPadding.bottom;
+
+ // take into account the popup's 'padding' property
+ this.fixPadding();
+ wPadding += this.padding.left + this.padding.right;
+ hPadding += this.padding.top + this.padding.bottom;
+
+ if (this.closeDiv) {
+ var closeDivWidth = parseInt(this.closeDiv.style.width);
+ wPadding += closeDivWidth + contentDivPadding.right;
+ }
+
+ // prevent the popup from being smaller than a specified minimal size
+ if (this.minSize) {
+ safeContentSize.w = Math.max(safeContentSize.w,
+ (this.minSize.w - wPadding));
+ safeContentSize.h = Math.max(safeContentSize.h,
+ (this.minSize.h - hPadding));
+ }
+
+ // prevent the popup from being bigger than a specified maximum size
+ if (this.maxSize) {
+ safeContentSize.w = Math.min(safeContentSize.w,
+ (this.maxSize.w - wPadding));
+ safeContentSize.h = Math.min(safeContentSize.h,
+ (this.maxSize.h - hPadding));
+ }
+
+ //make sure the desired size to set doesn't result in a popup that
+ // is bigger than the map's viewport.
+ //
+ if (this.map && this.map.size) {
+
+ // Note that there *was* a reference to a
+ // 'OpenLayers.Popup.SCROLL_BAR_WIDTH' constant here, with special
+ // tolerance for it and everything... but it was never defined in
+ // the first place, so I don't know what to think.
+
+ var maxY = this.map.size.h -
+ this.map.paddingForPopups.top -
+ this.map.paddingForPopups.bottom -
+ hPadding;
+
+ var maxX = this.map.size.w -
+ this.map.paddingForPopups.left -
+ this.map.paddingForPopups.right -
+ wPadding;
+
+ safeContentSize.w = Math.min(safeContentSize.w, maxX);
+ safeContentSize.h = Math.min(safeContentSize.h, maxY);
+ }
+
+ return safeContentSize;
+ },
+
+ /**
+ * Method: getContentDivPadding
+ * Glorious, oh glorious hack in order to determine the css 'padding' of
+ * the contentDiv. IE/Opera return null here unless we actually add the
+ * popup's main 'div' element (which contains contentDiv) to the DOM.
+ * So we make it invisible and then add it to the document temporarily.
+ *
+ * Once we've taken the padding readings we need, we then remove it
+ * from the DOM (it will actually get added to the DOM in
+ * Map.js's addPopup)
+ *
+ * Returns:
+ * {<OpenLayers.Bounds>}
+ */
+ getContentDivPadding: function() {
+
+ //use cached value if we have it
+ var contentDivPadding = this._contentDivPadding;
+ if (!contentDivPadding) {
+ //make the div invisible and add it to the page
+ this.div.style.display = "none";
+ document.body.appendChild(this.div);
+
+ //read the padding settings from css, put them in an OL.Bounds
+ contentDivPadding = new OpenLayers.Bounds(
+ OpenLayers.Element.getStyle(this.contentDiv, "padding-left"),
+ OpenLayers.Element.getStyle(this.contentDiv, "padding-bottom"),
+ OpenLayers.Element.getStyle(this.contentDiv, "padding-right"),
+ OpenLayers.Element.getStyle(this.contentDiv, "padding-top")
+ );
+
+ //cache the value
+ this._contentDivPadding = contentDivPadding;
+
+ //remove the div from the page and make it visible again
+ document.body.removeChild(this.div);
+ this.div.style.display = "";
+ }
+ return contentDivPadding;
+ },
+
+ /**
+ * Method: addCloseBox
+ *
+ * Parameters:
+ * callback - {Function} The callback to be called when the close button
+ * is clicked.
+ */
+ addCloseBox: function(callback) {
+
+ this.closeDiv = OpenLayers.Util.createDiv(
+ this.id + "_close", null, new OpenLayers.Size(17, 17)
+ );
+ this.closeDiv.className = "olPopupCloseBox";
+
+ // use the content div's css padding to determine if we should
+ // padd the close div
+ var contentDivPadding = this.getContentDivPadding();
+
+ this.closeDiv.style.right = contentDivPadding.right + "px";
+ this.closeDiv.style.top = contentDivPadding.top + "px";
+ this.groupDiv.appendChild(this.closeDiv);
+
+ var closePopup = callback || function(e) {
+ this.hide();
+ OpenLayers.Event.stop(e);
+ };
+ OpenLayers.Event.observe(this.closeDiv, "click",
+ OpenLayers.Function.bindAsEventListener(closePopup, this));
+ },
+
+ /**
+ * Method: panIntoView
+ * Pans the map such that the popup is totaly viewable (if necessary)
+ */
+ panIntoView: function() {
+
+ var mapSize = this.map.getSize();
+
+ //start with the top left corner of the popup, in px,
+ // relative to the viewport
+ var origTL = this.map.getViewPortPxFromLayerPx( new OpenLayers.Pixel(
+ parseInt(this.div.style.left),
+ parseInt(this.div.style.top)
+ ));
+ var newTL = origTL.clone();
+
+ //new left (compare to margins, using this.size to calculate right)
+ if (origTL.x < this.map.paddingForPopups.left) {
+ newTL.x = this.map.paddingForPopups.left;
+ } else
+ if ( (origTL.x + this.size.w) > (mapSize.w - this.map.paddingForPopups.right)) {
+ newTL.x = mapSize.w - this.map.paddingForPopups.right - this.size.w;
+ }
+
+ //new top (compare to margins, using this.size to calculate bottom)
+ if (origTL.y < this.map.paddingForPopups.top) {
+ newTL.y = this.map.paddingForPopups.top;
+ } else
+ if ( (origTL.y + this.size.h) > (mapSize.h - this.map.paddingForPopups.bottom)) {
+ newTL.y = mapSize.h - this.map.paddingForPopups.bottom - this.size.h;
+ }
+
+ var dx = origTL.x - newTL.x;
+ var dy = origTL.y - newTL.y;
+
+ this.map.pan(dx, dy);
+ },
+
+ /**
+ * Method: registerEvents
+ * Registers events on the popup.
+ *
+ * Do this in a separate function so that subclasses can
+ * choose to override it if they wish to deal differently
+ * with mouse events
+ *
+ * Note in the following handler functions that some special
+ * care is needed to deal correctly with mousing and popups.
+ *
+ * Because the user might select the zoom-rectangle option and
+ * then drag it over a popup, we need a safe way to allow the
+ * mousemove and mouseup events to pass through the popup when
+ * they are initiated from outside.
+ *
+ * Otherwise, we want to essentially kill the event propagation
+ * for all other events, though we have to do so carefully,
+ * without disabling basic html functionality, like clicking on
+ * hyperlinks or drag-selecting text.
+ */
+ registerEvents:function() {
+ this.events = new OpenLayers.Events(this, this.div, null, true);
+
+ this.events.on({
+ "mousedown": this.onmousedown,
+ "mousemove": this.onmousemove,
+ "mouseup": this.onmouseup,
+ "click": this.onclick,
+ "mouseout": this.onmouseout,
+ "dblclick": this.ondblclick,
+ scope: this
+ });
+
+ },
+
+ /**
+ * Method: onmousedown
+ * When mouse goes down within the popup, make a note of
+ * it locally, and then do not propagate the mousedown
+ * (but do so safely so that user can select text inside)
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ onmousedown: function (evt) {
+ this.mousedown = true;
+ OpenLayers.Event.stop(evt, true);
+ },
+
+ /**
+ * Method: onmousemove
+ * If the drag was started within the popup, then
+ * do not propagate the mousemove (but do so safely
+ * so that user can select text inside)
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ onmousemove: function (evt) {
+ if (this.mousedown) {
+ OpenLayers.Event.stop(evt, true);
+ }
+ },
+
+ /**
+ * Method: onmouseup
+ * When mouse comes up within the popup, after going down
+ * in it, reset the flag, and then (once again) do not
+ * propagate the event, but do so safely so that user can
+ * select text inside
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ onmouseup: function (evt) {
+ if (this.mousedown) {
+ this.mousedown = false;
+ OpenLayers.Event.stop(evt, true);
+ }
+ },
+
+ /**
+ * Method: onclick
+ * Ignore clicks, but allowing default browser handling
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ onclick: function (evt) {
+ OpenLayers.Event.stop(evt, true);
+ },
+
+ /**
+ * Method: onmouseout
+ * When mouse goes out of the popup set the flag to false so that
+ * if they let go and then drag back in, we won't be confused.
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ onmouseout: function (evt) {
+ this.mousedown = false;
+ },
+
+ /**
+ * Method: ondblclick
+ * Ignore double-clicks, but allowing default browser handling
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ ondblclick: function (evt) {
+ OpenLayers.Event.stop(evt, true);
+ },
+
+ CLASS_NAME: "OpenLayers.Popup"
+});
+
+OpenLayers.Popup.WIDTH = 200;
+OpenLayers.Popup.HEIGHT = 200;
+OpenLayers.Popup.COLOR = "white";
+OpenLayers.Popup.OPACITY = 1;
+OpenLayers.Popup.BORDER = "0px";
+/* ======================================================================
+ OpenLayers/Protocol.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. */
+
+/**
+ * Class: OpenLayers.Protocol
+ * Abstract vector layer protocol class. Not to be instantiated directly. Use
+ * one of the protocol subclasses instead.
+ */
+OpenLayers.Protocol = OpenLayers.Class({
+
+ /**
+ * Property: format
+ * {<OpenLayers.Format>}
+ */
+ format: null,
+
+ /**
+ * Property: options
+ * Any options sent to the constructor.
+ */
+ options: null,
+
+ /**
+ * Property: autoDestroy
+ * {Boolean} The creator of the protocol can set autoDestroy to false
+ * to fully control when the protocol is destroyed. Defaults to
+ * true.
+ */
+ autoDestroy: true,
+
+ /**
+ * Constructor: OpenLayers.Protocol
+ * Abstract class for vector protocols. Create instances of a subclass.
+ *
+ * Parameters:
+ * options - {Object} Optional object whose properties will be set on the
+ * instance.
+ */
+ initialize: function(options) {
+ options = options || {};
+ OpenLayers.Util.extend(this, options);
+ this.options = options;
+ },
+
+ /**
+ * APIMethod: destroy
+ * Clean up the protocol.
+ */
+ destroy: function() {
+ this.options = null;
+ this.format = null;
+ },
+
+ /**
+ * Method: read
+ * Construct a request for reading new features.
+ *
+ * Parameters:
+ * options - {Object} Optional object for configuring the request.
+ *
+ * Returns:
+ * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
+ * object, the same object will be passed to the callback function passed
+ * if one exists in the options object.
+ */
+ read: function() {
+ },
+
+
+ /**
+ * Method: create
+ * Construct a request for writing newly created features.
+ *
+ * Parameters:
+ * features - {Array({<OpenLayers.Feature.Vector>})} or
+ * {<OpenLayers.Feature.Vector>}
+ * options - {Object} Optional object for configuring the request.
+ *
+ * Returns:
+ * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
+ * object, the same object will be passed to the callback function passed
+ * if one exists in the options object.
+ */
+ create: function() {
+ },
+
+ /**
+ * Method: update
+ * Construct a request updating modified features.
+ *
+ * Parameters:
+ * features - {Array({<OpenLayers.Feature.Vector>})} or
+ * {<OpenLayers.Feature.Vector>}
+ * options - {Object} Optional object for configuring the request.
+ *
+ * Returns:
+ * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
+ * object, the same object will be passed to the callback function passed
+ * if one exists in the options object.
+ */
+ update: function() {
+ },
+
+ /**
+ * Method: delete
+ * Construct a request deleting a removed feature.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>}
+ * options - {Object} Optional object for configuring the request.
+ *
+ * Returns:
+ * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
+ * object, the same object will be passed to the callback function passed
+ * if one exists in the options object.
+ */
+ "delete": function() {
+ },
+
+ /**
+ * Method: commit
+ * Go over the features and for each take action
+ * based on the feature state. Possible actions are create,
+ * update and delete.
+ *
+ * Parameters:
+ * features - {Array({<OpenLayers.Feature.Vector>})}
+ * options - {Object} Object whose possible keys are "create", "update",
+ * "delete", "callback" and "scope", the values referenced by the
+ * first three are objects as passed to the "create", "update", and
+ * "delete" methods, the value referenced by the "callback" key is
+ * a function which is called when the commit operation is complete
+ * using the scope referenced by the "scope" key.
+ *
+ * Returns:
+ * {Array({<OpenLayers.Protocol.Response>})} An array of
+ * <OpenLayers.Protocol.Response> objects.
+ */
+ commit: function() {
+ },
+
+ CLASS_NAME: "OpenLayers.Protocol"
+});
+
+/**
+ * Class: OpenLayers.Protocol.Response
+ * Protocols return Response objects to their users.
+ */
+OpenLayers.Protocol.Response = OpenLayers.Class({
+ /**
+ * Property: code
+ * {Number} - OpenLayers.Protocol.Response.SUCCESS or
+ * OpenLayers.Protocol.Response.FAILURE
+ */
+ code: null,
+
+ /**
+ * Property: requestType
+ * {String} The type of request this response corresponds to. Either
+ * "create", "read", "update" or "delete".
+ */
+ requestType: null,
+
+ /**
+ * Property: last
+ * {Boolean} - true if this is the last response expected in a commit,
+ * false otherwise, defaults to true.
+ */
+ last: true,
+
+ /**
+ * Property: features
+ * {Array({<OpenLayers.Feature.Vector>})} or {<OpenLayers.Feature.Vector>}
+ * The features returned in the response by the server.
+ */
+ features: null,
+
+ /**
+ * Property: reqFeatures
+ * {Array({<OpenLayers.Feature.Vector>})} or {<OpenLayers.Feature.Vector>}
+ * The features provided by the user and placed in the request by the
+ * protocol.
+ */
+ reqFeatures: null,
+
+ /**
+ * Property: priv
+ */
+ priv: null,
+
+ /**
+ * Constructor: OpenLayers.Protocol.Response
+ *
+ * Parameters:
+ * options - {Object} Optional object whose properties will be set on the
+ * instance.
+ */
+ initialize: function(options) {
+ OpenLayers.Util.extend(this, options);
+ },
+
+ /**
+ * Method: success
+ *
+ * Returns:
+ * {Boolean} - true on success, false otherwise
+ */
+ success: function() {
+ return this.code > 0;
+ },
+
+ CLASS_NAME: "OpenLayers.Protocol.Response"
+});
+
+OpenLayers.Protocol.Response.SUCCESS = 1;
+OpenLayers.Protocol.Response.FAILURE = 0;
+/* ======================================================================
+ OpenLayers/Renderer.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. */
+
+/**
+ * Class: OpenLayers.Renderer
+ * This is the base class for all renderers.
+ *
+ * This is based on a merger code written by Paul Spencer and Bertil Chapuis.
+ * It is largely composed of virtual functions that are to be implemented
+ * in technology-specific subclasses, but there is some generic code too.
+ *
+ * The functions that *are* implemented here merely deal with the maintenance
+ * of the size and extent variables, as well as the cached 'resolution'
+ * value.
+ *
+ * A note to the user that all subclasses should use getResolution() instead
+ * of directly accessing this.resolution in order to correctly use the
+ * cacheing system.
+ *
+ */
+OpenLayers.Renderer = OpenLayers.Class({
+
+ /**
+ * Property: container
+ * {DOMElement}
+ */
+ container: null,
+
+ /**
+ * Property: extent
+ * {<OpenLayers.Bounds>}
+ */
+ extent: null,
+
+ /**
+ * Property: locked
+ * {Boolean} If the renderer is currently in a state where many things
+ * are changing, the 'locked' property is set to true. This means
+ * that renderers can expect at least one more drawFeature event to be
+ * called with the 'locked' property set to 'true': In some renderers,
+ * this might make sense to use as a 'only update local information'
+ * flag.
+ */
+ locked: false,
+
+ /**
+ * Property: size
+ * {<OpenLayers.Size>}
+ */
+ size: null,
+
+ /**
+ * Property: resolution
+ * {Float} cache of current map resolution
+ */
+ resolution: null,
+
+ /**
+ * Property: map
+ * {<OpenLayers.Map>} Reference to the map -- this is set in Vector's setMap()
+ */
+ map: null,
+
+ /**
+ * Constructor: OpenLayers.Renderer
+ *
+ * Parameters:
+ * containerID - {<String>}
+ * options - {Object} options for this renderer. See sublcasses for
+ * supported options.
+ */
+ initialize: function(containerID, options) {
+ this.container = OpenLayers.Util.getElement(containerID);
+ },
+
+ /**
+ * APIMethod: destroy
+ */
+ destroy: function() {
+ this.container = null;
+ this.extent = null;
+ this.size = null;
+ this.resolution = null;
+ this.map = null;
+ },
+
+ /**
+ * APIMethod: supported
+ * This should be overridden by specific subclasses
+ *
+ * Returns:
+ * {Boolean} Whether or not the browser supports the renderer class
+ */
+ supported: function() {
+ return false;
+ },
+
+ /**
+ * Method: setExtent
+ * Set the visible part of the layer.
+ *
+ * Resolution has probably changed, so we nullify the resolution
+ * cache (this.resolution) -- this way it will be re-computed when
+ * next it is needed.
+ * We nullify the resolution cache (this.resolution) if resolutionChanged
+ * is set to true - this way it will be re-computed on the next
+ * getResolution() request.
+ *
+ * Parameters:
+ * extent - {<OpenLayers.Bounds>}
+ * resolutionChanged - {Boolean}
+ */
+ setExtent: function(extent, resolutionChanged) {
+ this.extent = extent.clone();
+ if (resolutionChanged) {
+ this.resolution = null;
+ }
+ },
+
+ /**
+ * Method: setSize
+ * Sets the size of the drawing surface.
+ *
+ * Resolution has probably changed, so we nullify the resolution
+ * cache (this.resolution) -- this way it will be re-computed when
+ * next it is needed.
+ *
+ * Parameters:
+ * size - {<OpenLayers.Size>}
+ */
+ setSize: function(size) {
+ this.size = size.clone();
+ this.resolution = null;
+ },
+
+ /**
+ * Method: getResolution
+ * Uses cached copy of resolution if available to minimize computing
+ *
+ * Returns:
+ * The current map's resolution
+ */
+ getResolution: function() {
+ this.resolution = this.resolution || this.map.getResolution();
+ return this.resolution;
+ },
+
+ /**
+ * Method: drawFeature
+ * Draw the feature. The optional style argument can be used
+ * to override the feature's own style. This method should only
+ * be called from layer.drawFeature().
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>}
+ * style - {<Object>}
+ *
+ * Returns:
+ * {Boolean} true if the feature has been drawn completely, false if not,
+ * undefined if the feature had no geometry
+ */
+ drawFeature: function(feature, style) {
+ if(style == null) {
+ style = feature.style;
+ }
+ if (feature.geometry) {
+ if (!feature.geometry.getBounds().intersectsBounds(this.extent)) {
+ style = {display: "none"};
+ }
+ return this.drawGeometry(feature.geometry, style, feature.id);
+ }
+ },
+
+
+ /**
+ * Method: drawGeometry
+ *
+ * Draw a geometry. This should only be called from the renderer itself.
+ * Use layer.drawFeature() from outside the renderer.
+ * virtual function
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry>}
+ * style - {Object}
+ * featureId - {<String>}
+ */
+ drawGeometry: function(geometry, style, featureId) {},
+
+ /**
+ * Method: clear
+ * Clear all vectors from the renderer.
+ * virtual function.
+ */
+ clear: function() {},
+
+ /**
+ * Method: getFeatureIdFromEvent
+ * Returns a feature id from an event on the renderer.
+ * How this happens is specific to the renderer. This should be
+ * called from layer.getFeatureFromEvent().
+ * Virtual function.
+ *
+ * Parameters:
+ * evt - {<OpenLayers.Event>}
+ *
+ * Returns:
+ * {String} A feature id or null.
+ */
+ getFeatureIdFromEvent: function(evt) {},
+
+ /**
+ * Method: eraseFeatures
+ * This is called by the layer to erase features
+ *
+ * Parameters:
+ * features - {Array(<OpenLayers.Feature.Vector>)}
+ */
+ eraseFeatures: function(features) {
+ if(!(features instanceof Array)) {
+ features = [features];
+ }
+ for(var i=0, len=features.length; i<len; ++i) {
+ this.eraseGeometry(features[i].geometry);
+ }
+ },
+
+ /**
+ * Method: eraseGeometry
+ * Remove a geometry from the renderer (by id).
+ * virtual function.
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry>}
+ */
+ eraseGeometry: function(geometry) {},
+
+ CLASS_NAME: "OpenLayers.Renderer"
+});
+/* ======================================================================
+ OpenLayers/Request.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. */
+
+/**
+ * Namespace: OpenLayers.Request
+ * The OpenLayers.Request namespace contains convenience methods for working
+ * with XMLHttpRequests. These methods work with a cross-browser
+ * W3C compliant <OpenLayers.Request.XMLHttpRequest> class.
+ */
+OpenLayers.Request = {
+
+ /**
+ * Constant: DEFAULT_CONFIG
+ * {Object} Default configuration for all requests.
+ */
+ DEFAULT_CONFIG: {
+ method: "GET",
+ url: window.location.href,
+ async: true,
+ user: undefined,
+ password: undefined,
+ params: null,
+ proxy: OpenLayers.ProxyHost,
+ headers: {},
+ data: null,
+ callback: function() {},
+ success: null,
+ failure: null,
+ scope: null
+ },
+
+ /**
+ * APIMethod: issue
+ * Create a new XMLHttpRequest object, open it, set any headers, bind
+ * a callback to done state, and send any data.
+ *
+ * Parameters:
+ * config - {Object} Object containing properties for configuring the
+ * request. Allowed configuration properties are described below.
+ * This object is modified and should not be reused.
+ *
+ * Allowed config properties:
+ * method - {String} One of GET, POST, PUT, DELETE, HEAD, or
+ * OPTIONS. Default is GET.
+ * url - {String} URL for the request.
+ * async - {Boolean} Open an asynchronous request. Default is true.
+ * user - {String} User for relevant authentication scheme. Set
+ * to null to clear current user.
+ * password - {String} Password for relevant authentication scheme.
+ * Set to null to clear current password.
+ * proxy - {String} Optional proxy. Defaults to
+ * <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
+ * 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.
+ * callback - {Function} Function to call when request is done.
+ * To determine if the request failed, check request.status (200
+ * indicates success).
+ * success - {Function} Optional function to call if request status is in
+ * the 200s. This will be called in addition to callback above and
+ * would typically only be used as an alternative.
+ * failure - {Function} Optional function to call if request status is not
+ * in the 200s. This will be called in addition to callback above and
+ * would typically only be used as an alternative.
+ * scope - {Object} If callback is a public method on some object,
+ * set the scope to that object.
+ *
+ * Returns:
+ * {XMLHttpRequest} Request object. To abort the request before a response
+ * is received, call abort() on the request object.
+ */
+ issue: function(config) {
+ // apply default config - proxy host may have changed
+ var defaultConfig = OpenLayers.Util.extend(
+ this.DEFAULT_CONFIG,
+ {proxy: OpenLayers.ProxyHost}
+ );
+ config = OpenLayers.Util.applyDefaults(config, defaultConfig);
+
+ // create request, open, and set headers
+ var request = new OpenLayers.Request.XMLHttpRequest();
+ var url = config.url;
+ if(config.params) {
+ url += "?" + OpenLayers.Util.getParameterString(config.params);
+ }
+ if(config.proxy && (url.indexOf("http") == 0)) {
+ url = config.proxy + encodeURIComponent(url);
+ }
+ request.open(
+ config.method, url, config.async, config.user, config.password
+ );
+ for(var header in config.headers) {
+ request.setRequestHeader(header, config.headers[header]);
+ }
+
+ // bind callbacks to readyState 4 (done)
+ var complete = (config.scope) ?
+ OpenLayers.Function.bind(config.callback, config.scope) :
+ config.callback;
+
+ // optional success callback
+ var success;
+ if(config.success) {
+ success = (config.scope) ?
+ OpenLayers.Function.bind(config.success, config.scope) :
+ config.success;
+ }
+
+ // optional failure callback
+ var failure;
+ if(config.failure) {
+ failure = (config.scope) ?
+ OpenLayers.Function.bind(config.failure, config.scope) :
+ config.failure;
+ }
+
+ request.onreadystatechange = function() {
+ if(request.readyState == OpenLayers.Request.XMLHttpRequest.DONE) {
+ complete(request);
+ if(success && (!request.status ||
+ (request.status >= 200 && request.status < 300))) {
+ success(request);
+ }
+ if(failure && (request.status &&
+ (request.status < 200 || request.status >= 300))) {
+ failure(request);
+ }
+ }
+ }
+
+ // send request (optionally with data) and return
+ request.send(config.data);
+ return request;
+ },
+
+ /**
+ * APIMethod: GET
+ * Send an HTTP GET request. Additional configuration properties are
+ * documented in the <issue> method, with the method property set
+ * to GET.
+ *
+ * Parameters:
+ * config - {Object} Object with properties for configuring the request.
+ * See the <issue> method for documentation of allowed properties.
+ * This object is modified and should not be reused.
+ *
+ * Returns:
+ * {XMLHttpRequest} Request object.
+ */
+ GET: function(config) {
+ config = OpenLayers.Util.extend(config, {method: "GET"});
+ return OpenLayers.Request.issue(config);
+ },
+
+ /**
+ * APIMethod: POST
+ * Send a POST request. Additional configuration properties are
+ * documented in the <issue> method, with the method property set
+ * to POST and "Content-Type" header set to "application/xml".
+ *
+ * Parameters:
+ * config - {Object} Object with properties for configuring the request.
+ * See the <issue> method for documentation of allowed properties. The
+ * default "Content-Type" header will be set to "application-xml" if
+ * none is provided. This object is modified and should not be reused.
+ *
+ * Returns:
+ * {XMLHttpRequest} Request object.
+ */
+ POST: function(config) {
+ config = OpenLayers.Util.extend(config, {method: "POST"});
+ // set content type to application/xml if it isn't already set
+ config.headers = config.headers ? config.headers : {};
+ if(!("CONTENT-TYPE" in OpenLayers.Util.upperCaseObject(config.headers))) {
+ config.headers["Content-Type"] = "application/xml";
+ }
+ return OpenLayers.Request.issue(config);
+ },
+
+ /**
+ * APIMethod: PUT
+ * Send an HTTP PUT request. Additional configuration properties are
+ * documented in the <issue> method, with the method property set
+ * to PUT and "Content-Type" header set to "application/xml".
+ *
+ * Parameters:
+ * config - {Object} Object with properties for configuring the request.
+ * See the <issue> method for documentation of allowed properties. The
+ * default "Content-Type" header will be set to "application-xml" if
+ * none is provided. This object is modified and should not be reused.
+ *
+ * Returns:
+ * {XMLHttpRequest} Request object.
+ */
+ PUT: function(config) {
+ config = OpenLayers.Util.extend(config, {method: "PUT"});
+ // set content type to application/xml if it isn't already set
+ config.headers = config.headers ? config.headers : {};
+ if(!("CONTENT-TYPE" in OpenLayers.Util.upperCaseObject(config.headers))) {
+ config.headers["Content-Type"] = "application/xml";
+ }
+ return OpenLayers.Request.issue(config);
+ },
+
+ /**
+ * APIMethod: DELETE
+ * Send an HTTP DELETE request. Additional configuration properties are
+ * documented in the <issue> method, with the method property set
+ * to DELETE.
+ *
+ * Parameters:
+ * config - {Object} Object with properties for configuring the request.
+ * See the <issue> method for documentation of allowed properties.
+ * This object is modified and should not be reused.
+ *
+ * Returns:
+ * {XMLHttpRequest} Request object.
+ */
+ DELETE: function(config) {
+ config = OpenLayers.Util.extend(config, {method: "DELETE"});
+ return OpenLayers.Request.issue(config);
+ },
+
+ /**
+ * APIMethod: HEAD
+ * Send an HTTP HEAD request. Additional configuration properties are
+ * documented in the <issue> method, with the method property set
+ * to HEAD.
+ *
+ * Parameters:
+ * config - {Object} Object with properties for configuring the request.
+ * See the <issue> method for documentation of allowed properties.
+ * This object is modified and should not be reused.
+ *
+ * Returns:
+ * {XMLHttpRequest} Request object.
+ */
+ HEAD: function(config) {
+ config = OpenLayers.Util.extend(config, {method: "HEAD"});
+ return OpenLayers.Request.issue(config);
+ },
+
+ /**
+ * APIMethod: OPTIONS
+ * Send an HTTP OPTIONS request. Additional configuration properties are
+ * documented in the <issue> method, with the method property set
+ * to OPTIONS.
+ *
+ * Parameters:
+ * config - {Object} Object with properties for configuring the request.
+ * See the <issue> method for documentation of allowed properties.
+ * This object is modified and should not be reused.
+ *
+ * Returns:
+ * {XMLHttpRequest} Request object.
+ */
+ OPTIONS: function(config) {
+ config = OpenLayers.Util.extend(config, {method: "OPTIONS"});
+ return OpenLayers.Request.issue(config);
+ }
+
+};
+/* ======================================================================
+ OpenLayers/Strategy.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. */
+
+/**
+ * Class: OpenLayers.Strategy
+ * Abstract vector layer strategy class. Not to be instantiated directly. Use
+ * one of the strategy subclasses instead.
+ */
+OpenLayers.Strategy = OpenLayers.Class({
+
+ /**
+ * Property: layer
+ * {<OpenLayers.Layer.Vector>}
+ */
+ layer: null,
+
+ /**
+ * Property: options
+ * {Object} Any options sent to the constructor.
+ */
+ options: null,
+
+ /**
+ * Property: active
+ * {Boolean} The control is active.
+ */
+ active: null,
+
+ /**
+ * Property: autoActivate
+ * {Boolean} The creator of the strategy can set autoActivate to false
+ * to fully control when the protocol is activated and deactivated.
+ * Defaults to true.
+ */
+ autoActivate: true,
+
+ /**
+ * Property: autoDestroy
+ * {Boolean} The creator of the strategy can set autoDestroy to false
+ * to fully control when the strategy is destroyed. Defaults to
+ * true.
+ */
+ autoDestroy: true,
+
+ /**
+ * Constructor: OpenLayers.Strategy
+ * Abstract class for vector strategies. Create instances of a subclass.
+ *
+ * Parameters:
+ * options - {Object} Optional object whose properties will be set on the
+ * instance.
+ */
+ initialize: function(options) {
+ OpenLayers.Util.extend(this, options);
+ this.options = options;
+ // set the active property here, so that user cannot override it
+ this.active = false;
+ },
+
+ /**
+ * APIMethod: destroy
+ * Clean up the strategy.
+ */
+ destroy: function() {
+ this.deactivate();
+ this.layer = null;
+ this.options = null;
+ },
+
+ /**
+ * Method: setLayer.
+ *
+ * Parameters:
+ * {<OpenLayers.Layer.Vector>}
+ */
+ setLayer: function(layer) {
+ this.layer = layer;
+ },
+
+ /**
+ * Method: activate
+ * Activate the strategy. Register any listeners, do appropriate setup.
+ *
+ * Returns:
+ * {Boolean} True if the strategy was successfully activated or false if
+ * the strategy was already active.
+ */
+ activate: function() {
+ if (!this.active) {
+ this.active = true;
+ return true;
+ }
+ return false;
+ },
+
+ /**
+ * Method: deactivate
+ * Deactivate the strategy. Unregister any listeners, do appropriate
+ * tear-down.
+ *
+ * Returns:
+ * {Boolean} True if the strategy was successfully deactivated or false if
+ * the strategy was already inactive.
+ */
+ deactivate: function() {
+ if (this.active) {
+ this.active = false;
+ return true;
+ }
+ return false;
+ },
+
+ CLASS_NAME: "OpenLayers.Strategy"
+});
+/* ======================================================================
OpenLayers/Tween.js
====================================================================== */
@@ -5422,6 +7131,254 @@
CLASS_NAME: "OpenLayers.Easing.Quad"
};
/* ======================================================================
+ Rico/Color.js
+ ====================================================================== */
+
+/*
+ * This file has been edited substantially from the Rico-released version by
+ * the OpenLayers development team.
+ *
+ * This file is licensed under the Apache License, Version 2.0.
+ */
+OpenLayers.Rico.Color = OpenLayers.Class({
+
+ initialize: function(red, green, blue) {
+ this.rgb = { r: red, g : green, b : blue };
+ },
+
+ setRed: function(r) {
+ this.rgb.r = r;
+ },
+
+ setGreen: function(g) {
+ this.rgb.g = g;
+ },
+
+ setBlue: function(b) {
+ this.rgb.b = b;
+ },
+
+ setHue: function(h) {
+
+ // get an HSB model, and set the new hue...
+ var hsb = this.asHSB();
+ hsb.h = h;
+
+ // convert back to RGB...
+ this.rgb = OpenLayers.Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b);
+ },
+
+ setSaturation: function(s) {
+ // get an HSB model, and set the new hue...
+ var hsb = this.asHSB();
+ hsb.s = s;
+
+ // convert back to RGB and set values...
+ this.rgb = OpenLayers.Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b);
+ },
+
+ setBrightness: function(b) {
+ // get an HSB model, and set the new hue...
+ var hsb = this.asHSB();
+ hsb.b = b;
+
+ // convert back to RGB and set values...
+ this.rgb = OpenLayers.Rico.Color.HSBtoRGB( hsb.h, hsb.s, hsb.b );
+ },
+
+ darken: function(percent) {
+ var hsb = this.asHSB();
+ this.rgb = OpenLayers.Rico.Color.HSBtoRGB(hsb.h, hsb.s, Math.max(hsb.b - percent,0));
+ },
+
+ brighten: function(percent) {
+ var hsb = this.asHSB();
+ this.rgb = OpenLayers.Rico.Color.HSBtoRGB(hsb.h, hsb.s, Math.min(hsb.b + percent,1));
+ },
+
+ blend: function(other) {
+ this.rgb.r = Math.floor((this.rgb.r + other.rgb.r)/2);
+ this.rgb.g = Math.floor((this.rgb.g + other.rgb.g)/2);
+ this.rgb.b = Math.floor((this.rgb.b + other.rgb.b)/2);
+ },
+
+ isBright: function() {
+ var hsb = this.asHSB();
+ return this.asHSB().b > 0.5;
+ },
+
+ isDark: function() {
+ return ! this.isBright();
+ },
+
+ asRGB: function() {
+ return "rgb(" + this.rgb.r + "," + this.rgb.g + "," + this.rgb.b + ")";
+ },
+
+ asHex: function() {
+ return "#" + this.rgb.r.toColorPart() + this.rgb.g.toColorPart() + this.rgb.b.toColorPart();
+ },
+
+ asHSB: function() {
+ return OpenLayers.Rico.Color.RGBtoHSB(this.rgb.r, this.rgb.g, this.rgb.b);
+ },
+
+ toString: function() {
+ return this.asHex();
+ }
+
+});
+
+OpenLayers.Rico.Color.createFromHex = function(hexCode) {
+ if(hexCode.length==4) {
+ var shortHexCode = hexCode;
+ var hexCode = '#';
+ for(var i=1;i<4;i++) {
+ hexCode += (shortHexCode.charAt(i) +
+shortHexCode.charAt(i));
+ }
+ }
+ if ( hexCode.indexOf('#') == 0 ) {
+ hexCode = hexCode.substring(1);
+ }
+ var red = hexCode.substring(0,2);
+ var green = hexCode.substring(2,4);
+ var blue = hexCode.substring(4,6);
+ return new OpenLayers.Rico.Color( parseInt(red,16), parseInt(green,16), parseInt(blue,16) );
+};
+
+/**
+ * Factory method for creating a color from the background of
+ * an HTML element.
+ */
+OpenLayers.Rico.Color.createColorFromBackground = function(elem) {
+
+ var actualColor =
+ RicoUtil.getElementsComputedStyle(OpenLayers.Util.getElement(elem),
+ "backgroundColor",
+ "background-color");
+
+ if ( actualColor == "transparent" && elem.parentNode ) {
+ return OpenLayers.Rico.Color.createColorFromBackground(elem.parentNode);
+ }
+ if ( actualColor == null ) {
+ return new OpenLayers.Rico.Color(255,255,255);
+ }
+ if ( actualColor.indexOf("rgb(") == 0 ) {
+ var colors = actualColor.substring(4, actualColor.length - 1 );
+ var colorArray = colors.split(",");
+ return new OpenLayers.Rico.Color( parseInt( colorArray[0] ),
+ parseInt( colorArray[1] ),
+ parseInt( colorArray[2] ) );
+
+ }
+ else if ( actualColor.indexOf("#") == 0 ) {
+ return OpenLayers.Rico.Color.createFromHex(actualColor);
+ }
+ else {
+ return new OpenLayers.Rico.Color(255,255,255);
+ }
+};
+
+OpenLayers.Rico.Color.HSBtoRGB = function(hue, saturation, brightness) {
+
+ var red = 0;
+ var green = 0;
+ var blue = 0;
+
+ if (saturation == 0) {
+ red = parseInt(brightness * 255.0 + 0.5);
+ green = red;
+ blue = red;
+ }
+ else {
+ var h = (hue - Math.floor(hue)) * 6.0;
+ var f = h - Math.floor(h);
+ var p = brightness * (1.0 - saturation);
+ var q = brightness * (1.0 - saturation * f);
+ var t = brightness * (1.0 - (saturation * (1.0 - f)));
+
+ switch (parseInt(h)) {
+ case 0:
+ red = (brightness * 255.0 + 0.5);
+ green = (t * 255.0 + 0.5);
+ blue = (p * 255.0 + 0.5);
+ break;
+ case 1:
+ red = (q * 255.0 + 0.5);
+ green = (brightness * 255.0 + 0.5);
+ blue = (p * 255.0 + 0.5);
+ break;
+ case 2:
+ red = (p * 255.0 + 0.5);
+ green = (brightness * 255.0 + 0.5);
+ blue = (t * 255.0 + 0.5);
+ break;
+ case 3:
+ red = (p * 255.0 + 0.5);
+ green = (q * 255.0 + 0.5);
+ blue = (brightness * 255.0 + 0.5);
+ break;
+ case 4:
+ red = (t * 255.0 + 0.5);
+ green = (p * 255.0 + 0.5);
+ blue = (brightness * 255.0 + 0.5);
+ break;
+ case 5:
+ red = (brightness * 255.0 + 0.5);
+ green = (p * 255.0 + 0.5);
+ blue = (q * 255.0 + 0.5);
+ break;
+ }
+ }
+
+ return { r : parseInt(red), g : parseInt(green) , b : parseInt(blue) };
+};
+
+OpenLayers.Rico.Color.RGBtoHSB = function(r, g, b) {
+
+ var hue;
+ var saturation;
+ var brightness;
+
+ var cmax = (r > g) ? r : g;
+ if (b > cmax) {
+ cmax = b;
+ }
+ var cmin = (r < g) ? r : g;
+ if (b < cmin) {
+ cmin = b;
+ }
+ brightness = cmax / 255.0;
+ if (cmax != 0) {
+ saturation = (cmax - cmin)/cmax;
+ } else {
+ saturation = 0;
+ }
+ if (saturation == 0) {
+ hue = 0;
+ } else {
+ var redc = (cmax - r)/(cmax - cmin);
+ var greenc = (cmax - g)/(cmax - cmin);
+ var bluec = (cmax - b)/(cmax - cmin);
+
+ if (r == cmax) {
+ hue = bluec - greenc;
+ } else if (g == cmax) {
+ hue = 2.0 + redc - bluec;
+ } else {
+ hue = 4.0 + greenc - redc;
+ }
+ hue = hue / 6.0;
+ if (hue < 0) {
+ hue = hue + 1.0;
+ }
+ }
+
+ return { h : hue, s : saturation, b : brightness };
+};
+
+/* ======================================================================
OpenLayers/Control/ArgParser.js
====================================================================== */
@@ -5495,7 +7452,7 @@
OpenLayers.Control.prototype.setMap.apply(this, arguments);
//make sure we dont already have an arg parser attached
- for(var i=0; i< this.map.controls.length; i++) {
+ for(var i=0, len=this.map.controls.length; i<len; i++) {
var control = this.map.controls[i];
if ( (control != this) &&
(control.CLASS_NAME == "OpenLayers.Control.ArgParser") ) {
@@ -5568,7 +7525,7 @@
if (this.layers.length == this.map.layers.length) {
this.map.events.unregister('addlayer', this, this.configureLayers);
- for(var i=0; i < this.layers.length; i++) {
+ for(var i=0, len=this.layers.length; i<len; i++) {
var layer = this.map.layers[i];
var c = this.layers.charAt(i);
@@ -5585,6 +7542,1145 @@
CLASS_NAME: "OpenLayers.Control.ArgParser"
});
/* ======================================================================
+ OpenLayers/Control/Attribution.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
+ */
+
+/**
+ * Class: OpenLayers.Control.Attribution
+ * Add attribution from layers to the map display. Uses 'attribution' property
+ * of each layer.
+ */
+OpenLayers.Control.Attribution =
+ OpenLayers.Class(OpenLayers.Control, {
+
+ /**
+ * APIProperty: seperator
+ * {String} String used to seperate layers.
+ */
+ separator: ", ",
+
+ /**
+ * Constructor: OpenLayers.Control.Attribution
+ *
+ * Parameters:
+ * options - {Object} Options for control.
+ */
+ initialize: function(options) {
+ OpenLayers.Control.prototype.initialize.apply(this, arguments);
+ },
+
+ /**
+ * Method: destroy
+ * Destroy control.
+ */
+ destroy: function() {
+ this.map.events.un({
+ "removelayer": this.updateAttribution,
+ "addlayer": this.updateAttribution,
+ "changelayer": this.updateAttribution,
+ "changebaselayer": this.updateAttribution,
+ scope: this
+ });
+
+ OpenLayers.Control.prototype.destroy.apply(this, arguments);
+ },
+
+ /**
+ * Method: draw
+ * Initialize control.
+ *
+ * Returns:
+ * {DOMElement} A reference to the DIV DOMElement containing the control
+ */
+ draw: function() {
+ OpenLayers.Control.prototype.draw.apply(this, arguments);
+
+ this.map.events.on({
+ 'changebaselayer': this.updateAttribution,
+ 'changelayer': this.updateAttribution,
+ 'addlayer': this.updateAttribution,
+ 'removelayer': this.updateAttribution,
+ scope: this
+ });
+ this.updateAttribution();
+
+ return this.div;
+ },
+
+ /**
+ * Method: updateAttribution
+ * Update attribution string.
+ */
+ updateAttribution: function() {
+ var attributions = [];
+ if (this.map && this.map.layers) {
+ for(var i=0, len=this.map.layers.length; i<len; i++) {
+ var layer = this.map.layers[i];
+ if (layer.attribution && layer.getVisibility()) {
+ attributions.push( layer.attribution );
+ }
+ }
+ this.div.innerHTML = attributions.join(this.separator);
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Control.Attribution"
+});
+/* ======================================================================
+ OpenLayers/Control/Button.js
+ ====================================================================== */
+
+/* Copyright (c) 2006-2007 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/Control.js
+ */
+
+/**
+ * Class: OpenLayers.Control.Button
+ * A very simple button controlfor use with <OpenLayers.Control.Panel>.
+ * When clicked, the function trigger() is executed.
+ *
+ * Inherits from:
+ * - <OpenLayers.Control>
+ *
+ * Use:
+ * (code)
+ * var button = new OpenLayers.Control.Button({
+ * displayClass: "MyButton", trigger: myFunction
+ * });
+ * panel.addControls([button]);
+ * (end)
+ *
+ * Will create a button with CSS class MyButtonItemInactive, that
+ * will call the function MyFunction() when clicked.
+ */
+OpenLayers.Control.Button = OpenLayers.Class(OpenLayers.Control, {
+ /**
+ * Property: type
+ * {Integer} OpenLayers.Control.TYPE_BUTTON.
+ */
+ type: OpenLayers.Control.TYPE_BUTTON,
+
+ /**
+ * Method: trigger
+ * Called by a control panel when the button is clicked.
+ */
+ trigger: function() {},
+
+ CLASS_NAME: "OpenLayers.Control.Button"
+});
+/* ======================================================================
+ OpenLayers/Control/LayerSwitcher.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
+ */
+
+/**
+ * Class: OpenLayers.Control.LayerSwitcher
+ *
+ * Inherits from:
+ * - <OpenLayers.Control>
+ */
+OpenLayers.Control.LayerSwitcher =
+ OpenLayers.Class(OpenLayers.Control, {
+
+ /**
+ * Property: activeColor
+ * {String}
+ */
+ activeColor: "darkblue",
+
+ /**
+ * Property: layerStates
+ * {Array(Object)} Basically a copy of the "state" of the map's layers
+ * the last time the control was drawn. We have this in order to avoid
+ * unnecessarily redrawing the control.
+ */
+ layerStates: null,
+
+
+ // DOM Elements
+
+ /**
+ * Property: layersDiv
+ * {DOMElement}
+ */
+ layersDiv: null,
+
+ /**
+ * Property: baseLayersDiv
+ * {DOMElement}
+ */
+ baseLayersDiv: null,
+
+ /**
+ * Property: baseLayers
+ * {Array(<OpenLayers.Layer>)}
+ */
+ baseLayers: null,
+
+
+ /**
+ * Property: dataLbl
+ * {DOMElement}
+ */
+ dataLbl: null,
+
+ /**
+ * Property: dataLayersDiv
+ * {DOMElement}
+ */
+ dataLayersDiv: null,
+
+ /**
+ * Property: dataLayers
+ * {Array(<OpenLayers.Layer>)}
+ */
+ dataLayers: null,
+
+
+ /**
+ * Property: minimizeDiv
+ * {DOMElement}
+ */
+ minimizeDiv: null,
+
+ /**
+ * Property: maximizeDiv
+ * {DOMElement}
+ */
+ maximizeDiv: null,
+
+ /**
+ * APIProperty: ascending
+ * {Boolean}
+ */
+ ascending: true,
+
+ /**
+ * Constructor: OpenLayers.Control.LayerSwitcher
+ *
+ * Parameters:
+ * options - {Object}
+ */
+ initialize: function(options) {
+ OpenLayers.Control.prototype.initialize.apply(this, arguments);
+ this.layerStates = [];
+ },
+
+ /**
+ * APIMethod: destroy
+ */
+ destroy: function() {
+
+ OpenLayers.Event.stopObservingElement(this.div);
+
+ OpenLayers.Event.stopObservingElement(this.minimizeDiv);
+ OpenLayers.Event.stopObservingElement(this.maximizeDiv);
+
+ //clear out layers info and unregister their events
+ this.clearLayersArray("base");
+ this.clearLayersArray("data");
+
+ this.map.events.un({
+ "addlayer": this.redraw,
+ "changelayer": this.redraw,
+ "removelayer": this.redraw,
+ "changebaselayer": this.redraw,
+ scope: this
+ });
+
+ OpenLayers.Control.prototype.destroy.apply(this, arguments);
+ },
+
+ /**
+ * Method: setMap
+ *
+ * Properties:
+ * map - {<OpenLayers.Map>}
+ */
+ setMap: function(map) {
+ OpenLayers.Control.prototype.setMap.apply(this, arguments);
+
+ this.map.events.on({
+ "addlayer": this.redraw,
+ "changelayer": this.redraw,
+ "removelayer": this.redraw,
+ "changebaselayer": this.redraw,
+ scope: this
+ });
+ },
+
+ /**
+ * Method: draw
+ *
+ * Returns:
+ * {DOMElement} A reference to the DIV DOMElement containing the
+ * switcher tabs.
+ */
+ draw: function() {
+ OpenLayers.Control.prototype.draw.apply(this);
+
+ // create layout divs
+ this.loadContents();
+
+ // set mode to minimize
+ if(!this.outsideViewport) {
+ this.minimizeControl();
+ }
+
+ // populate div with current info
+ this.redraw();
+
+ return this.div;
+ },
+
+ /**
+ * Method: clearLayersArray
+ * User specifies either "base" or "data". we then clear all the
+ * corresponding listeners, the div, and reinitialize a new array.
+ *
+ * Parameters:
+ * layersType - {String}
+ */
+ clearLayersArray: function(layersType) {
+ var layers = this[layersType + "Layers"];
+ if (layers) {
+ for(var i=0, len=layers.length; i<len ; i++) {
+ var layer = layers[i];
+ OpenLayers.Event.stopObservingElement(layer.inputElem);
+ OpenLayers.Event.stopObservingElement(layer.labelSpan);
+ }
+ }
+ this[layersType + "LayersDiv"].innerHTML = "";
+ this[layersType + "Layers"] = [];
+ },
+
+
+ /**
+ * Method: checkRedraw
+ * Checks if the layer state has changed since the last redraw() call.
+ *
+ * Returns:
+ * {Boolean} The layer state changed since the last redraw() call.
+ */
+ checkRedraw: function() {
+ var redraw = false;
+ if ( !this.layerStates.length ||
+ (this.map.layers.length != this.layerStates.length) ) {
+ redraw = true;
+ } else {
+ for (var i=0, len=this.layerStates.length; i<len; i++) {
+ var layerState = this.layerStates[i];
+ var layer = this.map.layers[i];
+ if ( (layerState.name != layer.name) ||
+ (layerState.inRange != layer.inRange) ||
+ (layerState.id != layer.id) ||
+ (layerState.visibility != layer.visibility) ) {
+ redraw = true;
+ break;
+ }
+ }
+ }
+ return redraw;
+ },
+
+ /**
+ * Method: redraw
+ * Goes through and takes the current state of the Map and rebuilds the
+ * control to display that state. Groups base layers into a
+ * radio-button group and lists each data layer with a checkbox.
+ *
+ * Returns:
+ * {DOMElement} A reference to the DIV DOMElement containing the control
+ */
+ redraw: function() {
+ //if the state hasn't changed since last redraw, no need
+ // to do anything. Just return the existing div.
+ if (!this.checkRedraw()) {
+ return this.div;
+ }
+
+ //clear out previous layers
+ this.clearLayersArray("base");
+ this.clearLayersArray("data");
+
+ var containsOverlays = false;
+ var containsBaseLayers = false;
+
+ // Save state -- for checking layer if the map state changed.
+ // We save this before redrawing, because in the process of redrawing
+ // we will trigger more visibility changes, and we want to not redraw
+ // and enter an infinite loop.
+ var len = this.map.layers.length;
+ this.layerStates = new Array(len);
+ for (var i=0; i <len; i++) {
+ var layer = this.map.layers[i];
+ this.layerStates[i] = {
+ 'name': layer.name,
+ 'visibility': layer.visibility,
+ 'inRange': layer.inRange,
+ 'id': layer.id
+ };
+ }
+
+ var layers = this.map.layers.slice();
+ if (!this.ascending) { layers.reverse(); }
+ for(var i=0, len=layers.length; i<len; i++) {
+ var layer = layers[i];
+ var baseLayer = layer.isBaseLayer;
+
+ if (layer.displayInLayerSwitcher) {
+
+ if (baseLayer) {
+ containsBaseLayers = true;
+ } else {
+ containsOverlays = true;
+ }
+
+ // only check a baselayer if it is *the* baselayer, check data
+ // layers if they are visible
+ var checked = (baseLayer) ? (layer == this.map.baseLayer)
+ : layer.getVisibility();
+
+ // create input element
+ var inputElem = document.createElement("input");
+ inputElem.id = this.id + "_input_" + layer.name;
+ inputElem.name = (baseLayer) ? "baseLayers" : layer.name;
+ inputElem.type = (baseLayer) ? "radio" : "checkbox";
+ inputElem.value = layer.name;
+ inputElem.checked = checked;
+ inputElem.defaultChecked = checked;
+
+ if (!baseLayer && !layer.inRange) {
+ inputElem.disabled = true;
+ }
+ var context = {
+ 'inputElem': inputElem,
+ 'layer': layer,
+ 'layerSwitcher': this
+ };
+ OpenLayers.Event.observe(inputElem, "mouseup",
+ OpenLayers.Function.bindAsEventListener(this.onInputClick,
+ context)
+ );
+
+ // create span
+ var labelSpan = document.createElement("span");
+ if (!baseLayer && !layer.inRange) {
+ labelSpan.style.color = "gray";
+ }
+ labelSpan.innerHTML = layer.name;
+ labelSpan.style.verticalAlign = (baseLayer) ? "bottom"
+ : "baseline";
+ OpenLayers.Event.observe(labelSpan, "click",
+ OpenLayers.Function.bindAsEventListener(this.onInputClick,
+ context)
+ );
+ // create line break
+ var br = document.createElement("br");
+
+
+ var groupArray = (baseLayer) ? this.baseLayers
+ : this.dataLayers;
+ groupArray.push({
+ 'layer': layer,
+ 'inputElem': inputElem,
+ 'labelSpan': labelSpan
+ });
+
+
+ var groupDiv = (baseLayer) ? this.baseLayersDiv
+ : this.dataLayersDiv;
+ groupDiv.appendChild(inputElem);
+ groupDiv.appendChild(labelSpan);
+ groupDiv.appendChild(br);
+ }
+ }
+
+ // if no overlays, dont display the overlay label
+ this.dataLbl.style.display = (containsOverlays) ? "" : "none";
+
+ // if no baselayers, dont display the baselayer label
+ this.baseLbl.style.display = (containsBaseLayers) ? "" : "none";
+
+ return this.div;
+ },
+
+ /**
+ * Method:
+ * A label has been clicked, check or uncheck its corresponding input
+ *
+ * Parameters:
+ * e - {Event}
+ *
+ * Context:
+ * - {DOMElement} inputElem
+ * - {<OpenLayers.Control.LayerSwitcher>} layerSwitcher
+ * - {<OpenLayers.Layer>} layer
+ */
+
+ onInputClick: function(e) {
+
+ if (!this.inputElem.disabled) {
+ if (this.inputElem.type == "radio") {
+ this.inputElem.checked = true;
+ this.layer.map.setBaseLayer(this.layer);
+ } else {
+ this.inputElem.checked = !this.inputElem.checked;
+ this.layerSwitcher.updateMap();
+ }
+ }
+ OpenLayers.Event.stop(e);
+ },
+
+ /**
+ * Method: onLayerClick
+ * Need to update the map accordingly whenever user clicks in either of
+ * the layers.
+ *
+ * Parameters:
+ * e - {Event}
+ */
+ onLayerClick: function(e) {
+ this.updateMap();
+ },
+
+
+ /**
+ * Method: updateMap
+ * Cycles through the loaded data and base layer input arrays and makes
+ * the necessary calls to the Map object such that that the map's
+ * visual state corresponds to what the user has selected in
+ * the control.
+ */
+ updateMap: function() {
+
+ // set the newly selected base layer
+ for(var i=0, len=this.baseLayers.length; i<len; i++) {
+ var layerEntry = this.baseLayers[i];
+ if (layerEntry.inputElem.checked) {
+ this.map.setBaseLayer(layerEntry.layer, false);
+ }
+ }
+
+ // set the correct visibilities for the overlays
+ for(var i=0, len=this.dataLayers.length; i<len; i++) {
+ var layerEntry = this.dataLayers[i];
+ layerEntry.layer.setVisibility(layerEntry.inputElem.checked);
+ }
+
+ },
+
+ /**
+ * Method: maximizeControl
+ * Set up the labels and divs for the control
+ *
+ * Parameters:
+ * e - {Event}
+ */
+ maximizeControl: function(e) {
+
+ //HACK HACK HACK - find a way to auto-size this layerswitcher
+ this.div.style.width = "20em";
+ this.div.style.height = "";
+
+ this.showControls(false);
+
+ if (e != null) {
+ OpenLayers.Event.stop(e);
+ }
+ },
+
+ /**
+ * Method: minimizeControl
+ * Hide all the contents of the control, shrink the size,
+ * add the maximize icon
+ *
+ * Parameters:
+ * e - {Event}
+ */
+ minimizeControl: function(e) {
+
+ this.div.style.width = "0px";
+ this.div.style.height = "0px";
+
+ this.showControls(true);
+
+ if (e != null) {
+ OpenLayers.Event.stop(e);
+ }
+ },
+
+ /**
+ * Method: showControls
+ * Hide/Show all LayerSwitcher controls depending on whether we are
+ * minimized or not
+ *
+ * Parameters:
+ * minimize - {Boolean}
+ */
+ showControls: function(minimize) {
+
+ this.maximizeDiv.style.display = minimize ? "" : "none";
+ this.minimizeDiv.style.display = minimize ? "none" : "";
+
+ this.layersDiv.style.display = minimize ? "none" : "";
+ },
+
+ /**
+ * Method: loadContents
+ * Set up the labels and divs for the control
+ */
+ loadContents: function() {
+
+ //configure main div
+ this.div.style.position = "absolute";
+ this.div.style.top = "25px";
+ this.div.style.right = "0px";
+ this.div.style.left = "";
+ this.div.style.fontFamily = "sans-serif";
+ this.div.style.fontWeight = "bold";
+ this.div.style.marginTop = "3px";
+ this.div.style.marginLeft = "3px";
+ this.div.style.marginBottom = "3px";
+ this.div.style.fontSize = "smaller";
+ this.div.style.color = "white";
+ this.div.style.backgroundColor = "transparent";
+
+ OpenLayers.Event.observe(this.div, "mouseup",
+ OpenLayers.Function.bindAsEventListener(this.mouseUp, this));
+ OpenLayers.Event.observe(this.div, "click",
+ this.ignoreEvent);
+ OpenLayers.Event.observe(this.div, "mousedown",
+ OpenLayers.Function.bindAsEventListener(this.mouseDown, this));
+ OpenLayers.Event.observe(this.div, "dblclick", this.ignoreEvent);
+
+
+ // layers list div
+ this.layersDiv = document.createElement("div");
+ this.layersDiv.id = this.id + "_layersDiv";
+ this.layersDiv.style.paddingTop = "5px";
+ this.layersDiv.style.paddingLeft = "10px";
+ this.layersDiv.style.paddingBottom = "5px";
+ this.layersDiv.style.paddingRight = "75px";
+ this.layersDiv.style.backgroundColor = this.activeColor;
+
+ // had to set width/height to get transparency in IE to work.
+ // thanks -- http://jszen.blogspot.com/2005/04/ie6-opacity-filter-caveat.html
+ //
+ this.layersDiv.style.width = "100%";
+ this.layersDiv.style.height = "100%";
+
+
+ this.baseLbl = document.createElement("div");
+ this.baseLbl.innerHTML = OpenLayers.i18n("baseLayer");
+ this.baseLbl.style.marginTop = "3px";
+ this.baseLbl.style.marginLeft = "3px";
+ this.baseLbl.style.marginBottom = "3px";
+
+ this.baseLayersDiv = document.createElement("div");
+ this.baseLayersDiv.style.paddingLeft = "10px";
+ /*OpenLayers.Event.observe(this.baseLayersDiv, "click",
+ OpenLayers.Function.bindAsEventListener(this.onLayerClick, this));
+ */
+
+
+ this.dataLbl = document.createElement("div");
+ this.dataLbl.innerHTML = OpenLayers.i18n("overlays");
+ this.dataLbl.style.marginTop = "3px";
+ this.dataLbl.style.marginLeft = "3px";
+ this.dataLbl.style.marginBottom = "3px";
+
+ this.dataLayersDiv = document.createElement("div");
+ this.dataLayersDiv.style.paddingLeft = "10px";
+
+ if (this.ascending) {
+ this.layersDiv.appendChild(this.baseLbl);
+ this.layersDiv.appendChild(this.baseLayersDiv);
+ this.layersDiv.appendChild(this.dataLbl);
+ this.layersDiv.appendChild(this.dataLayersDiv);
+ } else {
+ this.layersDiv.appendChild(this.dataLbl);
+ this.layersDiv.appendChild(this.dataLayersDiv);
+ this.layersDiv.appendChild(this.baseLbl);
+ this.layersDiv.appendChild(this.baseLayersDiv);
+ }
+
+ this.div.appendChild(this.layersDiv);
+
+ OpenLayers.Rico.Corner.round(this.div, {corners: "tl bl",
+ bgColor: "transparent",
+ color: this.activeColor,
+ blend: false});
+
+ OpenLayers.Rico.Corner.changeOpacity(this.layersDiv, 0.75);
+
+ var imgLocation = OpenLayers.Util.getImagesLocation();
+ var sz = new OpenLayers.Size(18,18);
+
+ // maximize button div
+ var img = imgLocation + 'layer-switcher-maximize.png';
+ this.maximizeDiv = OpenLayers.Util.createAlphaImageDiv(
+ "OpenLayers_Control_MaximizeDiv",
+ null,
+ sz,
+ img,
+ "absolute");
+ this.maximizeDiv.style.top = "5px";
+ this.maximizeDiv.style.right = "0px";
+ this.maximizeDiv.style.left = "";
+ this.maximizeDiv.style.display = "none";
+ OpenLayers.Event.observe(this.maximizeDiv, "click",
+ OpenLayers.Function.bindAsEventListener(this.maximizeControl, this)
+ );
+
+ this.div.appendChild(this.maximizeDiv);
+
+ // minimize button div
+ var img = imgLocation + 'layer-switcher-minimize.png';
+ var sz = new OpenLayers.Size(18,18);
+ this.minimizeDiv = OpenLayers.Util.createAlphaImageDiv(
+ "OpenLayers_Control_MinimizeDiv",
+ null,
+ sz,
+ img,
+ "absolute");
+ this.minimizeDiv.style.top = "5px";
+ this.minimizeDiv.style.right = "0px";
+ this.minimizeDiv.style.left = "";
+ this.minimizeDiv.style.display = "none";
+ OpenLayers.Event.observe(this.minimizeDiv, "click",
+ OpenLayers.Function.bindAsEventListener(this.minimizeControl, this)
+ );
+
+ this.div.appendChild(this.minimizeDiv);
+ },
+
+ /**
+ * Method: ignoreEvent
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ ignoreEvent: function(evt) {
+ OpenLayers.Event.stop(evt);
+ },
+
+ /**
+ * Method: mouseDown
+ * Register a local 'mouseDown' flag so that we'll know whether or not
+ * to ignore a mouseUp event
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ mouseDown: function(evt) {
+ this.isMouseDown = true;
+ this.ignoreEvent(evt);
+ },
+
+ /**
+ * Method: mouseUp
+ * If the 'isMouseDown' flag has been set, that means that the drag was
+ * started from within the LayerSwitcher control, and thus we can
+ * ignore the mouseup. Otherwise, let the Event continue.
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ mouseUp: function(evt) {
+ if (this.isMouseDown) {
+ this.isMouseDown = false;
+ this.ignoreEvent(evt);
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Control.LayerSwitcher"
+});
+/* ======================================================================
+ OpenLayers/Control/MouseDefaults.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
+ */
+
+/**
+ * Class: OpenLayers.Control.MouseDefaults
+ *
+ * Inherits from:
+ * - <OpenLayers.Control>
+ */
+OpenLayers.Control.MouseDefaults = OpenLayers.Class(OpenLayers.Control, {
+
+ /** WARNING WARNING WARNING!!!
+ This class is DEPRECATED in 2.4 and will be removed by 3.0.
+ If you need this functionality, use Control.Navigation instead!!! */
+
+ /**
+ * Property: performedDrag
+ * {Boolean}
+ */
+ performedDrag: false,
+
+ /**
+ * Property: wheelObserver
+ * {Function}
+ */
+ wheelObserver: null,
+
+ /**
+ * Constructor: OpenLayers.Control.MouseDefaults
+ */
+ initialize: function() {
+ OpenLayers.Control.prototype.initialize.apply(this, arguments);
+ },
+
+ /**
+ * APIMethod: destroy
+ */
+ destroy: function() {
+
+ if (this.handler) {
+ this.handler.destroy();
+ }
+ this.handler = null;
+
+ this.map.events.un({
+ "click": this.defaultClick,
+ "dblclick": this.defaultDblClick,
+ "mousedown": this.defaultMouseDown,
+ "mouseup": this.defaultMouseUp,
+ "mousemove": this.defaultMouseMove,
+ "mouseout": this.defaultMouseOut,
+ scope: this
+ });
+
+ //unregister mousewheel events specifically on the window and document
+ OpenLayers.Event.stopObserving(window, "DOMMouseScroll",
+ this.wheelObserver);
+ OpenLayers.Event.stopObserving(window, "mousewheel",
+ this.wheelObserver);
+ OpenLayers.Event.stopObserving(document, "mousewheel",
+ this.wheelObserver);
+ this.wheelObserver = null;
+
+ OpenLayers.Control.prototype.destroy.apply(this, arguments);
+ },
+
+ /**
+ * Method: draw
+ */
+ draw: function() {
+ this.map.events.on({
+ "click": this.defaultClick,
+ "dblclick": this.defaultDblClick,
+ "mousedown": this.defaultMouseDown,
+ "mouseup": this.defaultMouseUp,
+ "mousemove": this.defaultMouseMove,
+ "mouseout": this.defaultMouseOut,
+ scope: this
+ });
+
+ this.registerWheelEvents();
+
+ },
+
+ /**
+ * Method: registerWheelEvents
+ */
+ registerWheelEvents: function() {
+
+ this.wheelObserver = OpenLayers.Function.bindAsEventListener(
+ this.onWheelEvent, this
+ );
+
+ //register mousewheel events specifically on the window and document
+ OpenLayers.Event.observe(window, "DOMMouseScroll", this.wheelObserver);
+ OpenLayers.Event.observe(window, "mousewheel", this.wheelObserver);
+ OpenLayers.Event.observe(document, "mousewheel", this.wheelObserver);
+ },
+
+ /**
+ * Method: defaultClick
+ *
+ * Parameters:
+ * evt - {Event}
+ *
+ * Returns:
+ * {Boolean}
+ */
+ defaultClick: function (evt) {
+ if (!OpenLayers.Event.isLeftClick(evt)) {
+ return;
+ }
+ var notAfterDrag = !this.performedDrag;
+ this.performedDrag = false;
+ return notAfterDrag;
+ },
+
+ /**
+ * Method: defaultDblClick
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ defaultDblClick: function (evt) {
+ var newCenter = this.map.getLonLatFromViewPortPx( evt.xy );
+ this.map.setCenter(newCenter, this.map.zoom + 1);
+ OpenLayers.Event.stop(evt);
+ return false;
+ },
+
+ /**
+ * Method: defaultMouseDown
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ defaultMouseDown: function (evt) {
+ if (!OpenLayers.Event.isLeftClick(evt)) {
+ return;
+ }
+ this.mouseDragStart = evt.xy.clone();
+ this.performedDrag = false;
+ if (evt.shiftKey) {
+ this.map.div.style.cursor = "crosshair";
+ this.zoomBox = OpenLayers.Util.createDiv('zoomBox',
+ this.mouseDragStart,
+ null,
+ null,
+ "absolute",
+ "2px solid red");
+ this.zoomBox.style.backgroundColor = "white";
+ this.zoomBox.style.filter = "alpha(opacity=50)"; // IE
+ this.zoomBox.style.opacity = "0.50";
+ this.zoomBox.style.fontSize = "1px";
+ this.zoomBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
+ this.map.viewPortDiv.appendChild(this.zoomBox);
+ }
+ document.onselectstart=function() { return false; };
+ OpenLayers.Event.stop(evt);
+ },
+
+ /**
+ * Method: defaultMouseMove
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ defaultMouseMove: function (evt) {
+ // record the mouse position, used in onWheelEvent
+ this.mousePosition = evt.xy.clone();
+
+ if (this.mouseDragStart != null) {
+ if (this.zoomBox) {
+ var deltaX = Math.abs(this.mouseDragStart.x - evt.xy.x);
+ var deltaY = Math.abs(this.mouseDragStart.y - evt.xy.y);
+ this.zoomBox.style.width = Math.max(1, deltaX) + "px";
+ this.zoomBox.style.height = Math.max(1, deltaY) + "px";
+ if (evt.xy.x < this.mouseDragStart.x) {
+ this.zoomBox.style.left = evt.xy.x+"px";
+ }
+ if (evt.xy.y < this.mouseDragStart.y) {
+ this.zoomBox.style.top = evt.xy.y+"px";
+ }
+ } else {
+ var deltaX = this.mouseDragStart.x - evt.xy.x;
+ var deltaY = this.mouseDragStart.y - evt.xy.y;
+ var size = this.map.getSize();
+ var newXY = new OpenLayers.Pixel(size.w / 2 + deltaX,
+ size.h / 2 + deltaY);
+ var newCenter = this.map.getLonLatFromViewPortPx( newXY );
+ this.map.setCenter(newCenter, null, true);
+ this.mouseDragStart = evt.xy.clone();
+ this.map.div.style.cursor = "move";
+ }
+ this.performedDrag = true;
+ }
+ },
+
+ /**
+ * Method: defaultMouseUp
+ *
+ * Parameters:
+ * evt - {<OpenLayers.Event>}
+ */
+ defaultMouseUp: function (evt) {
+ if (!OpenLayers.Event.isLeftClick(evt)) {
+ return;
+ }
+ if (this.zoomBox) {
+ this.zoomBoxEnd(evt);
+ } else {
+ if (this.performedDrag) {
+ this.map.setCenter(this.map.center);
+ }
+ }
+ document.onselectstart=null;
+ this.mouseDragStart = null;
+ this.map.div.style.cursor = "";
+ },
+
+ /**
+ * Method: defaultMouseOut
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ defaultMouseOut: function (evt) {
+ if (this.mouseDragStart != null &&
+ OpenLayers.Util.mouseLeft(evt, this.map.div)) {
+ if (this.zoomBox) {
+ this.removeZoomBox();
+ }
+ this.mouseDragStart = null;
+ }
+ },
+
+
+ /**
+ * Method: defaultWheelUp
+ * User spun scroll wheel up
+ *
+ */
+ defaultWheelUp: function(evt) {
+ if (this.map.getZoom() <= this.map.getNumZoomLevels()) {
+ this.map.setCenter(this.map.getLonLatFromPixel(evt.xy),
+ this.map.getZoom() + 1);
+ }
+ },
+
+ /**
+ * Method: defaultWheelDown
+ * User spun scroll wheel down
+ */
+ defaultWheelDown: function(evt) {
+ if (this.map.getZoom() > 0) {
+ this.map.setCenter(this.map.getLonLatFromPixel(evt.xy),
+ this.map.getZoom() - 1);
+ }
+ },
+
+ /**
+ * Method: zoomBoxEnd
+ * Zoombox function.
+ */
+ zoomBoxEnd: function(evt) {
+ if (this.mouseDragStart != null) {
+ if (Math.abs(this.mouseDragStart.x - evt.xy.x) > 5 ||
+ Math.abs(this.mouseDragStart.y - evt.xy.y) > 5) {
+ var start = this.map.getLonLatFromViewPortPx( this.mouseDragStart );
+ var end = this.map.getLonLatFromViewPortPx( evt.xy );
+ var top = Math.max(start.lat, end.lat);
+ var bottom = Math.min(start.lat, end.lat);
+ var left = Math.min(start.lon, end.lon);
+ var right = Math.max(start.lon, end.lon);
+ var bounds = new OpenLayers.Bounds(left, bottom, right, top);
+ this.map.zoomToExtent(bounds);
+ } else {
+ var end = this.map.getLonLatFromViewPortPx( evt.xy );
+ this.map.setCenter(new OpenLayers.LonLat(
+ (end.lon),
+ (end.lat)
+ ), this.map.getZoom() + 1);
+ }
+ this.removeZoomBox();
+ }
+ },
+
+ /**
+ * Method: removeZoomBox
+ * Remove the zoombox from the screen and nullify our reference to it.
+ */
+ removeZoomBox: function() {
+ this.map.viewPortDiv.removeChild(this.zoomBox);
+ this.zoomBox = null;
+ },
+
+
+/**
+ * Mouse ScrollWheel code thanks to http://adomas.org/javascript-mouse-wheel/
+ */
+
+
+ /**
+ * Method: onWheelEvent
+ * Catch the wheel event and handle it xbrowserly
+ *
+ * Parameters:
+ * e - {Event}
+ */
+ onWheelEvent: function(e){
+
+ // first determine whether or not the wheeling was inside the map
+ var inMap = false;
+ var elem = OpenLayers.Event.element(e);
+ while(elem != null) {
+ if (this.map && elem == this.map.div) {
+ inMap = true;
+ break;
+ }
+ elem = elem.parentNode;
+ }
+
+ if (inMap) {
+
+ var delta = 0;
+ if (!e) {
+ e = window.event;
+ }
+ if (e.wheelDelta) {
+ delta = e.wheelDelta/120;
+ if (window.opera && window.opera.version() < 9.2) {
+ delta = -delta;
+ }
+ } else if (e.detail) {
+ delta = -e.detail / 3;
+ }
+ if (delta) {
+ // add the mouse position to the event because mozilla has a bug
+ // with clientX and clientY (see https://bugzilla.mozilla.org/show_bug.cgi?id=352179)
+ // getLonLatFromViewPortPx(e) returns wrong values
+ e.xy = this.mousePosition;
+
+ if (delta < 0) {
+ this.defaultWheelDown(e);
+ } else {
+ this.defaultWheelUp(e);
+ }
+ }
+
+ //only wheel the map, not the window
+ OpenLayers.Event.stop(e);
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Control.MouseDefaults"
+});
+/* ======================================================================
OpenLayers/Control/MousePosition.js
====================================================================== */
@@ -5630,7 +8726,7 @@
* APIProperty: numDigits
* {Integer}
*/
- numdigits: 5,
+ numDigits: 5,
/**
* APIProperty: granularity
@@ -5734,7 +8830,7 @@
* lonLat - {<OpenLayers.LonLat>} Location to display
*/
formatOutput: function(lonLat) {
- var digits = parseInt(this.numdigits);
+ var digits = parseInt(this.numDigits);
var newHtml =
this.prefix +
lonLat.lon.toFixed(digits) +
@@ -5755,6 +8851,408 @@
CLASS_NAME: "OpenLayers.Control.MousePosition"
});
/* ======================================================================
+ OpenLayers/Control/NavigationHistory.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
+ */
+
+/**
+ * Class: OpenLayers.Control.NavigationHistory
+ * A navigation history control. This is a meta-control, that creates two
+ * dependent controls: <previous> and <next>. Call the trigger method
+ * on the <previous> and <next> controls to restore previous and next
+ * history states. The previous and next controls will become active
+ * when there are available states to restore and will become deactive
+ * when there are no states to restore.
+ *
+ * Inherits from:
+ * - <OpenLayers.Control>
+ */
+OpenLayers.Control.NavigationHistory = OpenLayers.Class(OpenLayers.Control, {
+
+ /**
+ * Property: type
+ * {String} Note that this control is not intended to be added directly
+ * to a control panel. Instead, add the sub-controls previous and
+ * next. These sub-controls are button type controls that activate
+ * and deactivate themselves. If this parent control is added to
+ * a panel, it will act as a toggle.
+ */
+ type: OpenLayers.Control.TYPE_TOGGLE,
+
+ /**
+ * APIProperty: previous
+ * {<OpenLayers.Control>} A button type control whose trigger method restores
+ * the previous state managed by this control.
+ */
+ previous: null,
+
+ /**
+ * APIProperty: previousOptions
+ * {Object} Set this property on the options argument of the constructor
+ * to set optional properties on the <previous> control.
+ */
+ previousOptions: null,
+
+ /**
+ * APIProperty: next
+ * {<OpenLayers.Control>} A button type control whose trigger method restores
+ * the next state managed by this control.
+ */
+ next: null,
+
+ /**
+ * APIProperty: nextOptions
+ * {Object} Set this property on the options argument of the constructor
+ * to set optional properties on the <next> control.
+ */
+ nextOptions: null,
+
+ /**
+ * APIProperty: limit
+ * {Integer} Optional limit on the number of history items to retain. If
+ * null, there is no limit. Default is 50.
+ */
+ limit: 50,
+
+ /**
+ * Property: activateOnDraw
+ * {Boolean} Activate the control when it is first added to the map.
+ * Default is true.
+ */
+ activateOnDraw: true,
+
+ /**
+ * Property: clearOnDeactivate
+ * {Boolean} Clear the history when the control is deactivated. Default
+ * is false.
+ */
+ clearOnDeactivate: false,
+
+ /**
+ * Property: registry
+ * {Object} An object with keys corresponding to event types. Values
+ * are functions that return an object representing the current state.
+ */
+ registry: null,
+
+ /**
+ * Property: nextStack
+ * {Array} Array of items in the history.
+ */
+ nextStack: null,
+
+ /**
+ * Property: previousStack
+ * {Array} List of items in the history. First item represents the current
+ * state.
+ */
+ previousStack: null,
+
+ /**
+ * Property: listeners
+ * {Object} An object containing properties corresponding to event types.
+ * This object is used to configure the control and is modified on
+ * construction.
+ */
+ listeners: null,
+
+ /**
+ * Property: restoring
+ * {Boolean} Currently restoring a history state. This is set to true
+ * before calling restore and set to false after restore returns.
+ */
+ restoring: false,
+
+ /**
+ * Constructor: OpenLayers.Control.NavigationHistory
+ *
+ * Parameters:
+ * options - {Object} An optional object whose properties will be used
+ * to extend the control.
+ */
+ initialize: function(options) {
+ OpenLayers.Control.prototype.initialize.apply(this, [options]);
+
+ this.registry = OpenLayers.Util.extend({
+ "moveend": function() {
+ return {
+ center: this.map.getCenter(),
+ resolution: this.map.getResolution()
+ };
+ }
+ }, this.registry);
+
+ this.clear();
+
+ var previousOptions = {
+ trigger: OpenLayers.Function.bind(this.previousTrigger, this),
+ displayClass: this.displayClass + " " + this.displayClass + "Previous"
+ };
+ OpenLayers.Util.extend(previousOptions, this.previousOptions);
+ this.previous = new OpenLayers.Control.Button(previousOptions);
+
+ var nextOptions = {
+ trigger: OpenLayers.Function.bind(this.nextTrigger, this),
+ displayClass: this.displayClass + " " + this.displayClass + "Next"
+ };
+ OpenLayers.Util.extend(nextOptions, this.nextOptions);
+ this.next = new OpenLayers.Control.Button(nextOptions);
+
+ },
+
+ /**
+ * Method: onPreviousChange
+ * Called when the previous history stack changes.
+ *
+ * Parameters:
+ * state - {Object} An object representing the state to be restored
+ * if previous is triggered again or null if no previous states remain.
+ * length - {Integer} The number of remaining previous states that can
+ * be restored.
+ */
+ onPreviousChange: function(state, length) {
+ if(state && !this.previous.active) {
+ this.previous.activate();
+ } else if(!state && this.previous.active) {
+ this.previous.deactivate();
+ }
+ },
+
+ /**
+ * Method: onNextChange
+ * Called when the next history stack changes.
+ *
+ * Parameters:
+ * state - {Object} An object representing the state to be restored
+ * if next is triggered again or null if no next states remain.
+ * length - {Integer} The number of remaining next states that can
+ * be restored.
+ */
+ onNextChange: function(state, length) {
+ if(state && !this.next.active) {
+ this.next.activate();
+ } else if(!state && this.next.active) {
+ this.next.deactivate();
+ }
+ },
+
+ /**
+ * APIMethod: destroy
+ * Destroy the control.
+ */
+ destroy: function() {
+ OpenLayers.Control.prototype.destroy.apply(this);
+ this.previous.destroy();
+ this.next.destroy();
+ this.deactivate();
+ for(var prop in this) {
+ this[prop] = null;
+ }
+ },
+
+ /**
+ * Method: setMap
+ * Set the map property for the control and <previous> and <next> child
+ * controls.
+ *
+ * Parameters:
+ * map - {<OpenLayers.Map>}
+ */
+ setMap: function(map) {
+ this.map = map;
+ this.next.setMap(map);
+ this.previous.setMap(map);
+ },
+
+ /**
+ * Method: draw
+ * Called when the control is added to the map.
+ */
+ draw: function() {
+ OpenLayers.Control.prototype.draw.apply(this, arguments);
+ this.next.draw();
+ this.previous.draw();
+ if(this.activateOnDraw) {
+ this.activate();
+ }
+ },
+
+ /**
+ * Method: previousTrigger
+ * Restore the previous state. If no items are in the previous history
+ * stack, this has no effect.
+ *
+ * Returns:
+ * {Object} Item representing state that was restored. Undefined if no
+ * items are in the previous history stack.
+ */
+ previousTrigger: function() {
+ var current = this.previousStack.shift();
+ var state = this.previousStack.shift();
+ if(state != undefined) {
+ this.nextStack.unshift(current);
+ this.previousStack.unshift(state);
+ this.restoring = true;
+ this.restore(state);
+ this.restoring = false;
+ this.onNextChange(this.nextStack[0], this.nextStack.length);
+ this.onPreviousChange(
+ this.previousStack[1], this.previousStack.length - 1
+ );
+ } else {
+ this.previousStack.unshift(current);
+ }
+ return state;
+ },
+
+ /**
+ * APIMethod: nextTrigger
+ * Restore the next state. If no items are in the next history
+ * stack, this has no effect. The next history stack is populated
+ * as states are restored from the previous history stack.
+ *
+ * Returns:
+ * {Object} Item representing state that was restored. Undefined if no
+ * items are in the next history stack.
+ */
+ nextTrigger: function() {
+ var state = this.nextStack.shift();
+ if(state != undefined) {
+ this.previousStack.unshift(state);
+ this.restoring = true;
+ this.restore(state);
+ this.restoring = false;
+ this.onNextChange(this.nextStack[0], this.nextStack.length);
+ this.onPreviousChange(
+ this.previousStack[1], this.previousStack.length - 1
+ );
+ }
+ return state;
+ },
+
+ /**
+ * APIMethod: clear
+ * Clear history.
+ */
+ clear: function() {
+ this.previousStack = [];
+ this.nextStack = [];
+ },
+
+ /**
+ * Method: restore
+ * Update the state with the given object.
+ *
+ * Parameters:
+ * state - {Object} An object representing the state to restore.
+ */
+ restore: function(state) {
+ var zoom = this.map.getZoomForResolution(state.resolution);
+ this.map.setCenter(state.center, zoom);
+ },
+
+ /**
+ * Method: setListeners
+ * Sets functions to be registered in the listeners object.
+ */
+ setListeners: function() {
+ this.listeners = {};
+ for(var type in this.registry) {
+ this.listeners[type] = OpenLayers.Function.bind(function() {
+ if(!this.restoring) {
+ var state = this.registry[type].apply(this, arguments);
+ this.previousStack.unshift(state);
+ if(this.previousStack.length > 1) {
+ this.onPreviousChange(
+ this.previousStack[1], this.previousStack.length - 1
+ );
+ }
+ if(this.previousStack.length > (this.limit + 1)) {
+ this.previousStack.pop();
+ }
+ if(this.nextStack.length > 0) {
+ this.nextStack = [];
+ this.onNextChange(null, 0);
+ }
+ }
+ return true;
+ }, this);
+ }
+ },
+
+ /**
+ * APIMethod: activate
+ * Activate the control. This registers any listeners.
+ *
+ * Returns:
+ * {Boolean} Control successfully activated.
+ */
+ activate: function() {
+ var activated = false;
+ if(this.map) {
+ if(OpenLayers.Control.prototype.activate.apply(this)) {
+ if(this.listeners == null) {
+ this.setListeners();
+ }
+ for(var type in this.listeners) {
+ this.map.events.register(type, this, this.listeners[type]);
+ }
+ activated = true;
+ if(this.previousStack.length == 0) {
+ this.initStack();
+ }
+ }
+ }
+ return activated;
+ },
+
+ /**
+ * Method: initStack
+ * Called after the control is activated if the previous history stack is
+ * empty.
+ */
+ initStack: function() {
+ if(this.map.getCenter()) {
+ this.listeners.moveend();
+ }
+ },
+
+ /**
+ * APIMethod: deactivate
+ * Deactivate the control. This unregisters any listeners.
+ *
+ * Returns:
+ * {Boolean} Control successfully deactivated.
+ */
+ deactivate: function() {
+ var deactivated = false;
+ if(this.map) {
+ if(OpenLayers.Control.prototype.deactivate.apply(this)) {
+ for(var type in this.listeners) {
+ this.map.events.unregister(
+ type, this, this.listeners[type]
+ );
+ }
+ if(this.clearOnDeactivate) {
+ this.clear();
+ }
+ deactivated = true;
+ }
+ }
+ return deactivated;
+ },
+
+ CLASS_NAME: "OpenLayers.Control.NavigationHistory"
+});
+
+/* ======================================================================
OpenLayers/Control/PanZoom.js
====================================================================== */
@@ -5871,7 +9369,7 @@
_addButton:function(id, img, xy, sz) {
var imgLocation = OpenLayers.Util.getImagesLocation() + img;
var btn = OpenLayers.Util.createAlphaImageDiv(
- "OpenLayers_Control_PanZoom_" + id,
+ this.id + "_" + id,
xy, sz, imgLocation, "absolute");
//we want to add the outer div
@@ -5959,6 +9457,373 @@
*/
OpenLayers.Control.PanZoom.Y = 4;
/* ======================================================================
+ OpenLayers/Control/Panel.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
+ */
+
+/**
+ * Class: OpenLayers.Control.Panel
+ *
+ * Inherits from:
+ * - <OpenLayers.Control>
+ */
+OpenLayers.Control.Panel = OpenLayers.Class(OpenLayers.Control, {
+ /**
+ * Property: controls
+ * {Array(<OpenLayers.Control>)}
+ */
+ controls: null,
+
+ /**
+ * APIProperty: defaultControl
+ * <OpenLayers.Control> The control which is activated when the control is
+ * activated (turned on), which also happens at instantiation.
+ */
+ defaultControl: null,
+
+ /**
+ * Constructor: OpenLayers.Control.Panel
+ * Create a new control panel.
+ *
+ * Parameters:
+ * options - {Object} An optional object whose properties will be used
+ * to extend the control.
+ */
+ initialize: function(options) {
+ OpenLayers.Control.prototype.initialize.apply(this, [options]);
+ this.controls = [];
+ },
+
+ /**
+ * APIMethod: destroy
+ */
+ destroy: function() {
+ OpenLayers.Control.prototype.destroy.apply(this, arguments);
+ for(var i = this.controls.length - 1 ; i >= 0; i--) {
+ if(this.controls[i].events) {
+ this.controls[i].events.un({
+ "activate": this.redraw,
+ "deactivate": this.redraw,
+ scope: this
+ });
+ }
+ OpenLayers.Event.stopObservingElement(this.controls[i].panel_div);
+ this.controls[i].panel_div = null;
+ }
+ },
+
+ /**
+ * APIMethod: activate
+ */
+ activate: function() {
+ if (OpenLayers.Control.prototype.activate.apply(this, arguments)) {
+ for(var i=0, len=this.controls.length; i<len; i++) {
+ if (this.controls[i] == this.defaultControl) {
+ this.controls[i].activate();
+ }
+ }
+ this.redraw();
+ return true;
+ } else {
+ return false;
+ }
+ },
+
+ /**
+ * APIMethod: deactivate
+ */
+ deactivate: function() {
+ if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {
+ for(var i=0, len=this.controls.length; i<len; i++) {
+ this.controls[i].deactivate();
+ }
+ return true;
+ } else {
+ return false;
+ }
+ },
+
+ /**
+ * Method: draw
+ *
+ * Returns:
+ * {DOMElement}
+ */
+ draw: function() {
+ OpenLayers.Control.prototype.draw.apply(this, arguments);
+ for (var i=0, len=this.controls.length; i<len; i++) {
+ this.map.addControl(this.controls[i]);
+ this.controls[i].deactivate();
+ this.controls[i].events.on({
+ "activate": this.redraw,
+ "deactivate": this.redraw,
+ scope: this
+ });
+ }
+ this.activate();
+ return this.div;
+ },
+
+ /**
+ * Method: redraw
+ */
+ redraw: function() {
+ this.div.innerHTML = "";
+ if (this.active) {
+ for (var i=0, len=this.controls.length; i<len; i++) {
+ var element = this.controls[i].panel_div;
+ if (this.controls[i].active) {
+ element.className = this.controls[i].displayClass + "ItemActive";
+ } else {
+ element.className = this.controls[i].displayClass + "ItemInactive";
+ }
+ this.div.appendChild(element);
+ }
+ }
+ },
+
+ /**
+ * APIMethod: activateControl
+ *
+ * Parameters:
+ * control - {<OpenLayers.Control>}
+ */
+ activateControl: function (control) {
+ if (!this.active) { return false; }
+ if (control.type == OpenLayers.Control.TYPE_BUTTON) {
+ control.trigger();
+ this.redraw();
+ return;
+ }
+ if (control.type == OpenLayers.Control.TYPE_TOGGLE) {
+ if (control.active) {
+ control.deactivate();
+ } else {
+ control.activate();
+ }
+ this.redraw();
+ return;
+ }
+ for (var i=0, len=this.controls.length; i<len; i++) {
+ if (this.controls[i] != control) {
+ if (this.controls[i].type != OpenLayers.Control.TYPE_TOGGLE) {
+ this.controls[i].deactivate();
+ }
+ }
+ }
+ control.activate();
+ },
+
+ /**
+ * APIMethod: addControls
+ * To build a toolbar, you add a set of controls to it. addControls
+ * lets you add a single control or a list of controls to the
+ * Control Panel.
+ *
+ * Parameters:
+ * controls - {<OpenLayers.Control>}
+ */
+ addControls: function(controls) {
+ if (!(controls instanceof Array)) {
+ controls = [controls];
+ }
+ this.controls = this.controls.concat(controls);
+
+ // Give each control a panel_div which will be used later.
+ // Access to this div is via the panel_div attribute of the
+ // control added to the panel.
+ // Also, stop mousedowns and clicks, but don't stop mouseup,
+ // since they need to pass through.
+ for (var i=0, len=controls.length; i<len; i++) {
+ var element = document.createElement("div");
+ var textNode = document.createTextNode(" ");
+ controls[i].panel_div = element;
+ if (controls[i].title != "") {
+ controls[i].panel_div.title = controls[i].title;
+ }
+ OpenLayers.Event.observe(controls[i].panel_div, "click",
+ OpenLayers.Function.bind(this.onClick, this, controls[i]));
+ OpenLayers.Event.observe(controls[i].panel_div, "mousedown",
+ OpenLayers.Function.bindAsEventListener(OpenLayers.Event.stop));
+ }
+
+ if (this.map) { // map.addControl() has already been called on the panel
+ for (var i=0, len=controls.length; i<len; i++) {
+ this.map.addControl(controls[i]);
+ controls[i].deactivate();
+ controls[i].events.on({
+ "activate": this.redraw,
+ "deactivate": this.redraw,
+ scope: this
+ });
+ }
+ this.redraw();
+ }
+ },
+
+ /**
+ * Method: onClick
+ */
+ onClick: function (ctrl, evt) {
+ OpenLayers.Event.stop(evt ? evt : window.event);
+ this.activateControl(ctrl);
+ },
+
+ /**
+ * APIMethod: getControlsBy
+ * Get a list of controls with properties matching the given criteria.
+ *
+ * Parameter:
+ * property - {String} A control property to be matched.
+ * match - {String | Object} A string to match. Can also be a regular
+ * expression literal or object. In addition, it can be any object
+ * with a method named test. For reqular expressions or other, if
+ * match.test(control[property]) evaluates to true, the control will be
+ * included in the array returned. If no controls are found, an empty
+ * array is returned.
+ *
+ * Returns:
+ * {Array(<OpenLayers.Control>)} A list of controls matching the given criteria.
+ * An empty array is returned if no matches are found.
+ */
+ getControlsBy: function(property, match) {
+ var test = (typeof match.test == "function");
+ var found = OpenLayers.Array.filter(this.controls, function(item) {
+ return item[property] == match || (test && match.test(item[property]));
+ });
+ return found;
+ },
+
+ /**
+ * APIMethod: getControlsByName
+ * Get a list of contorls with names matching the given name.
+ *
+ * Parameter:
+ * match - {String | Object} A control name. The name can also be a regular
+ * expression literal or object. In addition, it can be any object
+ * with a method named test. For reqular expressions or other, if
+ * name.test(control.name) evaluates to true, the control will be included
+ * in the list of controls returned. If no controls are found, an empty
+ * array is returned.
+ *
+ * Returns:
+ * {Array(<OpenLayers.Control>)} A list of controls matching the given name.
+ * An empty array is returned if no matches are found.
+ */
+ getControlsByName: function(match) {
+ return this.getControlsBy("name", match);
+ },
+
+ /**
+ * APIMethod: getControlsByClass
+ * Get a list of controls of a given type (CLASS_NAME).
+ *
+ * Parameter:
+ * match - {String | Object} A control class name. The type can also be a
+ * regular expression literal or object. In addition, it can be any
+ * object with a method named test. For reqular expressions or other,
+ * if type.test(control.CLASS_NAME) evaluates to true, the control will
+ * be included in the list of controls returned. If no controls are
+ * found, an empty array is returned.
+ *
+ * Returns:
+ * {Array(<OpenLayers.Control>)} A list of controls matching the given type.
+ * An empty array is returned if no matches are found.
+ */
+ getControlsByClass: function(match) {
+ return this.getControlsBy("CLASS_NAME", match);
+ },
+
+ CLASS_NAME: "OpenLayers.Control.Panel"
+});
+
+/* ======================================================================
+ OpenLayers/Control/Scale.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
+ */
+
+/**
+ * Class: OpenLayers.Control.Scale
+ * Display a small scale indicator on the map.
+ * Inherits from:
+ * - <OpenLayers.Control>
+ */
+OpenLayers.Control.Scale = OpenLayers.Class(OpenLayers.Control, {
+
+ /**
+ * Parameter: element
+ * {DOMElement}
+ */
+ element: null,
+
+ /**
+ * Constructor: OpenLayers.Control.Scale
+ *
+ * Parameters:
+ * element - {DOMElement}
+ * options - {Object}
+ */
+ initialize: function(element, options) {
+ OpenLayers.Control.prototype.initialize.apply(this, [options]);
+ this.element = OpenLayers.Util.getElement(element);
+ },
+
+ /**
+ * Method: draw
+ *
+ * Returns:
+ * {DOMElement}
+ */
+ draw: function() {
+ OpenLayers.Control.prototype.draw.apply(this, arguments);
+ if (!this.element) {
+ this.element = document.createElement("div");
+ this.div.appendChild(this.element);
+ }
+ this.map.events.register( 'moveend', this, this.updateScale);
+ this.updateScale();
+ return this.div;
+ },
+
+ /**
+ * Method: updateScale
+ */
+ updateScale: function() {
+ var scale = this.map.getScale();
+ if (!scale) {
+ return;
+ }
+
+ if (scale >= 9500 && scale <= 950000) {
+ scale = Math.round(scale / 1000) + "K";
+ } else if (scale >= 950000) {
+ scale = Math.round(scale / 1000000) + "M";
+ } else {
+ scale = Math.round(scale);
+ }
+
+ this.element.innerHTML = OpenLayers.i18n("scale", {'scaleDenom':scale});
+ },
+
+ CLASS_NAME: "OpenLayers.Control.Scale"
+});
+
+/* ======================================================================
OpenLayers/Control/ScaleLine.js
====================================================================== */
@@ -6117,7 +9982,7 @@
return;
}
- var curMapUnits = this.map.units;
+ var curMapUnits = this.map.getUnits();
var inches = OpenLayers.INCHES_PER_UNIT;
// convert maxWidth to map units
@@ -6163,6 +10028,45 @@
});
/* ======================================================================
+ OpenLayers/Control/ZoomToMaxExtent.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
+ */
+
+/**
+ * Class: OpenLayers.Control.ZoomToMaxExtent
+ * Imlements a very simple button control. Designed to be used with a
+ * <OpenLayers.Control.Panel>.
+ *
+ * Inherits from:
+ * - <OpenLayers.Control>
+ */
+OpenLayers.Control.ZoomToMaxExtent = OpenLayers.Class(OpenLayers.Control, {
+ /**
+ * Property: type
+ * TYPE_BUTTON.
+ */
+ type: OpenLayers.Control.TYPE_BUTTON,
+
+ /*
+ * Method: trigger
+ * Do the zoom.
+ */
+ trigger: function() {
+ if (this.map) {
+ this.map.zoomToMaxExtent();
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Control.ZoomToMaxExtent"
+});
+/* ======================================================================
OpenLayers/Events.js
====================================================================== */
@@ -6273,6 +10177,21 @@
},
/**
+ * Method: isRightClick
+ * Determine whether event was caused by a right mouse click.
+ *
+ * Parameters:
+ * event - {Event}
+ *
+ * Returns:
+ * {Boolean}
+ */
+ isRightClick: function(event) {
+ return (((event.which) && (event.which == 3)) ||
+ ((event.button) && (event.button == 2)));
+ },
+
+ /**
* Method: stop
* Stops an event from propagating.
*
@@ -6520,7 +10439,7 @@
BROWSER_EVENTS: [
"mouseover", "mouseout",
"mousedown", "mouseup", "mousemove",
- "click", "dblclick",
+ "click", "dblclick", "rightclick", "dblrightclick",
"resize", "focus", "blur"
],
@@ -6560,6 +10479,34 @@
*/
fallThrough: null,
+ /**
+ * APIProperty: includeXY
+ * {Boolean} Should the .xy property automatically be created for browser
+ * mouse events? In general, this should be false. If it is true, then
+ * mouse events will automatically generate a '.xy' property on the
+ * event object that is passed. (Prior to OpenLayers 2.7, this was true
+ * by default.) Otherwise, you can call the getMousePosition on the
+ * relevant events handler on the object available via the 'evt.object'
+ * property of the evt object. So, for most events, you can call:
+ * function named(evt) {
+ * this.xy = this.object.events.getMousePosition(evt)
+ * }
+ *
+ * This option typically defaults to false for performance reasons:
+ * when creating an events object whose primary purpose is to manage
+ * relatively positioned mouse events within a div, it may make
+ * sense to set it to true.
+ *
+ * This option is also used to control whether the events object caches
+ * offsets. If this is false, it will not: the reason for this is that
+ * it is only expected to be called many times if the includeXY property
+ * is set to true. If you set this to true, you are expected to clear
+ * the offset cache manually (using this.clearMouseCache()) if:
+ * the border of the element changes
+ * the location of the element in the page changes
+ */
+ includeXY: false,
+
/**
* Constructor: OpenLayers.Events
* Construct an OpenLayers.Events object.
@@ -6570,11 +10517,12 @@
* eventTypes - {Array(String)} Array of custom application events
* fallThrough - {Boolean} Allow events to fall through after these have
* been handled?
+ * options - {Object} Options for the events object.
*/
- initialize: function (object, element, eventTypes, fallThrough) {
+ initialize: function (object, element, eventTypes, fallThrough, options) {
+ OpenLayers.Util.extend(this, options);
this.object = object;
this.element = element;
- this.eventTypes = eventTypes;
this.fallThrough = fallThrough;
this.listeners = {};
@@ -6586,9 +10534,10 @@
// if eventTypes is specified, create a listeners list for each
// custom application event.
- if (this.eventTypes != null) {
- for (var i = 0; i < this.eventTypes.length; i++) {
- this.addEventType(this.eventTypes[i]);
+ this.eventTypes = [];
+ if (eventTypes != null) {
+ for (var i=0, len=eventTypes.length; i<len; i++) {
+ this.addEventType(eventTypes[i]);
}
}
@@ -6625,6 +10574,7 @@
*/
addEventType: function(eventName) {
if (!this.listeners[eventName]) {
+ this.eventTypes.push(eventName);
this.listeners[eventName] = [];
}
},
@@ -6636,7 +10586,7 @@
* element - {HTMLDOMElement} a DOM element to attach browser events to
*/
attachToElement: function (element) {
- for (var i = 0; i < this.BROWSER_EVENTS.length; i++) {
+ for (var i=0, len=this.BROWSER_EVENTS.length; i<len; i++) {
var eventType = this.BROWSER_EVENTS[i];
// every browser event has a corresponding application event
@@ -6700,16 +10650,14 @@
*/
register: function (type, obj, func) {
- if (func != null &&
- ((this.eventTypes && OpenLayers.Util.indexOf(this.eventTypes, type) != -1) ||
- OpenLayers.Util.indexOf(this.BROWSER_EVENTS, type) != -1)) {
+ if ( (func != null) &&
+ (OpenLayers.Util.indexOf(this.eventTypes, type) != -1) ) {
+
if (obj == null) {
obj = this.object;
}
var listeners = this.listeners[type];
- if (listeners != null) {
- listeners.push( {obj: obj, func: func} );
- }
+ listeners.push( {obj: obj, func: func} );
}
},
@@ -6778,7 +10726,7 @@
}
var listeners = this.listeners[type];
if (listeners != null) {
- for (var i = 0; i < listeners.length; i++) {
+ for (var i=0, len=listeners.length; i<len; i++) {
if (listeners[i].obj == obj && listeners[i].func == func) {
listeners.splice(i, 1);
break;
@@ -6832,7 +10780,7 @@
this.listeners[type].slice() : null;
if ((listeners != null) && (listeners.length > 0)) {
var continueChain;
- for (var i = 0; i < listeners.length; i++) {
+ for (var i=0, len=listeners.length; i<len; i++) {
var callback = listeners[i];
// bind the context to callback.obj
continueChain = callback.func.apply(callback.obj, [evt]);
@@ -6860,11 +10808,25 @@
* evt - {Event}
*/
handleBrowserEvent: function (evt) {
- evt.xy = this.getMousePosition(evt);
+ if (this.includeXY) {
+ evt.xy = this.getMousePosition(evt);
+ }
this.triggerEvent(evt.type, evt);
},
/**
+ * APIMethod: clearMouseCache
+ * Clear cached data about the mouse position. This should be called any
+ * time the element that events are registered on changes position
+ * within the page.
+ */
+ clearMouseCache: function() {
+ this.element.scrolls = null;
+ this.element.lefttop = null;
+ this.element.offsets = null;
+ },
+
+ /**
* Method: getMousePosition
*
* Parameters:
@@ -6875,26 +10837,137 @@
* for offsets
*/
getMousePosition: function (evt) {
- if (!this.element.offsets) {
- this.element.offsets = OpenLayers.Util.pagePosition(this.element);
- this.element.offsets[0] += (document.documentElement.scrollLeft
+ if (!this.includeXY) {
+ this.clearMouseCache();
+ } else if (!this.element.hasScrollEvent) {
+ OpenLayers.Event.observe(window, 'scroll',
+ OpenLayers.Function.bind(this.clearMouseCache, this));
+ this.element.hasScrollEvent = true;
+ }
+
+ if (!this.element.scrolls) {
+ this.element.scrolls = [];
+ this.element.scrolls[0] = (document.documentElement.scrollLeft
|| document.body.scrollLeft);
- this.element.offsets[1] += (document.documentElement.scrollTop
+ this.element.scrolls[1] = (document.documentElement.scrollTop
|| document.body.scrollTop);
}
+
+ if (!this.element.lefttop) {
+ this.element.lefttop = [];
+ this.element.lefttop[0] = (document.documentElement.clientLeft || 0);
+ this.element.lefttop[1] = (document.documentElement.clientTop || 0);
+ }
+
+ if (!this.element.offsets) {
+ this.element.offsets = OpenLayers.Util.pagePosition(this.element);
+ this.element.offsets[0] += this.element.scrolls[0];
+ this.element.offsets[1] += this.element.scrolls[1];
+ }
return new OpenLayers.Pixel(
- (evt.clientX + (document.documentElement.scrollLeft
- || document.body.scrollLeft)) - this.element.offsets[0]
- - (document.documentElement.clientLeft || 0),
- (evt.clientY + (document.documentElement.scrollTop
- || document.body.scrollTop)) - this.element.offsets[1]
- - (document.documentElement.clientTop || 0)
+ (evt.clientX + this.element.scrolls[0]) - this.element.offsets[0]
+ - this.element.lefttop[0],
+ (evt.clientY + this.element.scrolls[1]) - this.element.offsets[1]
+ - this.element.lefttop[1]
);
},
CLASS_NAME: "OpenLayers.Events"
});
/* ======================================================================
+ OpenLayers/Format.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/Util.js
+ */
+
+/**
+ * Class: OpenLayers.Format
+ * Base class for format reading/writing a variety of formats. Subclasses
+ * of OpenLayers.Format are expected to have read and write methods.
+ */
+OpenLayers.Format = OpenLayers.Class({
+
+ /**
+ * APIProperty: externalProjection
+ * {<OpenLayers.Projection>} When passed a externalProjection and
+ * internalProjection, the format will reproject the geometries it
+ * reads or writes. The externalProjection is the projection used by
+ * the content which is passed into read or which comes out of write.
+ * In order to reproject, a projection transformation function for the
+ * specified projections must be available. This support may be
+ * provided via proj4js or via a custom transformation function. See
+ * {<OpenLayers.Projection.addTransform>} for more information on
+ * custom transformations.
+ */
+ externalProjection: null,
+
+ /**
+ * APIProperty: internalProjection
+ * {<OpenLayers.Projection>} When passed a externalProjection and
+ * internalProjection, the format will reproject the geometries it
+ * reads or writes. The internalProjection is the projection used by
+ * the geometries which are returned by read or which are passed into
+ * write. In order to reproject, a projection transformation function
+ * for the specified projections must be available. This support may be
+ * provided via proj4js or via a custom transformation function. See
+ * {<OpenLayers.Projection.addTransform>} for more information on
+ * custom transformations.
+ */
+ internalProjection: null,
+
+ /**
+ * Constructor: OpenLayers.Format
+ * Instances of this class are not useful. See one of the subclasses.
+ *
+ * Parameters:
+ * options - {Object} An optional object with properties to set on the
+ * format
+ *
+ * Returns:
+ * An instance of OpenLayers.Format
+ */
+ initialize: function(options) {
+ OpenLayers.Util.extend(this, options);
+ },
+
+ /**
+ * Method: read
+ * Read data from a string, and return an object whose type depends on the
+ * subclass.
+ *
+ * Parameters:
+ * data - {string} Data to read/parse.
+ *
+ * Returns:
+ * Depends on the subclass
+ */
+ read: function(data) {
+ OpenLayers.Console.userError(OpenLayers.i18n("readNotImplemented"));
+ },
+
+ /**
+ * Method: write
+ * Accept an object, and return a string.
+ *
+ * Parameters:
+ * object - {Object} Object to be serialized
+ *
+ * Returns:
+ * {String} A string representation of the object.
+ */
+ write: function(object) {
+ OpenLayers.Console.userError(OpenLayers.i18n("writeNotImplemented"));
+ },
+
+ CLASS_NAME: "OpenLayers.Format"
+});
+/* ======================================================================
OpenLayers/Lang/en.js
====================================================================== */
@@ -6975,7 +11048,7 @@
"To get rid of this message, select a new BaseLayer " +
"in the layer switcher in the upper-right corner.<br><br>" +
"Most likely, this is because the ${layerLib} library " +
- "script was either not correctly included.<br><br>" +
+ "script was not correctly included.<br><br>" +
"Developers: For help getting this working correctly, " +
"<a href='http://trac.openlayers.org/wiki/${layerLib}' " +
"target='_blank'>click here</a>",
@@ -7019,6 +11092,193 @@
'end': ''
};
/* ======================================================================
+ OpenLayers/Popup/Anchored.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/Popup.js
+ */
+
+/**
+ * Class: OpenLayers.Popup.Anchored
+ *
+ * Inherits from:
+ * - <OpenLayers.Popup>
+ */
+OpenLayers.Popup.Anchored =
+ OpenLayers.Class(OpenLayers.Popup, {
+
+ /**
+ * Parameter: relativePosition
+ * {String} Relative position of the popup ("br", "tr", "tl" or "bl").
+ */
+ relativePosition: null,
+
+ /**
+ * Parameter: anchor
+ * {Object} Object to which we'll anchor the popup. Must expose a
+ * 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>).
+ */
+ anchor: null,
+
+ /**
+ * Constructor: OpenLayers.Popup.Anchored
+ *
+ * Parameters:
+ * id - {String}
+ * lonlat - {<OpenLayers.LonLat>}
+ * contentSize - {<OpenLayers.Size>}
+ * contentHTML - {String}
+ * anchor - {Object} Object which must expose a 'size' <OpenLayers.Size>
+ * and 'offset' <OpenLayers.Pixel> (generally an <OpenLayers.Icon>).
+ * closeBox - {Boolean}
+ * closeBoxCallback - {Function} Function to be called on closeBox click.
+ */
+ initialize:function(id, lonlat, contentSize, contentHTML, anchor, closeBox,
+ closeBoxCallback) {
+ var newArguments = [
+ id, lonlat, contentSize, contentHTML, closeBox, closeBoxCallback
+ ];
+ OpenLayers.Popup.prototype.initialize.apply(this, newArguments);
+
+ this.anchor = (anchor != null) ? anchor
+ : { size: new OpenLayers.Size(0,0),
+ offset: new OpenLayers.Pixel(0,0)};
+ },
+
+ /**
+ * APIMethod: destroy
+ */
+ destroy: function() {
+ this.anchor = null;
+ this.relativePosition = null;
+
+ OpenLayers.Popup.prototype.destroy.apply(this, arguments);
+ },
+
+ /**
+ * APIMethod: show
+ * Overridden from Popup since user might hide popup and then show() it
+ * in a new location (meaning we might want to update the relative
+ * position on the show)
+ */
+ show: function() {
+ this.updatePosition();
+ OpenLayers.Popup.prototype.show.apply(this, arguments);
+ },
+
+ /**
+ * Method: moveTo
+ * Since the popup is moving to a new px, it might need also to be moved
+ * relative to where the marker is. We first calculate the new
+ * relativePosition, and then we calculate the new px where we will
+ * put the popup, based on the new relative position.
+ *
+ * If the relativePosition has changed, we must also call
+ * updateRelativePosition() to make any visual changes to the popup
+ * which are associated with putting it in a new relativePosition.
+ *
+ * Parameters:
+ * px - {<OpenLayers.Pixel>}
+ */
+ moveTo: function(px) {
+ var oldRelativePosition = this.relativePosition;
+ this.relativePosition = this.calculateRelativePosition(px);
+
+ var newPx = this.calculateNewPx(px);
+
+ var newArguments = new Array(newPx);
+ OpenLayers.Popup.prototype.moveTo.apply(this, newArguments);
+
+ //if this move has caused the popup to change its relative position,
+ // we need to make the appropriate cosmetic changes.
+ if (this.relativePosition != oldRelativePosition) {
+ this.updateRelativePosition();
+ }
+ },
+
+ /**
+ * APIMethod: setSize
+ *
+ * Parameters:
+ * contentSize - {<OpenLayers.Size>} the new size for the popup's
+ * contents div (in pixels).
+ */
+ setSize:function(contentSize) {
+ OpenLayers.Popup.prototype.setSize.apply(this, arguments);
+
+ if ((this.lonlat) && (this.map)) {
+ var px = this.map.getLayerPxFromLonLat(this.lonlat);
+ this.moveTo(px);
+ }
+ },
+
+ /**
+ * Method: calculateRelativePosition
+ *
+ * Parameters:
+ * px - {<OpenLayers.Pixel>}
+ *
+ * Returns:
+ * {String} The relative position ("br" "tr" "tl" "bl") at which the popup
+ * should be placed.
+ */
+ calculateRelativePosition:function(px) {
+ var lonlat = this.map.getLonLatFromLayerPx(px);
+
+ var extent = this.map.getExtent();
+ var quadrant = extent.determineQuadrant(lonlat);
+
+ return OpenLayers.Bounds.oppositeQuadrant(quadrant);
+ },
+
+ /**
+ * Method: updateRelativePosition
+ * The popup has been moved to a new relative location, so we may want to
+ * make some cosmetic adjustments to it.
+ *
+ * Note that in the classic Anchored popup, there is nothing to do
+ * here, since the popup looks exactly the same in all four positions.
+ * Subclasses such as the AnchoredBubble and Framed, however, will
+ * want to do something special here.
+ */
+ updateRelativePosition: function() {
+ //to be overridden by subclasses
+ },
+
+ /**
+ * Method: calculateNewPx
+ *
+ * Parameters:
+ * px - {<OpenLayers.Pixel>}
+ *
+ * Returns:
+ * {<OpenLayers.Pixel>} The the new px position of the popup on the screen
+ * relative to the passed-in px.
+ */
+ calculateNewPx:function(px) {
+ var newPx = px.offset(this.anchor.offset);
+
+ //use contentSize if size is not already set
+ var size = this.size || this.contentSize;
+
+ var top = (this.relativePosition.charAt(0) == 't');
+ newPx.y += (top) ? -size.h : this.anchor.size.h;
+
+ var left = (this.relativePosition.charAt(1) == 'l');
+ newPx.x += (left) ? -size.w : this.anchor.size.w;
+
+ return newPx;
+ },
+
+ CLASS_NAME: "OpenLayers.Popup.Anchored"
+});
+/* ======================================================================
OpenLayers/Projection.js
====================================================================== */
@@ -7198,6 +11458,1740 @@
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 = rendered && this.drawGeometry(
+ geometry.components[i], style, featureId);
+ }
+ 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 currentNode = OpenLayers.Util.getElement(id);
+
+ // Create a new node, or use the current one if it's
+ // already there.
+ var newNode;
+ if (!currentNode) {
+ var nodeType = this.getNodeType(geometry, style);
+ newNode = this.createNode(nodeType, id);
+ } else {
+ newNode = currentNode;
+ }
+
+ // Set the data for the node, then draw it.
+ newNode._featureId = featureId;
+ newNode._geometry = geometry;
+ newNode._geometryClass = geometry.CLASS_NAME;
+ newNode._style = style;
+
+ var drawResult = this.drawGeometryNode(newNode, geometry, style);
+ if(drawResult === false) {
+ return false;
+ }
+
+ newNode = 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(newNode) : null;
+
+ if(insert) {
+ this.root.insertBefore(newNode, insert);
+ } else {
+ this.root.appendChild(newNode);
+ }
+
+ this.postDraw(newNode);
+
+ 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);
+
+ 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: 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
+ ====================================================================== */
+
+// 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
+//
+// 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.
+
+/**
+ * @requires OpenLayers/Request.js
+ */
+
+(function () {
+
+ // Save reference to earlier defined object implementation (if any)
+ var oXMLHttpRequest = window.XMLHttpRequest;
+
+ // Define on browser type
+ var bGecko = !!window.controllers,
+ bIE = window.document.all && !window.opera;
+
+ // Constructor
+ function cXMLHttpRequest() {
+ this._object = oXMLHttpRequest ? new oXMLHttpRequest : new window.ActiveXObject('Microsoft.XMLHTTP');
+ };
+
+ // BUGFIX: Firefox with Firebug installed would break pages if not executed
+ if (bGecko && oXMLHttpRequest.wrapped)
+ cXMLHttpRequest.wrapped = oXMLHttpRequest.wrapped;
+
+ // Constants
+ cXMLHttpRequest.UNSENT = 0;
+ cXMLHttpRequest.OPENED = 1;
+ cXMLHttpRequest.HEADERS_RECEIVED = 2;
+ cXMLHttpRequest.LOADING = 3;
+ cXMLHttpRequest.DONE = 4;
+
+ // Public Properties
+ cXMLHttpRequest.prototype.readyState = cXMLHttpRequest.UNSENT;
+ cXMLHttpRequest.prototype.responseText = "";
+ cXMLHttpRequest.prototype.responseXML = null;
+ cXMLHttpRequest.prototype.status = 0;
+ cXMLHttpRequest.prototype.statusText = "";
+
+ // Instance-level Events Handlers
+ cXMLHttpRequest.prototype.onreadystatechange = null;
+
+ // Class-level Events Handlers
+ cXMLHttpRequest.onreadystatechange = null;
+ cXMLHttpRequest.onopen = null;
+ cXMLHttpRequest.onsend = null;
+ cXMLHttpRequest.onabort = null;
+
+ // Public Methods
+ cXMLHttpRequest.prototype.open = function(sMethod, sUrl, bAsync, sUser, sPassword) {
+
+ // Save async parameter for fixing Gecko bug with missing readystatechange in synchronous requests
+ this._async = bAsync;
+
+ // Set the onreadystatechange handler
+ var oRequest = this,
+ nState = this.readyState;
+
+ // BUGFIX: IE - memory leak on page unload (inter-page leak)
+ if (bIE) {
+ var fOnUnload = function() {
+ if (oRequest._object.readyState != cXMLHttpRequest.DONE)
+ fCleanTransport(oRequest);
+ };
+ if (bAsync)
+ window.attachEvent("onunload", fOnUnload);
+ }
+
+ this._object.onreadystatechange = function() {
+ if (bGecko && !bAsync)
+ return;
+
+ // Synchronize state
+ oRequest.readyState = oRequest._object.readyState;
+
+ //
+ fSynchronizeValues(oRequest);
+
+ // BUGFIX: Firefox fires unneccesary DONE when aborting
+ if (oRequest._aborted) {
+ // Reset readyState to UNSENT
+ oRequest.readyState = cXMLHttpRequest.UNSENT;
+
+ // Return now
+ return;
+ }
+
+ if (oRequest.readyState == cXMLHttpRequest.DONE) {
+ //
+ fCleanTransport(oRequest);
+// Uncomment this block if you need a fix for IE cache
+/*
+ // BUGFIX: IE - cache issue
+ if (!oRequest._object.getResponseHeader("Date")) {
+ // Save object to cache
+ oRequest._cached = oRequest._object;
+
+ // Instantiate a new transport object
+ cXMLHttpRequest.call(oRequest);
+
+ // Re-send request
+ oRequest._object.open(sMethod, sUrl, bAsync, sUser, sPassword);
+ oRequest._object.setRequestHeader("If-Modified-Since", oRequest._cached.getResponseHeader("Last-Modified") || new window.Date(0));
+ // Copy headers set
+ if (oRequest._headers)
+ for (var sHeader in oRequest._headers)
+ if (typeof oRequest._headers[sHeader] == "string") // Some frameworks prototype objects with functions
+ oRequest._object.setRequestHeader(sHeader, oRequest._headers[sHeader]);
+
+ oRequest._object.onreadystatechange = function() {
+ // Synchronize state
+ oRequest.readyState = oRequest._object.readyState;
+
+ if (oRequest._aborted) {
+ //
+ oRequest.readyState = cXMLHttpRequest.UNSENT;
+
+ // Return
+ return;
+ }
+
+ if (oRequest.readyState == cXMLHttpRequest.DONE) {
+ // Clean Object
+ fCleanTransport(oRequest);
+
+ // get cached request
+ if (oRequest.status == 304)
+ oRequest._object = oRequest._cached;
+
+ //
+ delete oRequest._cached;
+
+ //
+ fSynchronizeValues(oRequest);
+
+ //
+ fReadyStateChange(oRequest);
+
+ // BUGFIX: IE - memory leak in interrupted
+ if (bIE && bAsync)
+ window.detachEvent("onunload", fOnUnload);
+ }
+ };
+ oRequest._object.send(null);
+
+ // Return now - wait untill re-sent request is finished
+ return;
+ };
+*/
+ // BUGFIX: IE - memory leak in interrupted
+ if (bIE && bAsync)
+ window.detachEvent("onunload", fOnUnload);
+ }
+
+ // BUGFIX: Some browsers (Internet Explorer, Gecko) fire OPEN readystate twice
+ if (nState != oRequest.readyState)
+ fReadyStateChange(oRequest);
+
+ nState = oRequest.readyState;
+ };
+
+ // Add method sniffer
+ if (cXMLHttpRequest.onopen)
+ cXMLHttpRequest.onopen.apply(this, arguments);
+
+ this._object.open(sMethod, sUrl, bAsync, sUser, sPassword);
+
+ // BUGFIX: Gecko - missing readystatechange calls in synchronous requests
+ if (!bAsync && bGecko) {
+ this.readyState = cXMLHttpRequest.OPENED;
+
+ fReadyStateChange(this);
+ }
+ };
+ cXMLHttpRequest.prototype.send = function(vData) {
+ // Add method sniffer
+ if (cXMLHttpRequest.onsend)
+ cXMLHttpRequest.onsend.apply(this, arguments);
+
+ // BUGFIX: Safari - fails sending documents created/modified dynamically, so an explicit serialization required
+ // BUGFIX: IE - rewrites any custom mime-type to "text/xml" in case an XMLNode is sent
+ // BUGFIX: Gecko - fails sending Element (this is up to the implementation either to standard)
+ if (vData && vData.nodeType) {
+ vData = window.XMLSerializer ? new window.XMLSerializer().serializeToString(vData) : vData.xml;
+ if (!this._headers["Content-Type"])
+ this._object.setRequestHeader("Content-Type", "application/xml");
+ }
+
+ this._object.send(vData);
+
+ // BUGFIX: Gecko - missing readystatechange calls in synchronous requests
+ if (bGecko && !this._async) {
+ this.readyState = cXMLHttpRequest.OPENED;
+
+ // Synchronize state
+ fSynchronizeValues(this);
+
+ // Simulate missing states
+ while (this.readyState < cXMLHttpRequest.DONE) {
+ this.readyState++;
+ fReadyStateChange(this);
+ // Check if we are aborted
+ if (this._aborted)
+ return;
+ }
+ }
+ };
+ cXMLHttpRequest.prototype.abort = function() {
+ // Add method sniffer
+ if (cXMLHttpRequest.onabort)
+ cXMLHttpRequest.onabort.apply(this, arguments);
+
+ // BUGFIX: Gecko - unneccesary DONE when aborting
+ if (this.readyState > cXMLHttpRequest.UNSENT)
+ this._aborted = true;
+
+ this._object.abort();
+
+ // BUGFIX: IE - memory leak
+ fCleanTransport(this);
+ };
+ cXMLHttpRequest.prototype.getAllResponseHeaders = function() {
+ return this._object.getAllResponseHeaders();
+ };
+ cXMLHttpRequest.prototype.getResponseHeader = function(sName) {
+ return this._object.getResponseHeader(sName);
+ };
+ cXMLHttpRequest.prototype.setRequestHeader = function(sName, sValue) {
+ // BUGFIX: IE - cache issue
+ if (!this._headers)
+ this._headers = {};
+ this._headers[sName] = sValue;
+
+ return this._object.setRequestHeader(sName, sValue);
+ };
+ cXMLHttpRequest.prototype.toString = function() {
+ return '[' + "object" + ' ' + "XMLHttpRequest" + ']';
+ };
+ cXMLHttpRequest.toString = function() {
+ return '[' + "XMLHttpRequest" + ']';
+ };
+
+ // Helper function
+ function fReadyStateChange(oRequest) {
+ // Execute onreadystatechange
+ if (oRequest.onreadystatechange)
+ oRequest.onreadystatechange.apply(oRequest);
+
+ // Sniffing code
+ if (cXMLHttpRequest.onreadystatechange)
+ cXMLHttpRequest.onreadystatechange.apply(oRequest);
+ };
+
+ function fGetDocument(oRequest) {
+ var oDocument = oRequest.responseXML;
+ // Try parsing responseText
+ if (bIE && oDocument && !oDocument.documentElement && oRequest.getResponseHeader("Content-Type").match(/[^\/]+\/[^\+]+\+xml/)) {
+ oDocument = new ActiveXObject('Microsoft.XMLDOM');
+ oDocument.loadXML(oRequest.responseText);
+ }
+ // Check if there is no error in document
+ if (oDocument)
+ if ((bIE && oDocument.parseError != 0) || (oDocument.documentElement && oDocument.documentElement.tagName == "parsererror"))
+ return null;
+ return oDocument;
+ };
+
+ function fSynchronizeValues(oRequest) {
+ try { oRequest.responseText = oRequest._object.responseText; } catch (e) {}
+ try { oRequest.responseXML = fGetDocument(oRequest._object); } catch (e) {}
+ try { oRequest.status = oRequest._object.status; } catch (e) {}
+ try { oRequest.statusText = oRequest._object.statusText; } catch (e) {}
+ };
+
+ function fCleanTransport(oRequest) {
+ // BUGFIX: IE - memory leak (on-page leak)
+ oRequest._object.onreadystatechange = new window.Function;
+
+ // Delete private properties
+ delete oRequest._headers;
+ };
+
+ // Internet Explorer 5.0 (missing apply)
+ if (!window.Function.prototype.apply) {
+ window.Function.prototype.apply = function(oRequest, oArguments) {
+ if (!oArguments)
+ oArguments = [];
+ oRequest.__func = this;
+ oRequest.__func(oArguments[0], oArguments[1], oArguments[2], oArguments[3], oArguments[4]);
+ delete oRequest.__func;
+ };
+ };
+
+ // Register new object with window
+ /**
+ * Class: OpenLayers.Request.XMLHttpRequest
+ * Standard-compliant (W3C) cross-browser implementation of the
+ * XMLHttpRequest object. From
+ * http://code.google.com/p/xmlhttprequest/.
+ */
+ OpenLayers.Request.XMLHttpRequest = cXMLHttpRequest;
+})();
+/* ======================================================================
+ OpenLayers/Strategy/Fixed.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/Strategy.js
+ */
+
+/**
+ * Class: OpenLayers.Strategy.Fixed
+ * A simple strategy that requests features once and never requests new data.
+ *
+ * Inherits from:
+ * - <OpenLayers.Strategy>
+ */
+OpenLayers.Strategy.Fixed = OpenLayers.Class(OpenLayers.Strategy, {
+
+ /**
+ * Constructor: OpenLayers.Strategy.Fixed
+ * Create a new Fixed strategy.
+ *
+ * Parameters:
+ * options - {Object} Optional object whose properties will be set on the
+ * instance.
+ */
+ initialize: function(options) {
+ OpenLayers.Strategy.prototype.initialize.apply(this, [options]);
+ },
+
+ /**
+ * APIMethod: destroy
+ * Clean up the strategy.
+ */
+ destroy: function() {
+ OpenLayers.Strategy.prototype.destroy.apply(this, arguments);
+ },
+
+ /**
+ * Method: activate
+ * Activate the strategy: reads all features from the protocol and add them
+ * to the layer.
+ *
+ * Returns:
+ * {Boolean} True if the strategy was successfully activated or false if
+ * the strategy was already active.
+ */
+ activate: function() {
+ if(OpenLayers.Strategy.prototype.activate.apply(this, arguments)) {
+ this.layer.protocol.read({
+ callback: this.merge,
+ scope: this
+ });
+ return true;
+ }
+ return false;
+ },
+
+ /**
+ * Method: merge
+ * Add all features to the layer.
+ */
+ merge: function(resp) {
+ var features = resp.features;
+ if (features && features.length > 0) {
+ this.layer.addFeatures(features);
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Strategy.Fixed"
+});
+/* ======================================================================
OpenLayers/Tile.js
====================================================================== */
@@ -7282,39 +13276,7 @@
* {Boolean} Is the tile loading?
*/
isLoading: false,
-
- /**
- * Property: isBackBuffer
- * {Boolean} Is this tile a back buffer tile?
- */
- isBackBuffer: false,
-
- /**
- * Property: lastRatio
- * {Float} Used in transition code only. This is the previous ratio
- * of the back buffer tile resolution to the map resolution. Compared
- * with the current ratio to determine if zooming occurred.
- */
- lastRatio: 1,
-
- /**
- * Property: isFirstDraw
- * {Boolean} Is this the first time the tile is being drawn?
- * This is used to force resetBackBuffer to synchronize
- * the backBufferTile with the foreground tile the first time
- * the foreground tile loads so that if the user zooms
- * before the layer has fully loaded, the backBufferTile for
- * tiles that have been loaded can be used.
- */
- isFirstDraw: true,
- /**
- * Property: backBufferTile
- * {<OpenLayers.Tile>} A clone of the tile used to create transition
- * effects when the tile is moved or changes resolution.
- */
- backBufferTile: null,
-
/** TBD 3.0 -- remove 'url' from the list of parameters to the constructor.
* there is no need for the base tile class to have a url.
*
@@ -7360,13 +13322,6 @@
* Nullify references to prevent circular references and memory leaks.
*/
destroy:function() {
- if (OpenLayers.Util.indexOf(this.layer.SUPPORTED_TRANSITIONS,
- this.layer.transitionEffect) != -1) {
- this.layer.events.unregister("loadend", this, this.resetBackBuffer);
- this.events.unregister('loadend', this, this.resetBackBuffer);
- } else {
- this.events.unregister('loadend', this, this.showTile);
- }
this.layer = null;
this.bounds = null;
this.size = null;
@@ -7374,12 +13329,6 @@
this.events.destroy();
this.events = null;
-
- /* clean up the backBufferTile if it exists */
- if (this.backBufferTile) {
- this.backBufferTile.destroy();
- this.backBufferTile = null;
- }
},
/**
@@ -7424,50 +13373,12 @@
// The only case where we *wouldn't* want to draw the tile is if the
// tile is outside its layer's maxExtent.
- var drawTile = (withinMaxExtent || this.layer.displayOutsideMaxExtent);
-
- if (OpenLayers.Util.indexOf(this.layer.SUPPORTED_TRANSITIONS, this.layer.transitionEffect) != -1) {
- if (drawTile) {
- //we use a clone of this tile to create a double buffer for visual
- //continuity. The backBufferTile is used to create transition
- //effects while the tile in the grid is repositioned and redrawn
- if (!this.backBufferTile) {
- this.backBufferTile = this.clone();
- this.backBufferTile.hide();
- // this is important. It allows the backBuffer to place itself
- // appropriately in the DOM. The Image subclass needs to put
- // the backBufferTile behind the main tile so the tiles can
- // load over top and display as soon as they are loaded.
- this.backBufferTile.isBackBuffer = true;
-
- // potentially end any transition effects when the tile loads
- this.events.register('loadend', this, this.resetBackBuffer);
-
- // clear transition back buffer tile only after all tiles in
- // this layer have loaded to avoid visual glitches
- this.layer.events.register("loadend", this, this.resetBackBuffer);
- }
- // run any transition effects
- this.startTransition();
- } else {
- // if we aren't going to draw the tile, then the backBuffer should
- // be hidden too!
- if (this.backBufferTile) {
- this.backBufferTile.clear();
- }
- }
- } else {
- if (drawTile && this.isFirstDraw) {
- this.events.register('loadend', this, this.showTile);
- this.isFirstDraw = false;
- }
- }
- this.shouldDraw = drawTile;
-
+ this.shouldDraw = (withinMaxExtent || this.layer.displayOutsideMaxExtent);
+
//clear tile's contents and mark as not drawn
this.clear();
- return drawTile;
+ return this.shouldDraw;
},
/**
@@ -7538,49 +13449,6 @@
topLeft.lat);
return bounds;
},
-
- /**
- * Method: startTransition
- * Prepare the tile for a transition effect. To be
- * implemented by subclasses.
- */
- startTransition: function() {},
-
- /**
- * Method: resetBackBuffer
- * Triggered by two different events, layer loadend, and tile loadend.
- * In any of these cases, we check to see if we can hide the
- * backBufferTile yet and update its parameters to match the
- * foreground tile.
- *
- * Basic logic:
- * - If the backBufferTile hasn't been drawn yet, reset it
- * - If layer is still loading, show foreground tile but don't hide
- * the backBufferTile yet
- * - If layer is done loading, reset backBuffer tile and show
- * foreground tile
- */
- resetBackBuffer: function() {
- this.showTile();
- if (this.backBufferTile &&
- (this.isFirstDraw || !this.layer.numLoadingTiles)) {
- this.isFirstDraw = false;
- // check to see if the backBufferTile is within the max extents
- // before rendering it
- var maxExtent = this.layer.maxExtent;
- var withinMaxExtent = (maxExtent &&
- this.bounds.intersectsBounds(maxExtent, false));
- if (withinMaxExtent) {
- this.backBufferTile.position = this.position;
- this.backBufferTile.bounds = this.bounds;
- this.backBufferTile.size = this.size;
- this.backBufferTile.imageSize = this.layer.imageSize || this.size;
- this.backBufferTile.imageOffset = this.layer.imageOffset;
- this.backBufferTile.resolution = this.layer.getResolution();
- this.backBufferTile.renderTile();
- }
- }
- },
/**
* Method: showTile
@@ -7607,6 +13475,2440 @@
CLASS_NAME: "OpenLayers.Tile"
});
/* ======================================================================
+ OpenLayers/Ajax.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/Request/XMLHttpRequest.js
+ */
+
+OpenLayers.ProxyHost = "";
+//OpenLayers.ProxyHost = "examples/proxy.cgi?url=";
+
+/**
+ * Ajax reader for OpenLayers
+ *
+ * @uri url to do remote XML http get
+ * @param {String} 'get' format params (x=y&a=b...)
+ * @who object to handle callbacks for this request
+ * @complete the function to be called on success
+ * @failure the function to be called on failure
+ *
+ * example usage from a caller:
+ *
+ * caps: function(request) {
+ * -blah-
+ * },
+ *
+ * OpenLayers.loadURL(url,params,this,caps);
+ *
+ * Notice the above example does not provide an error handler; a default empty
+ * handler is provided which merely logs the error if a failure handler is not
+ * supplied
+ *
+ */
+
+
+/**
+ * Function: OpenLayers.nullHandler
+ * @param {} request
+ */
+OpenLayers.nullHandler = function(request) {
+ OpenLayers.Console.userError(OpenLayers.i18n("unhandledRequest", {'statusText':request.statusText}));
+};
+
+/**
+ * Function: loadURL
+ * Background load a document. For more flexibility in using XMLHttpRequest,
+ * see the <OpenLayers.Request> methods.
+ *
+ * Parameters:
+ * uri - {String} URI of source doc
+ * params - {String} or {Object} GET params. Either a string in the form
+ * "?hello=world&foo=bar" (do not forget the leading question mark)
+ * or an object in the form {'hello': 'world', 'foo': 'bar}
+ * caller - {Object} object which gets callbacks
+ * onComplete - {Function} Optional callback for success. The callback
+ * will be called with this set to caller and will receive the request
+ * object as an argument. Note that if you do not specify an onComplete
+ * function, <OpenLayers.nullHandler> will be called (which pops up a
+ * user friendly error message dialog).
+ * onFailure - {Function} Optional callback for failure. In the event of
+ * a failure, the callback will be called with this set to caller and will
+ * receive the request object as an argument. Note that if you do not
+ * specify an onComplete function, <OpenLayers.nullHandler> will be called
+ * (which pops up a user friendly error message dialog).
+ *
+ * Returns:
+ * {<OpenLayers.Request.XMLHttpRequest>} The request object. To abort loading,
+ * call request.abort().
+ */
+OpenLayers.loadURL = function(uri, params, caller,
+ onComplete, onFailure) {
+
+ if(typeof params == 'string') {
+ params = OpenLayers.Util.getParameters(params);
+ }
+ var success = (onComplete) ? onComplete : OpenLayers.nullHandler;
+ var failure = (onFailure) ? onFailure : OpenLayers.nullHandler;
+
+ return OpenLayers.Request.GET({
+ url: uri, params: params,
+ success: success, failure: failure, scope: caller
+ });
+};
+
+/**
+ * Function: parseXMLString
+ * Parse XML into a doc structure
+ *
+ * Parameters:
+ * text - {String}
+ *
+ * Returns:
+ * {?} Parsed AJAX Responsev
+ */
+OpenLayers.parseXMLString = function(text) {
+
+ //MS sucks, if the server is bad it dies
+ var index = text.indexOf('<');
+ if (index > 0) {
+ text = text.substring(index);
+ }
+
+ var ajaxResponse = OpenLayers.Util.Try(
+ function() {
+ var xmldom = new ActiveXObject('Microsoft.XMLDOM');
+ xmldom.loadXML(text);
+ return xmldom;
+ },
+ function() {
+ return new DOMParser().parseFromString(text, 'text/xml');
+ },
+ function() {
+ var req = new XMLHttpRequest();
+ req.open("GET", "data:" + "text/xml" +
+ ";charset=utf-8," + encodeURIComponent(text), false);
+ if (req.overrideMimeType) {
+ req.overrideMimeType("text/xml");
+ }
+ req.send(null);
+ return req.responseXML;
+ }
+ );
+
+ return ajaxResponse;
+};
+
+
+/**
+ * Namespace: OpenLayers.Ajax
+ */
+OpenLayers.Ajax = {
+
+ /**
+ * Method: emptyFunction
+ */
+ emptyFunction: function () {},
+
+ /**
+ * Method: getTransport
+ *
+ * Returns:
+ * {Object} Transport mechanism for whichever browser we're in, or false if
+ * none available.
+ */
+ getTransport: function() {
+ return OpenLayers.Util.Try(
+ function() {return new XMLHttpRequest();},
+ function() {return new ActiveXObject('Msxml2.XMLHTTP');},
+ function() {return new ActiveXObject('Microsoft.XMLHTTP');}
+ ) || false;
+ },
+
+ /**
+ * Property: activeRequestCount
+ * {Integer}
+ */
+ activeRequestCount: 0
+};
+
+/**
+ * Namespace: OpenLayers.Ajax.Responders
+ * {Object}
+ */
+OpenLayers.Ajax.Responders = {
+
+ /**
+ * Property: responders
+ * {Array}
+ */
+ responders: [],
+
+ /**
+ * Method: register
+ *
+ * Parameters:
+ * responderToAdd - {?}
+ */
+ register: function(responderToAdd) {
+ for (var i = 0; i < this.responders.length; i++){
+ if (responderToAdd == this.responders[i]){
+ return;
+ }
+ }
+ this.responders.push(responderToAdd);
+ },
+
+ /**
+ * Method: unregister
+ *
+ * Parameters:
+ * responderToRemove - {?}
+ */
+ unregister: function(responderToRemove) {
+ OpenLayers.Util.removeItem(this.reponders, responderToRemove);
+ },
+
+ /**
+ * Method: dispatch
+ *
+ * Parameters:
+ * callback - {?}
+ * request - {?}
+ * transport - {?}
+ */
+ dispatch: function(callback, request, transport) {
+ var responder;
+ for (var i = 0; i < this.responders.length; i++) {
+ responder = this.responders[i];
+
+ if (responder[callback] &&
+ typeof responder[callback] == 'function') {
+ try {
+ responder[callback].apply(responder,
+ [request, transport]);
+ } catch (e) {}
+ }
+ }
+ }
+};
+
+OpenLayers.Ajax.Responders.register({
+ /**
+ * Function: onCreate
+ */
+ onCreate: function() {
+ OpenLayers.Ajax.activeRequestCount++;
+ },
+
+ /**
+ * Function: onComplete
+ */
+ onComplete: function() {
+ OpenLayers.Ajax.activeRequestCount--;
+ }
+});
+
+/**
+ * Class: OpenLayers.Ajax.Base
+ */
+OpenLayers.Ajax.Base = OpenLayers.Class({
+
+ /**
+ * Constructor: OpenLayers.Ajax.Base
+ *
+ * Parameters:
+ * options - {Object}
+ */
+ initialize: function(options) {
+ this.options = {
+ method: 'post',
+ asynchronous: true,
+ contentType: 'application/xml',
+ parameters: ''
+ };
+ OpenLayers.Util.extend(this.options, options || {});
+
+ this.options.method = this.options.method.toLowerCase();
+
+ if (typeof this.options.parameters == 'string') {
+ this.options.parameters =
+ OpenLayers.Util.getParameters(this.options.parameters);
+ }
+ }
+});
+
+/**
+ * Class: OpenLayers.Ajax.Request
+ * *Deprecated*. Use <OpenLayers.Request> method instead.
+ *
+ * Inherit:
+ * - <OpenLayers.Ajax.Base>
+ */
+OpenLayers.Ajax.Request = OpenLayers.Class(OpenLayers.Ajax.Base, {
+
+ /**
+ * Property: _complete
+ *
+ * {Boolean}
+ */
+ _complete: false,
+
+ /**
+ * Constructor: OpenLayers.Ajax.Request
+ *
+ * Parameters:
+ * url - {String}
+ * options - {Object}
+ */
+ initialize: function(url, options) {
+ OpenLayers.Ajax.Base.prototype.initialize.apply(this, [options]);
+
+ if (OpenLayers.ProxyHost && OpenLayers.String.startsWith(url, "http")) {
+ url = OpenLayers.ProxyHost + encodeURIComponent(url);
+ }
+
+ this.transport = OpenLayers.Ajax.getTransport();
+ this.request(url);
+ },
+
+ /**
+ * Method: request
+ *
+ * Parameters:
+ * url - {String}
+ */
+ request: function(url) {
+ this.url = url;
+ this.method = this.options.method;
+ var params = OpenLayers.Util.extend({}, this.options.parameters);
+
+ if (this.method != 'get' && this.method != 'post') {
+ // simulate other verbs over post
+ params['_method'] = this.method;
+ this.method = 'post';
+ }
+
+ this.parameters = params;
+
+ if (params = OpenLayers.Util.getParameterString(params)) {
+ // when GET, append parameters to URL
+ if (this.method == 'get') {
+ this.url += ((this.url.indexOf('?') > -1) ? '&' : '?') + params;
+ } else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
+ params += '&_=';
+ }
+ }
+ try {
+ var response = new OpenLayers.Ajax.Response(this);
+ if (this.options.onCreate) {
+ this.options.onCreate(response);
+ }
+
+ OpenLayers.Ajax.Responders.dispatch('onCreate',
+ this,
+ response);
+
+ this.transport.open(this.method.toUpperCase(),
+ this.url,
+ this.options.asynchronous);
+
+ if (this.options.asynchronous) {
+ window.setTimeout(
+ OpenLayers.Function.bind(this.respondToReadyState, this, 1),
+ 10);
+ }
+
+ this.transport.onreadystatechange =
+ OpenLayers.Function.bind(this.onStateChange, this);
+ this.setRequestHeaders();
+
+ this.body = this.method == 'post' ?
+ (this.options.postBody || params) : null;
+ this.transport.send(this.body);
+
+ // Force Firefox to handle ready state 4 for synchronous requests
+ if (!this.options.asynchronous &&
+ this.transport.overrideMimeType) {
+ this.onStateChange();
+ }
+ } catch (e) {
+ this.dispatchException(e);
+ }
+ },
+
+ /**
+ * Method: onStateChange
+ */
+ onStateChange: function() {
+ var readyState = this.transport.readyState;
+ if (readyState > 1 && !((readyState == 4) && this._complete)) {
+ this.respondToReadyState(this.transport.readyState);
+ }
+ },
+
+ /**
+ * Method: setRequestHeaders
+ */
+ setRequestHeaders: function() {
+ var headers = {
+ 'X-Requested-With': 'XMLHttpRequest',
+ 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*',
+ 'OpenLayers': true
+ };
+
+ if (this.method == 'post') {
+ headers['Content-type'] = this.options.contentType +
+ (this.options.encoding ? '; charset=' + this.options.encoding : '');
+
+ /* Force "Connection: close" for older Mozilla browsers to work
+ * around a bug where XMLHttpRequest sends an incorrect
+ * Content-length header. See Mozilla Bugzilla #246651.
+ */
+ if (this.transport.overrideMimeType &&
+ (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005) {
+ headers['Connection'] = 'close';
+ }
+ }
+ // user-defined headers
+ if (typeof this.options.requestHeaders == 'object') {
+ var extras = this.options.requestHeaders;
+
+ if (typeof extras.push == 'function') {
+ for (var i = 0, length = extras.length; i < length; i += 2) {
+ headers[extras[i]] = extras[i+1];
+ }
+ } else {
+ for (var i in extras) {
+ headers[i] = extras[i];
+ }
+ }
+ }
+
+ for (var name in headers) {
+ this.transport.setRequestHeader(name, headers[name]);
+ }
+ },
+
+ /**
+ * Method: success
+ *
+ * Returns:
+ * {Boolean} -
+ */
+ success: function() {
+ var status = this.getStatus();
+ return !status || (status >=200 && status < 300);
+ },
+
+ /**
+ * Method: getStatus
+ *
+ * Returns:
+ * {Integer} - Status
+ */
+ getStatus: function() {
+ try {
+ return this.transport.status || 0;
+ } catch (e) {
+ return 0;
+ }
+ },
+
+ /**
+ * Method: respondToReadyState
+ *
+ * Parameters:
+ * readyState - {?}
+ */
+ respondToReadyState: function(readyState) {
+ var state = OpenLayers.Ajax.Request.Events[readyState];
+ var response = new OpenLayers.Ajax.Response(this);
+
+ if (state == 'Complete') {
+ try {
+ this._complete = true;
+ (this.options['on' + response.status] ||
+ this.options['on' + (this.success() ? 'Success' : 'Failure')] ||
+ OpenLayers.Ajax.emptyFunction)(response);
+ } catch (e) {
+ this.dispatchException(e);
+ }
+
+ var contentType = response.getHeader('Content-type');
+ }
+
+ try {
+ (this.options['on' + state] ||
+ OpenLayers.Ajax.emptyFunction)(response);
+ OpenLayers.Ajax.Responders.dispatch('on' + state,
+ this,
+ response);
+ } catch (e) {
+ this.dispatchException(e);
+ }
+
+ if (state == 'Complete') {
+ // avoid memory leak in MSIE: clean up
+ this.transport.onreadystatechange = OpenLayers.Ajax.emptyFunction;
+ }
+ },
+
+ /**
+ * Method: getHeader
+ *
+ * Parameters:
+ * name - {String} Header name
+ *
+ * Returns:
+ * {?} - response header for the given name
+ */
+ getHeader: function(name) {
+ try {
+ return this.transport.getResponseHeader(name);
+ } catch (e) {
+ return null;
+ }
+ },
+
+ /**
+ * Method: dispatchException
+ * If the optional onException function is set, execute it
+ * and then dispatch the call to any other listener registered
+ * for onException.
+ *
+ * If no optional onException function is set, we suspect that
+ * the user may have also not used
+ * OpenLayers.Ajax.Responders.register to register a listener
+ * for the onException call. To make sure that something
+ * gets done with this exception, only dispatch the call if there
+ * are listeners.
+ *
+ * If you explicitly want to swallow exceptions, set
+ * request.options.onException to an empty function (function(){})
+ * or register an empty function with <OpenLayers.Ajax.Responders>
+ * for onException.
+ *
+ * Parameters:
+ * exception - {?}
+ */
+ dispatchException: function(exception) {
+ var handler = this.options.onException;
+ if(handler) {
+ // call options.onException and alert any other listeners
+ handler(this, exception);
+ OpenLayers.Ajax.Responders.dispatch('onException', this, exception);
+ } else {
+ // check if there are any other listeners
+ var listener = false;
+ var responders = OpenLayers.Ajax.Responders.responders;
+ for (var i = 0; i < responders.length; i++) {
+ if(responders[i].onException) {
+ listener = true;
+ break;
+ }
+ }
+ if(listener) {
+ // call all listeners
+ OpenLayers.Ajax.Responders.dispatch('onException', this, exception);
+ } else {
+ // let the exception through
+ throw exception;
+ }
+ }
+ }
+});
+
+/**
+ * Property: Events
+ * {Array(String)}
+ */
+OpenLayers.Ajax.Request.Events =
+ ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
+
+/**
+ * Class: OpenLayers.Ajax.Response
+ */
+OpenLayers.Ajax.Response = OpenLayers.Class({
+
+ /**
+ * Property: status
+ *
+ * {Integer}
+ */
+ status: 0,
+
+
+ /**
+ * Property: statusText
+ *
+ * {String}
+ */
+ statusText: '',
+
+ /**
+ * Constructor: OpenLayers.Ajax.Response
+ *
+ * Parameters:
+ * request - {Object}
+ */
+ initialize: function(request) {
+ this.request = request;
+ var transport = this.transport = request.transport,
+ readyState = this.readyState = transport.readyState;
+
+ if ((readyState > 2 &&
+ !(!!(window.attachEvent && !window.opera))) ||
+ readyState == 4) {
+ this.status = this.getStatus();
+ this.statusText = this.getStatusText();
+ this.responseText = transport.responseText == null ?
+ '' : String(transport.responseText);
+ }
+
+ if(readyState == 4) {
+ var xml = transport.responseXML;
+ this.responseXML = xml === undefined ? null : xml;
+ }
+ },
+
+ /**
+ * Method: getStatus
+ */
+ getStatus: OpenLayers.Ajax.Request.prototype.getStatus,
+
+ /**
+ * Method: getStatustext
+ *
+ * Returns:
+ * {String} - statusText
+ */
+ getStatusText: function() {
+ try {
+ return this.transport.statusText || '';
+ } catch (e) {
+ return '';
+ }
+ },
+
+ /**
+ * Method: getHeader
+ */
+ getHeader: OpenLayers.Ajax.Request.prototype.getHeader,
+
+ /**
+ * Method: getResponseHeader
+ *
+ * Returns:
+ * {?} - response header for given name
+ */
+ getResponseHeader: function(name) {
+ return this.transport.getResponseHeader(name);
+ }
+});
+
+
+/**
+ * Function: getElementsByTagNameNS
+ *
+ * Parameters:
+ * parentnode - {?}
+ * nsuri - {?}
+ * nsprefix - {?}
+ * tagname - {?}
+ *
+ * Returns:
+ * {?}
+ */
+OpenLayers.Ajax.getElementsByTagNameNS = function(parentnode, nsuri,
+ nsprefix, tagname) {
+ var elem = null;
+ if (parentnode.getElementsByTagNameNS) {
+ elem = parentnode.getElementsByTagNameNS(nsuri, tagname);
+ } else {
+ elem = parentnode.getElementsByTagName(nsprefix + ':' + tagname);
+ }
+ return elem;
+};
+
+
+/**
+ * Function: serializeXMLToString
+ * Wrapper function around XMLSerializer, which doesn't exist/work in
+ * IE/Safari. We need to come up with a way to serialize in those browser:
+ * for now, these browsers will just fail. #535, #536
+ *
+ * Parameters:
+ * xmldom {XMLNode} xml dom to serialize
+ *
+ * Returns:
+ * {?}
+ */
+OpenLayers.Ajax.serializeXMLToString = function(xmldom) {
+ var serializer = new XMLSerializer();
+ var data = serializer.serializeToString(xmldom);
+ return data;
+};
+/* ======================================================================
+ OpenLayers/Control/MouseToolbar.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/Control/MouseDefaults.js
+ */
+
+/**
+ * Class: OpenLayers.Control.MouseToolbar
+ * This class is DEPRECATED in 2.4 and will be removed by 3.0.
+ * If you need this functionality, use Control.NavToolbar instead!!!
+ */
+OpenLayers.Control.MouseToolbar = OpenLayers.Class(
+ OpenLayers.Control.MouseDefaults, {
+
+ /**
+ * Property: mode
+ */
+ mode: null,
+ /**
+ * Property: buttons
+ */
+ buttons: null,
+
+ /**
+ * APIProperty: direction
+ * {String} 'vertical' or 'horizontal'
+ */
+ direction: "vertical",
+
+ /**
+ * Property: buttonClicked
+ * {String}
+ */
+ buttonClicked: null,
+
+ /**
+ * Constructor: OpenLayers.Control.MouseToolbar
+ *
+ * Parameters:
+ * position - {<OpenLayers.Pixel>}
+ * direction - {String}
+ */
+ initialize: function(position, direction) {
+ OpenLayers.Control.prototype.initialize.apply(this, arguments);
+ this.position = new OpenLayers.Pixel(OpenLayers.Control.MouseToolbar.X,
+ OpenLayers.Control.MouseToolbar.Y);
+ if (position) {
+ this.position = position;
+ }
+ if (direction) {
+ this.direction = direction;
+ }
+ this.measureDivs = [];
+ },
+
+ /**
+ * APIMethod: destroy
+ */
+ destroy: function() {
+ for( var btnId in this.buttons) {
+ var btn = this.buttons[btnId];
+ btn.map = null;
+ btn.events.destroy();
+ }
+ OpenLayers.Control.MouseDefaults.prototype.destroy.apply(this,
+ arguments);
+ },
+
+ /**
+ * Method: draw
+ */
+ draw: function() {
+ OpenLayers.Control.prototype.draw.apply(this, arguments);
+ OpenLayers.Control.MouseDefaults.prototype.draw.apply(this, arguments);
+ this.buttons = {};
+ var sz = new OpenLayers.Size(28,28);
+ var centered = new OpenLayers.Pixel(OpenLayers.Control.MouseToolbar.X,0);
+ this._addButton("zoombox", "drag-rectangle-off.png", "drag-rectangle-on.png", centered, sz, "Shift->Drag to zoom to area");
+ centered = centered.add((this.direction == "vertical" ? 0 : sz.w), (this.direction == "vertical" ? sz.h : 0));
+ this._addButton("pan", "panning-hand-off.png", "panning-hand-on.png", centered, sz, "Drag the map to pan.");
+ centered = centered.add((this.direction == "vertical" ? 0 : sz.w), (this.direction == "vertical" ? sz.h : 0));
+ this.switchModeTo("pan");
+
+ return this.div;
+ },
+
+ /**
+ * Method: _addButton
+ */
+ _addButton:function(id, img, activeImg, xy, sz, title) {
+ var imgLocation = OpenLayers.Util.getImagesLocation() + img;
+ var activeImgLocation = OpenLayers.Util.getImagesLocation() + activeImg;
+ // var btn = new ol.AlphaImage("_"+id, imgLocation, xy, sz);
+ var btn = OpenLayers.Util.createAlphaImageDiv(
+ "OpenLayers_Control_MouseToolbar_" + id,
+ xy, sz, imgLocation, "absolute");
+
+ //we want to add the outer div
+ this.div.appendChild(btn);
+ btn.imgLocation = imgLocation;
+ btn.activeImgLocation = activeImgLocation;
+
+ btn.events = new OpenLayers.Events(this, btn, null, true);
+ btn.events.on({
+ "mousedown": this.buttonDown,
+ "mouseup": this.buttonUp,
+ "dblclick": OpenLayers.Event.stop,
+ scope: this
+ });
+ btn.action = id;
+ btn.title = title;
+ btn.alt = title;
+ btn.map = this.map;
+
+ //we want to remember/reference the outer div
+ this.buttons[id] = btn;
+ return btn;
+ },
+
+ /**
+ * Method: buttonDown
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ buttonDown: function(evt) {
+ if (!OpenLayers.Event.isLeftClick(evt)) {
+ return;
+ }
+ this.buttonClicked = evt.element.action;
+ OpenLayers.Event.stop(evt);
+ },
+
+ /**
+ * Method: buttonUp
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ buttonUp: function(evt) {
+ if (!OpenLayers.Event.isLeftClick(evt)) {
+ return;
+ }
+ if (this.buttonClicked != null) {
+ if (this.buttonClicked == evt.element.action) {
+ this.switchModeTo(evt.element.action);
+ }
+ OpenLayers.Event.stop(evt);
+ this.buttonClicked = null;
+ }
+ },
+
+ /**
+ * Method: defaultDblClick
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ defaultDblClick: function (evt) {
+ this.switchModeTo("pan");
+ this.performedDrag = false;
+ var newCenter = this.map.getLonLatFromViewPortPx( evt.xy );
+ this.map.setCenter(newCenter, this.map.zoom + 1);
+ OpenLayers.Event.stop(evt);
+ return false;
+ },
+
+ /**
+ * Method: defaultMouseDown
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ defaultMouseDown: function (evt) {
+ if (!OpenLayers.Event.isLeftClick(evt)) {
+ return;
+ }
+ this.mouseDragStart = evt.xy.clone();
+ this.performedDrag = false;
+ this.startViaKeyboard = false;
+ if (evt.shiftKey && this.mode !="zoombox") {
+ this.switchModeTo("zoombox");
+ this.startViaKeyboard = true;
+ } else if (evt.altKey && this.mode !="measure") {
+ this.switchModeTo("measure");
+ } else if (!this.mode) {
+ this.switchModeTo("pan");
+ }
+
+ switch (this.mode) {
+ case "zoombox":
+ this.map.div.style.cursor = "crosshair";
+ this.zoomBox = OpenLayers.Util.createDiv('zoomBox',
+ this.mouseDragStart,
+ null,
+ null,
+ "absolute",
+ "2px solid red");
+ this.zoomBox.style.backgroundColor = "white";
+ this.zoomBox.style.filter = "alpha(opacity=50)"; // IE
+ this.zoomBox.style.opacity = "0.50";
+ this.zoomBox.style.fontSize = "1px";
+ this.zoomBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
+ this.map.viewPortDiv.appendChild(this.zoomBox);
+ this.performedDrag = true;
+ break;
+ case "measure":
+ var distance = "";
+ if (this.measureStart) {
+ var measureEnd = this.map.getLonLatFromViewPortPx(this.mouseDragStart);
+ distance = OpenLayers.Util.distVincenty(this.measureStart, measureEnd);
+ distance = Math.round(distance * 100) / 100;
+ distance = distance + "km";
+ this.measureStartBox = this.measureBox;
+ }
+ this.measureStart = this.map.getLonLatFromViewPortPx(this.mouseDragStart);;
+ this.measureBox = OpenLayers.Util.createDiv(null,
+ this.mouseDragStart.add(
+ -2-parseInt(this.map.layerContainerDiv.style.left),
+ -2-parseInt(this.map.layerContainerDiv.style.top)),
+ null,
+ null,
+ "absolute");
+ this.measureBox.style.width="4px";
+ this.measureBox.style.height="4px";
+ this.measureBox.style.fontSize = "1px";
+ this.measureBox.style.backgroundColor="red";
+ this.measureBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
+ this.map.layerContainerDiv.appendChild(this.measureBox);
+ if (distance) {
+ this.measureBoxDistance = OpenLayers.Util.createDiv(null,
+ this.mouseDragStart.add(
+ -2-parseInt(this.map.layerContainerDiv.style.left),
+ 2-parseInt(this.map.layerContainerDiv.style.top)),
+ null,
+ null,
+ "absolute");
+
+ this.measureBoxDistance.innerHTML = distance;
+ this.measureBoxDistance.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
+ this.map.layerContainerDiv.appendChild(this.measureBoxDistance);
+ this.measureDivs.push(this.measureBoxDistance);
+ }
+ this.measureBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
+ this.map.layerContainerDiv.appendChild(this.measureBox);
+ this.measureDivs.push(this.measureBox);
+ break;
+ default:
+ this.map.div.style.cursor = "move";
+ break;
+ }
+ document.onselectstart = function() { return false; };
+ OpenLayers.Event.stop(evt);
+ },
+
+ /**
+ * Method: switchModeTo
+ *
+ * Parameters:
+ * mode - {String}
+ */
+ switchModeTo: function(mode) {
+ if (mode != this.mode) {
+
+
+ if (this.mode && this.buttons[this.mode]) {
+ OpenLayers.Util.modifyAlphaImageDiv(this.buttons[this.mode], null, null, null, this.buttons[this.mode].imgLocation);
+ }
+ if (this.mode == "measure" && mode != "measure") {
+ for(var i=0, len=this.measureDivs.length; i<len; i++) {
+ if (this.measureDivs[i]) {
+ this.map.layerContainerDiv.removeChild(this.measureDivs[i]);
+ }
+ }
+ this.measureDivs = [];
+ this.measureStart = null;
+ }
+ this.mode = mode;
+ if (this.buttons[mode]) {
+ OpenLayers.Util.modifyAlphaImageDiv(this.buttons[mode], null, null, null, this.buttons[mode].activeImgLocation);
+ }
+ switch (this.mode) {
+ case "zoombox":
+ this.map.div.style.cursor = "crosshair";
+ break;
+ default:
+ this.map.div.style.cursor = "";
+ break;
+ }
+
+ }
+ },
+
+ /**
+ * Method: leaveMode
+ */
+ leaveMode: function() {
+ this.switchModeTo("pan");
+ },
+
+ /**
+ * Method: defaultMouseMove
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ defaultMouseMove: function (evt) {
+ if (this.mouseDragStart != null) {
+ switch (this.mode) {
+ case "zoombox":
+ var deltaX = Math.abs(this.mouseDragStart.x - evt.xy.x);
+ var deltaY = Math.abs(this.mouseDragStart.y - evt.xy.y);
+ this.zoomBox.style.width = Math.max(1, deltaX) + "px";
+ this.zoomBox.style.height = Math.max(1, deltaY) + "px";
+ if (evt.xy.x < this.mouseDragStart.x) {
+ this.zoomBox.style.left = evt.xy.x+"px";
+ }
+ if (evt.xy.y < this.mouseDragStart.y) {
+ this.zoomBox.style.top = evt.xy.y+"px";
+ }
+ break;
+ default:
+ var deltaX = this.mouseDragStart.x - evt.xy.x;
+ var deltaY = this.mouseDragStart.y - evt.xy.y;
+ var size = this.map.getSize();
+ var newXY = new OpenLayers.Pixel(size.w / 2 + deltaX,
+ size.h / 2 + deltaY);
+ var newCenter = this.map.getLonLatFromViewPortPx( newXY );
+ this.map.setCenter(newCenter, null, true);
+ this.mouseDragStart = evt.xy.clone();
+ }
+ this.performedDrag = true;
+ }
+ },
+
+ /**
+ * Method: defaultMouseUp
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ defaultMouseUp: function (evt) {
+ if (!OpenLayers.Event.isLeftClick(evt)) {
+ return;
+ }
+ switch (this.mode) {
+ case "zoombox":
+ this.zoomBoxEnd(evt);
+ if (this.startViaKeyboard) {
+ this.leaveMode();
+ }
+ break;
+ case "pan":
+ if (this.performedDrag) {
+ this.map.setCenter(this.map.center);
+ }
+ }
+ document.onselectstart = null;
+ this.mouseDragStart = null;
+ this.map.div.style.cursor = "default";
+ },
+
+ /**
+ * Method: defaultMouseOut
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ defaultMouseOut: function (evt) {
+ if (this.mouseDragStart != null
+ && OpenLayers.Util.mouseLeft(evt, this.map.div)) {
+ if (this.zoomBox) {
+ this.removeZoomBox();
+ if (this.startViaKeyboard) {
+ this.leaveMode();
+ }
+ }
+ this.mouseDragStart = null;
+ this.map.div.style.cursor = "default";
+ }
+ },
+
+ /**
+ * Method: defaultClick
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ defaultClick: function (evt) {
+ if (this.performedDrag) {
+ this.performedDrag = false;
+ return false;
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Control.MouseToolbar"
+});
+
+OpenLayers.Control.MouseToolbar.X = 6;
+OpenLayers.Control.MouseToolbar.Y = 300;
+/* ======================================================================
+ OpenLayers/Control/PanZoomBar.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/PanZoom.js
+ */
+
+/**
+ * Class: OpenLayers.Control.PanZoomBar
+ *
+ * Inherits from:
+ * - <OpenLayers.Control.PanZoom>
+ */
+OpenLayers.Control.PanZoomBar = OpenLayers.Class(OpenLayers.Control.PanZoom, {
+
+ /**
+ * APIProperty: zoomStopWidth
+ */
+ zoomStopWidth: 18,
+
+ /**
+ * APIProperty: zoomStopHeight
+ */
+ zoomStopHeight: 11,
+
+ /**
+ * Property: slider
+ */
+ slider: null,
+
+ /**
+ * Property: sliderEvents
+ * {<OpenLayers.Events>}
+ */
+ sliderEvents: null,
+
+ /**
+ * Property: zoomBarDiv
+ * {DOMElement}
+ */
+ zoomBarDiv: null,
+
+ /**
+ * Property: divEvents
+ * {<OpenLayers.Events>}
+ */
+ divEvents: null,
+
+ /**
+ * Property: zoomWorldIcon
+ * {Boolean}
+ */
+ zoomWorldIcon: false,
+
+ /**
+ * Constructor: OpenLayers.Control.PanZoomBar
+ */
+ initialize: function() {
+ OpenLayers.Control.PanZoom.prototype.initialize.apply(this, arguments);
+ },
+
+ /**
+ * APIMethod: destroy
+ */
+ destroy: function() {
+
+ this.div.removeChild(this.slider);
+ this.slider = null;
+
+ this.sliderEvents.destroy();
+ this.sliderEvents = null;
+
+ this.div.removeChild(this.zoombarDiv);
+ this.zoomBarDiv = null;
+
+ this.divEvents.destroy();
+ this.divEvents = null;
+
+ this.map.events.un({
+ "zoomend": this.moveZoomBar,
+ "changebaselayer": this.redraw,
+ scope: this
+ });
+
+ OpenLayers.Control.PanZoom.prototype.destroy.apply(this, arguments);
+ },
+
+ /**
+ * Method: setMap
+ *
+ * Parameters:
+ * map - {<OpenLayers.Map>}
+ */
+ setMap: function(map) {
+ OpenLayers.Control.PanZoom.prototype.setMap.apply(this, arguments);
+ this.map.events.register("changebaselayer", this, this.redraw);
+ },
+
+ /**
+ * Method: redraw
+ * clear the div and start over.
+ */
+ redraw: function() {
+ if (this.div != null) {
+ this.div.innerHTML = "";
+ }
+ this.draw();
+ },
+
+ /**
+ * Method: draw
+ *
+ * Parameters:
+ * px - {<OpenLayers.Pixel>}
+ */
+ draw: function(px) {
+ // initialize our internal div
+ OpenLayers.Control.prototype.draw.apply(this, arguments);
+ px = this.position.clone();
+
+ // place the controls
+ this.buttons = [];
+
+ var sz = new OpenLayers.Size(18,18);
+ var centered = new OpenLayers.Pixel(px.x+sz.w/2, px.y);
+ var wposition = sz.w;
+
+ if (this.zoomWorldIcon) {
+ centered = new OpenLayers.Pixel(px.x+sz.w, px.y);
+ }
+
+ this._addButton("panup", "north-mini.png", centered, sz);
+ px.y = centered.y+sz.h;
+ this._addButton("panleft", "west-mini.png", px, sz);
+ if (this.zoomWorldIcon) {
+ this._addButton("zoomworld", "zoom-world-mini.png", px.add(sz.w, 0), sz);
+
+ wposition *= 2;
+ }
+ this._addButton("panright", "east-mini.png", px.add(wposition, 0), sz);
+ this._addButton("pandown", "south-mini.png", centered.add(0, sz.h*2), sz);
+ this._addButton("zoomin", "zoom-plus-mini.png", centered.add(0, sz.h*3+5), sz);
+ centered = this._addZoomBar(centered.add(0, sz.h*4 + 5));
+ this._addButton("zoomout", "zoom-minus-mini.png", centered, sz);
+ return this.div;
+ },
+
+ /**
+ * Method: _addZoomBar
+ *
+ * Parameters:
+ * location - {<OpenLayers.Pixel>} where zoombar drawing is to start.
+ */
+ _addZoomBar:function(centered) {
+ var imgLocation = OpenLayers.Util.getImagesLocation();
+
+ var id = this.id + "_" + this.map.id;
+ var zoomsToEnd = this.map.getNumZoomLevels() - 1 - this.map.getZoom();
+ var slider = OpenLayers.Util.createAlphaImageDiv(id,
+ centered.add(-1, zoomsToEnd * this.zoomStopHeight),
+ new OpenLayers.Size(20,9),
+ imgLocation+"slider.png",
+ "absolute");
+ this.slider = slider;
+
+ this.sliderEvents = new OpenLayers.Events(this, slider, null, true,
+ {includeXY: true});
+ this.sliderEvents.on({
+ "mousedown": this.zoomBarDown,
+ "mousemove": this.zoomBarDrag,
+ "mouseup": this.zoomBarUp,
+ "dblclick": this.doubleClick,
+ "click": this.doubleClick
+ });
+
+ var sz = new OpenLayers.Size();
+ sz.h = this.zoomStopHeight * this.map.getNumZoomLevels();
+ sz.w = this.zoomStopWidth;
+ var div = null;
+
+ if (OpenLayers.Util.alphaHack()) {
+ var id = this.id + "_" + this.map.id;
+ div = OpenLayers.Util.createAlphaImageDiv(id, centered,
+ new OpenLayers.Size(sz.w,
+ this.zoomStopHeight),
+ imgLocation + "zoombar.png",
+ "absolute", null, "crop");
+ div.style.height = sz.h + "px";
+ } else {
+ div = OpenLayers.Util.createDiv(
+ 'OpenLayers_Control_PanZoomBar_Zoombar' + this.map.id,
+ centered,
+ sz,
+ imgLocation+"zoombar.png");
+ }
+
+ this.zoombarDiv = div;
+
+ this.divEvents = new OpenLayers.Events(this, div, null, true,
+ {includeXY: true});
+ this.divEvents.on({
+ "mousedown": this.divClick,
+ "mousemove": this.passEventToSlider,
+ "dblclick": this.doubleClick,
+ "click": this.doubleClick
+ });
+
+ this.div.appendChild(div);
+
+ this.startTop = parseInt(div.style.top);
+ this.div.appendChild(slider);
+
+ this.map.events.register("zoomend", this, this.moveZoomBar);
+
+ centered = centered.add(0,
+ this.zoomStopHeight * this.map.getNumZoomLevels());
+ return centered;
+ },
+
+ /*
+ * Method: passEventToSlider
+ * This function is used to pass events that happen on the div, or the map,
+ * through to the slider, which then does its moving thing.
+ *
+ * Parameters:
+ * evt - {<OpenLayers.Event>}
+ */
+ passEventToSlider:function(evt) {
+ this.sliderEvents.handleBrowserEvent(evt);
+ },
+
+ /*
+ * Method: divClick
+ * Picks up on clicks directly on the zoombar div
+ * and sets the zoom level appropriately.
+ */
+ divClick: function (evt) {
+ if (!OpenLayers.Event.isLeftClick(evt)) {
+ return;
+ }
+ var y = evt.xy.y;
+ var top = OpenLayers.Util.pagePosition(evt.object)[1];
+ var levels = (y - top)/this.zoomStopHeight;
+ if(!this.map.fractionalZoom) {
+ levels = Math.floor(levels);
+ }
+ var zoom = (this.map.getNumZoomLevels() - 1) - levels;
+ zoom = Math.min(Math.max(zoom, 0), this.map.getNumZoomLevels() - 1);
+ this.map.zoomTo(zoom);
+ OpenLayers.Event.stop(evt);
+ },
+
+ /*
+ * Method: zoomBarDown
+ * event listener for clicks on the slider
+ *
+ * Parameters:
+ * evt - {<OpenLayers.Event>}
+ */
+ zoomBarDown:function(evt) {
+ if (!OpenLayers.Event.isLeftClick(evt)) {
+ return;
+ }
+ this.map.events.on({
+ "mousemove": this.passEventToSlider,
+ "mouseup": this.passEventToSlider,
+ scope: this
+ });
+ this.mouseDragStart = evt.xy.clone();
+ this.zoomStart = evt.xy.clone();
+ this.div.style.cursor = "move";
+ // reset the div offsets just in case the div moved
+ this.zoombarDiv.offsets = null;
+ OpenLayers.Event.stop(evt);
+ },
+
+ /*
+ * Method: zoomBarDrag
+ * This is what happens when a click has occurred, and the client is
+ * dragging. Here we must ensure that the slider doesn't go beyond the
+ * bottom/top of the zoombar div, as well as moving the slider to its new
+ * visual location
+ *
+ * Parameters:
+ * evt - {<OpenLayers.Event>}
+ */
+ zoomBarDrag:function(evt) {
+ if (this.mouseDragStart != null) {
+ var deltaY = this.mouseDragStart.y - evt.xy.y;
+ var offsets = OpenLayers.Util.pagePosition(this.zoombarDiv);
+ if ((evt.clientY - offsets[1]) > 0 &&
+ (evt.clientY - offsets[1]) < parseInt(this.zoombarDiv.style.height) - 2) {
+ var newTop = parseInt(this.slider.style.top) - deltaY;
+ this.slider.style.top = newTop+"px";
+ }
+ this.mouseDragStart = evt.xy.clone();
+ OpenLayers.Event.stop(evt);
+ }
+ },
+
+ /*
+ * Method: zoomBarUp
+ * Perform cleanup when a mouseup event is received -- discover new zoom
+ * level and switch to it.
+ *
+ * Parameters:
+ * evt - {<OpenLayers.Event>}
+ */
+ zoomBarUp:function(evt) {
+ if (!OpenLayers.Event.isLeftClick(evt)) {
+ return;
+ }
+ if (this.zoomStart) {
+ this.div.style.cursor="";
+ this.map.events.un({
+ "mouseup": this.passEventToSlider,
+ "mousemove": this.passEventToSlider,
+ scope: this
+ });
+ var deltaY = this.zoomStart.y - evt.xy.y;
+ var zoomLevel = this.map.zoom;
+ if (this.map.fractionalZoom) {
+ zoomLevel += deltaY/this.zoomStopHeight;
+ zoomLevel = Math.min(Math.max(zoomLevel, 0),
+ this.map.getNumZoomLevels() - 1);
+ } else {
+ zoomLevel += Math.round(deltaY/this.zoomStopHeight);
+ }
+ this.map.zoomTo(zoomLevel);
+ this.moveZoomBar();
+ this.mouseDragStart = null;
+ OpenLayers.Event.stop(evt);
+ }
+ },
+
+ /*
+ * Method: moveZoomBar
+ * Change the location of the slider to match the current zoom level.
+ */
+ moveZoomBar:function() {
+ var newTop =
+ ((this.map.getNumZoomLevels()-1) - this.map.getZoom()) *
+ this.zoomStopHeight + this.startTop + 1;
+ this.slider.style.top = newTop + "px";
+ },
+
+ CLASS_NAME: "OpenLayers.Control.PanZoomBar"
+});
+/* ======================================================================
+ OpenLayers/Control/Permalink.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/Control/ArgParser.js
+ */
+
+/**
+ * Class: OpenLayers.Control.Permalink
+ *
+ * Inherits from:
+ * - <OpenLayers.Control>
+ */
+OpenLayers.Control.Permalink = OpenLayers.Class(OpenLayers.Control, {
+
+ /**
+ * APIProperty: argParserClass
+ * {Class} The ArgParser control class (not instance) to use with this
+ * control.
+ */
+ argParserClass: OpenLayers.Control.ArgParser,
+
+ /**
+ * Property: element
+ * {DOMElement}
+ */
+ element: null,
+
+ /**
+ * APIProperty: base
+ * {String}
+ */
+ base: '',
+
+ /**
+ * APIProperty: displayProjection
+ * {<OpenLayers.Projection>} Requires proj4js support. Projection used
+ * when creating the coordinates in the link. This will reproject the
+ * map coordinates into display coordinates. If you are using this
+ * functionality, the permalink which is last added to the map will
+ * determine the coordinate type which is read from the URL, which
+ * means you should not add permalinks with different
+ * displayProjections to the same map.
+ */
+ displayProjection: null,
+
+ /**
+ * Constructor: OpenLayers.Control.Permalink
+ *
+ * Parameters:
+ * element - {DOMElement}
+ * base - {String}
+ * options - {Object} options to the control.
+ */
+ initialize: function(element, base, options) {
+ OpenLayers.Control.prototype.initialize.apply(this, [options]);
+ this.element = OpenLayers.Util.getElement(element);
+ this.base = base || document.location.href;
+ },
+
+ /**
+ * APIMethod: destroy
+ */
+ destroy: function() {
+ if (this.element.parentNode == this.div) {
+ this.div.removeChild(this.element);
+ }
+ this.element = null;
+
+ this.map.events.unregister('moveend', this, this.updateLink);
+
+ OpenLayers.Control.prototype.destroy.apply(this, arguments);
+ },
+
+ /**
+ * Method: setMap
+ * Set the map property for the control.
+ *
+ * Parameters:
+ * map - {<OpenLayers.Map>}
+ */
+ setMap: function(map) {
+ OpenLayers.Control.prototype.setMap.apply(this, arguments);
+
+ //make sure we have an arg parser attached
+ for(var i=0, len=this.map.controls.length; i<len; i++) {
+ var control = this.map.controls[i];
+ if (control.CLASS_NAME == this.argParserClass.CLASS_NAME) {
+
+ // If a permalink is added to the map, and an ArgParser already
+ // exists, we override the displayProjection to be the one
+ // on the permalink.
+ if (control.displayProjection != this.displayProjection) {
+ this.displayProjection = control.displayProjection;
+ }
+
+ break;
+ }
+ }
+ if (i == this.map.controls.length) {
+ this.map.addControl(new this.argParserClass(
+ { 'displayProjection': this.displayProjection }));
+ }
+
+ },
+
+ /**
+ * Method: draw
+ *
+ * Returns:
+ * {DOMElement}
+ */
+ draw: function() {
+ OpenLayers.Control.prototype.draw.apply(this, arguments);
+
+ if (!this.element) {
+ this.div.className = this.displayClass;
+ this.element = document.createElement("a");
+ this.element.innerHTML = OpenLayers.i18n("permalink");
+ this.element.href="";
+ this.div.appendChild(this.element);
+ }
+ this.map.events.on({
+ 'moveend': this.updateLink,
+ 'changelayer': this.updateLink,
+ 'changebaselayer': this.updateLink,
+ scope: this
+ });
+
+ // Make it so there is at least a link even though the map may not have
+ // moved yet.
+ this.updateLink();
+
+ return this.div;
+ },
+
+ /**
+ * Method: updateLink
+ */
+ updateLink: function() {
+ var href = this.base;
+ if (href.indexOf('?') != -1) {
+ href = href.substring( 0, href.indexOf('?') );
+ }
+
+ href += '?' + OpenLayers.Util.getParameterString(this.createParams());
+ this.element.href = href;
+ },
+
+ /**
+ * APIMethod: createParams
+ * Creates the parameters that need to be encoded into the permalink url.
+ *
+ * Parameters:
+ * center - {<OpenLayers.LonLat>} center to encode in the permalink.
+ * Defaults to the current map center.
+ * zoom - {Integer} zoom level to encode in the permalink. Defaults to the
+ * current map zoom level.
+ * layers - {Array(<OpenLayers.Layer>)} layers to encode in the permalink.
+ * Defaults to the current map layers.
+ *
+ * Returns:
+ * {Object} Hash of parameters that will be url-encoded into the
+ * permalink.
+ */
+ createParams: function(center, zoom, layers) {
+ center = center || this.map.getCenter();
+ zoom = zoom || this.map.getZoom();
+ layers = layers || this.map.layers;
+
+ var params = OpenLayers.Util.getParameters(this.base);
+
+ // If there's still no center, map is not initialized yet.
+ // Break out of this function, and simply return the params from the
+ // base link.
+ if (center) {
+
+ params.zoom = this.map.getZoom();
+ var lat = center.lat;
+ var lon = center.lon;
+
+ if (this.displayProjection) {
+ var mapPosition = OpenLayers.Projection.transform(
+ { x: lon, y: lat },
+ this.map.getProjectionObject(),
+ this.displayProjection );
+ lon = mapPosition.x;
+ lat = mapPosition.y;
+ }
+ params.lat = Math.round(lat*100000)/100000;
+ params.lon = Math.round(lon*100000)/100000;
+
+ params.layers = '';
+ for (var i=0, len=this.map.layers.length; i<len; i++) {
+ var layer = this.map.layers[i];
+
+ if (layer.isBaseLayer) {
+ params.layers += (layer == this.map.baseLayer) ? "B" : "0";
+ } else {
+ params.layers += (layer.getVisibility()) ? "T" : "F";
+ }
+ }
+ }
+
+ return params;
+ },
+
+ CLASS_NAME: "OpenLayers.Control.Permalink"
+});
+/* ======================================================================
+ OpenLayers/Format/JSON.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. */
+
+/**
+ * Note:
+ * This work draws heavily from the public domain JSON serializer/deserializer
+ * at http://www.json.org/json.js. Rewritten so that it doesn't modify
+ * basic data prototypes.
+ */
+
+/**
+ * @requires OpenLayers/Format.js
+ */
+
+/**
+ * Class: OpenLayers.Format.JSON
+ * A parser to read/write JSON safely. Create a new instance with the
+ * <OpenLayers.Format.JSON> constructor.
+ *
+ * Inherits from:
+ * - <OpenLayers.Format>
+ */
+OpenLayers.Format.JSON = OpenLayers.Class(OpenLayers.Format, {
+
+ /**
+ * APIProperty: indent
+ * {String} For "pretty" printing, the indent string will be used once for
+ * each indentation level.
+ */
+ indent: " ",
+
+ /**
+ * APIProperty: space
+ * {String} For "pretty" printing, the space string will be used after
+ * the ":" separating a name/value pair.
+ */
+ space: " ",
+
+ /**
+ * APIProperty: newline
+ * {String} For "pretty" printing, the newline string will be used at the
+ * end of each name/value pair or array item.
+ */
+ newline: "\n",
+
+ /**
+ * Property: level
+ * {Integer} For "pretty" printing, this is incremented/decremented during
+ * serialization.
+ */
+ level: 0,
+
+ /**
+ * Property: pretty
+ * {Boolean} Serialize with extra whitespace for structure. This is set
+ * by the <write> method.
+ */
+ pretty: false,
+
+ /**
+ * Constructor: OpenLayers.Format.JSON
+ * Create a new parser for JSON.
+ *
+ * Parameters:
+ * options - {Object} An optional object whose properties will be set on
+ * this instance.
+ */
+ initialize: function(options) {
+ OpenLayers.Format.prototype.initialize.apply(this, [options]);
+ },
+
+ /**
+ * APIMethod: read
+ * Deserialize a json string.
+ *
+ * Parameters:
+ * json - {String} A JSON string
+ * filter - {Function} A function which will be called for every key and
+ * value at every level of the final result. Each value will be
+ * replaced by the result of the filter function. This can be used to
+ * reform generic objects into instances of classes, or to transform
+ * date strings into Date objects.
+ *
+ * Returns:
+ * {Object} An object, array, string, or number .
+ */
+ read: function(json, filter) {
+ /**
+ * Parsing happens in three stages. In the first stage, we run the text
+ * against a regular expression which looks for non-JSON
+ * characters. We are especially concerned with '()' and 'new'
+ * because they can cause invocation, and '=' because it can cause
+ * mutation. But just to be safe, we will reject all unexpected
+ * characters.
+ */
+ try {
+ if (/^[\],:{}\s]*$/.test(json.replace(/\\["\\\/bfnrtu]/g, '@').
+ replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
+ replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
+
+ /**
+ * In the second stage we use the eval function to compile the
+ * text into a JavaScript structure. The '{' operator is
+ * subject to a syntactic ambiguity in JavaScript - it can
+ * begin a block or an object literal. We wrap the text in
+ * parens to eliminate the ambiguity.
+ */
+ var object = eval('(' + json + ')');
+
+ /**
+ * In the optional third stage, we recursively walk the new
+ * structure, passing each name/value pair to a filter
+ * function for possible transformation.
+ */
+ if(typeof filter === 'function') {
+ function walk(k, v) {
+ if(v && typeof v === 'object') {
+ for(var i in v) {
+ if(v.hasOwnProperty(i)) {
+ v[i] = walk(i, v[i]);
+ }
+ }
+ }
+ return filter(k, v);
+ }
+ object = walk('', object);
+ }
+ return object;
+ }
+ } catch(e) {
+ // Fall through if the regexp test fails.
+ }
+ return null;
+ },
+
+ /**
+ * APIMethod: write
+ * Serialize an object into a JSON string.
+ *
+ * Parameters:
+ * value - {String} The object, array, string, number, boolean or date
+ * to be serialized.
+ * pretty - {Boolean} Structure the output with newlines and indentation.
+ * Default is false.
+ *
+ * Returns:
+ * {String} The JSON string representation of the input value.
+ */
+ write: function(value, pretty) {
+ this.pretty = !!pretty;
+ var json = null;
+ var type = typeof value;
+ if(this.serialize[type]) {
+ json = this.serialize[type].apply(this, [value]);
+ }
+ return json;
+ },
+
+ /**
+ * Method: writeIndent
+ * Output an indentation string depending on the indentation level.
+ *
+ * Returns:
+ * {String} An appropriate indentation string.
+ */
+ writeIndent: function() {
+ var pieces = [];
+ if(this.pretty) {
+ for(var i=0; i<this.level; ++i) {
+ pieces.push(this.indent);
+ }
+ }
+ return pieces.join('');
+ },
+
+ /**
+ * Method: writeNewline
+ * Output a string representing a newline if in pretty printing mode.
+ *
+ * Returns:
+ * {String} A string representing a new line.
+ */
+ writeNewline: function() {
+ return (this.pretty) ? this.newline : '';
+ },
+
+ /**
+ * Method: writeSpace
+ * Output a string representing a space if in pretty printing mode.
+ *
+ * Returns:
+ * {String} A space.
+ */
+ writeSpace: function() {
+ return (this.pretty) ? this.space : '';
+ },
+
+ /**
+ * Property: serialize
+ * Object with properties corresponding to the serializable data types.
+ * Property values are functions that do the actual serializing.
+ */
+ serialize: {
+ /**
+ * Method: serialize.object
+ * Transform an object into a JSON string.
+ *
+ * Parameters:
+ * object - {Object} The object to be serialized.
+ *
+ * Returns:
+ * {String} A JSON string representing the object.
+ */
+ 'object': function(object) {
+ // three special objects that we want to treat differently
+ if(object == null) {
+ return "null";
+ }
+ if(object.constructor == Date) {
+ return this.serialize.date.apply(this, [object]);
+ }
+ if(object.constructor == Array) {
+ return this.serialize.array.apply(this, [object]);
+ }
+ var pieces = ['{'];
+ this.level += 1;
+ var key, keyJSON, valueJSON;
+
+ var addComma = false;
+ for(key in object) {
+ if(object.hasOwnProperty(key)) {
+ // recursive calls need to allow for sub-classing
+ keyJSON = OpenLayers.Format.JSON.prototype.write.apply(this,
+ [key, this.pretty]);
+ valueJSON = OpenLayers.Format.JSON.prototype.write.apply(this,
+ [object[key], this.pretty]);
+ if(keyJSON != null && valueJSON != null) {
+ if(addComma) {
+ pieces.push(',');
+ }
+ pieces.push(this.writeNewline(), this.writeIndent(),
+ keyJSON, ':', this.writeSpace(), valueJSON);
+ addComma = true;
+ }
+ }
+ }
+
+ this.level -= 1;
+ pieces.push(this.writeNewline(), this.writeIndent(), '}');
+ return pieces.join('');
+ },
+
+ /**
+ * Method: serialize.array
+ * Transform an array into a JSON string.
+ *
+ * Parameters:
+ * array - {Array} The array to be serialized
+ *
+ * Returns:
+ * {String} A JSON string representing the array.
+ */
+ 'array': function(array) {
+ var json;
+ var pieces = ['['];
+ this.level += 1;
+
+ for(var i=0, len=array.length; i<len; ++i) {
+ // recursive calls need to allow for sub-classing
+ json = OpenLayers.Format.JSON.prototype.write.apply(this,
+ [array[i], this.pretty]);
+ if(json != null) {
+ if(i > 0) {
+ pieces.push(',');
+ }
+ pieces.push(this.writeNewline(), this.writeIndent(), json);
+ }
+ }
+
+ this.level -= 1;
+ pieces.push(this.writeNewline(), this.writeIndent(), ']');
+ return pieces.join('');
+ },
+
+ /**
+ * Method: serialize.string
+ * Transform a string into a JSON string.
+ *
+ * Parameters:
+ * string - {String} The string to be serialized
+ *
+ * Returns:
+ * {String} A JSON string representing the string.
+ */
+ 'string': function(string) {
+ // If the string contains no control characters, no quote characters, and no
+ // backslash characters, then we can simply slap some quotes around it.
+ // Otherwise we must also replace the offending characters with safe
+ // sequences.
+ var m = {
+ '\b': '\\b',
+ '\t': '\\t',
+ '\n': '\\n',
+ '\f': '\\f',
+ '\r': '\\r',
+ '"' : '\\"',
+ '\\': '\\\\'
+ };
+ if(/["\\\x00-\x1f]/.test(string)) {
+ return '"' + string.replace(/([\x00-\x1f\\"])/g, function(a, b) {
+ var c = m[b];
+ if(c) {
+ return c;
+ }
+ c = b.charCodeAt();
+ return '\\u00' +
+ Math.floor(c / 16).toString(16) +
+ (c % 16).toString(16);
+ }) + '"';
+ }
+ return '"' + string + '"';
+ },
+
+ /**
+ * Method: serialize.number
+ * Transform a number into a JSON string.
+ *
+ * Parameters:
+ * number - {Number} The number to be serialized.
+ *
+ * Returns:
+ * {String} A JSON string representing the number.
+ */
+ 'number': function(number) {
+ return isFinite(number) ? String(number) : "null";
+ },
+
+ /**
+ * Method: serialize.boolean
+ * Transform a boolean into a JSON string.
+ *
+ * Parameters:
+ * bool - {Boolean} The boolean to be serialized.
+ *
+ * Returns:
+ * {String} A JSON string representing the boolean.
+ */
+ 'boolean': function(bool) {
+ return String(bool);
+ },
+
+ /**
+ * Method: serialize.object
+ * Transform a date into a JSON string.
+ *
+ * Parameters:
+ * date - {Date} The date to be serialized.
+ *
+ * Returns:
+ * {String} A JSON string representing the date.
+ */
+ 'date': function(date) {
+ function format(number) {
+ // Format integers to have at least two digits.
+ return (number < 10) ? '0' + number : number;
+ }
+ return '"' + date.getFullYear() + '-' +
+ format(date.getMonth() + 1) + '-' +
+ format(date.getDate()) + 'T' +
+ format(date.getHours()) + ':' +
+ format(date.getMinutes()) + ':' +
+ format(date.getSeconds()) + '"';
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Format.JSON"
+
+});
+/* ======================================================================
+ OpenLayers/Format/XML.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/Format.js
+ */
+
+/**
+ * Class: OpenLayers.Format.XML
+ * Read and write XML. For cross-browser XML generation, use methods on an
+ * instance of the XML format class instead of on <code>document<end>.
+ * The DOM creation and traversing methods exposed here all mimic the
+ * W3C XML DOM methods. Create a new parser with the
+ * <OpenLayers.Format.XML> constructor.
+ *
+ * Inherits from:
+ * - <OpenLayers.Format>
+ */
+OpenLayers.Format.XML = OpenLayers.Class(OpenLayers.Format, {
+
+ /**
+ * Property: xmldom
+ * {XMLDom} If this browser uses ActiveX, this will be set to a XMLDOM
+ * object. It is not intended to be a browser sniffing property.
+ * Instead, the xmldom property is used instead of <code>document<end>
+ * where namespaced node creation methods are not supported. In all
+ * other browsers, this remains null.
+ */
+ xmldom: null,
+
+ /**
+ * Constructor: OpenLayers.Format.XML
+ * Construct an XML parser. The parser is used to read and write XML.
+ * Reading XML from a string returns a DOM element. Writing XML from
+ * a DOM element returns a string.
+ *
+ * Parameters:
+ * options - {Object} Optional object whose properties will be set on
+ * the object.
+ */
+ initialize: function(options) {
+ if(window.ActiveXObject) {
+ this.xmldom = new ActiveXObject("Microsoft.XMLDOM");
+ }
+ OpenLayers.Format.prototype.initialize.apply(this, [options]);
+ },
+
+ /**
+ * APIMethod: read
+ * Deserialize a XML string and return a DOM node.
+ *
+ * Parameters:
+ * text - {String} A XML string
+
+ * Returns:
+ * {DOMElement} A DOM node
+ */
+ read: function(text) {
+ var index = text.indexOf('<');
+ if(index > 0) {
+ text = text.substring(index);
+ }
+ var node = OpenLayers.Util.Try(
+ OpenLayers.Function.bind((
+ function() {
+ var xmldom;
+ /**
+ * Since we want to be able to call this method on the prototype
+ * itself, this.xmldom may not exist even if in IE.
+ */
+ if(window.ActiveXObject && !this.xmldom) {
+ xmldom = new ActiveXObject("Microsoft.XMLDOM");
+ } else {
+ xmldom = this.xmldom;
+
+ }
+ xmldom.loadXML(text);
+ return xmldom;
+ }
+ ), this),
+ function() {
+ return new DOMParser().parseFromString(text, 'text/xml');
+ },
+ function() {
+ var req = new XMLHttpRequest();
+ req.open("GET", "data:" + "text/xml" +
+ ";charset=utf-8," + encodeURIComponent(text), false);
+ if(req.overrideMimeType) {
+ req.overrideMimeType("text/xml");
+ }
+ req.send(null);
+ return req.responseXML;
+ }
+ );
+ return node;
+ },
+
+ /**
+ * APIMethod: write
+ * Serialize a DOM node into a XML string.
+ *
+ * Parameters:
+ * node - {DOMElement} A DOM node.
+ *
+ * Returns:
+ * {String} The XML string representation of the input node.
+ */
+ write: function(node) {
+ var data;
+ if(this.xmldom) {
+ data = node.xml;
+ } else {
+ var serializer = new XMLSerializer();
+ if (node.nodeType == 1) {
+ // Add nodes to a document before serializing. Everything else
+ // is serialized as is. This may need more work. See #1218 .
+ var doc = document.implementation.createDocument("", "", null);
+ if (doc.importNode) {
+ node = doc.importNode(node, true);
+ }
+ doc.appendChild(node);
+ data = serializer.serializeToString(doc);
+ } else {
+ data = serializer.serializeToString(node);
+ }
+ }
+ return data;
+ },
+
+ /**
+ * APIMethod: createElementNS
+ * Create a new element with namespace. This node can be appended to
+ * another node with the standard node.appendChild method. For
+ * cross-browser support, this method must be used instead of
+ * document.createElementNS.
+ *
+ * Parameters:
+ * uri - {String} Namespace URI for the element.
+ * name - {String} The qualified name of the element (prefix:localname).
+ *
+ * Returns:
+ * {Element} A DOM element with namespace.
+ */
+ createElementNS: function(uri, name) {
+ var element;
+ if(this.xmldom) {
+ if(typeof uri == "string") {
+ element = this.xmldom.createNode(1, name, uri);
+ } else {
+ element = this.xmldom.createNode(1, name, "");
+ }
+ } else {
+ element = document.createElementNS(uri, name);
+ }
+ return element;
+ },
+
+ /**
+ * APIMethod: createTextNode
+ * Create a text node. This node can be appended to another node with
+ * the standard node.appendChild method. For cross-browser support,
+ * this method must be used instead of document.createTextNode.
+ *
+ * Parameters:
+ * text - {String} The text of the node.
+ *
+ * Returns:
+ * {DOMElement} A DOM text node.
+ */
+ createTextNode: function(text) {
+ var node;
+ if(this.xmldom) {
+ node = this.xmldom.createTextNode(text);
+ } else {
+ node = document.createTextNode(text);
+ }
+ return node;
+ },
+
+ /**
+ * APIMethod: getElementsByTagNameNS
+ * Get a list of elements on a node given the namespace URI and local name.
+ * To return all nodes in a given namespace, use '*' for the name
+ * argument. To return all nodes of a given (local) name, regardless
+ * of namespace, use '*' for the uri argument.
+ *
+ * Parameters:
+ * node - {Element} Node on which to search for other nodes.
+ * uri - {String} Namespace URI.
+ * name - {String} Local name of the tag (without the prefix).
+ *
+ * Returns:
+ * {NodeList} A node list or array of elements.
+ */
+ getElementsByTagNameNS: function(node, uri, name) {
+ var elements = [];
+ if(node.getElementsByTagNameNS) {
+ elements = node.getElementsByTagNameNS(uri, name);
+ } else {
+ // brute force method
+ var allNodes = node.getElementsByTagName("*");
+ var potentialNode, fullName;
+ for(var i=0, len=allNodes.length; i<len; ++i) {
+ potentialNode = allNodes[i];
+ fullName = (potentialNode.prefix) ?
+ (potentialNode.prefix + ":" + name) : name;
+ if((name == "*") || (fullName == potentialNode.nodeName)) {
+ if((uri == "*") || (uri == potentialNode.namespaceURI)) {
+ elements.push(potentialNode);
+ }
+ }
+ }
+ }
+ return elements;
+ },
+
+ /**
+ * APIMethod: getAttributeNodeNS
+ * Get an attribute node given the namespace URI and local name.
+ *
+ * Parameters:
+ * node - {Element} Node on which to search for attribute nodes.
+ * uri - {String} Namespace URI.
+ * name - {String} Local name of the attribute (without the prefix).
+ *
+ * Returns:
+ * {DOMElement} An attribute node or null if none found.
+ */
+ getAttributeNodeNS: function(node, uri, name) {
+ var attributeNode = null;
+ if(node.getAttributeNodeNS) {
+ attributeNode = node.getAttributeNodeNS(uri, name);
+ } else {
+ var attributes = node.attributes;
+ var potentialNode, fullName;
+ for(var i=0, len=attributes.length; i<len; ++i) {
+ potentialNode = attributes[i];
+ if(potentialNode.namespaceURI == uri) {
+ fullName = (potentialNode.prefix) ?
+ (potentialNode.prefix + ":" + name) : name;
+ if(fullName == potentialNode.nodeName) {
+ attributeNode = potentialNode;
+ break;
+ }
+ }
+ }
+ }
+ return attributeNode;
+ },
+
+ /**
+ * APIMethod: getAttributeNS
+ * Get an attribute value given the namespace URI and local name.
+ *
+ * Parameters:
+ * node - {Element} Node on which to search for an attribute.
+ * uri - {String} Namespace URI.
+ * name - {String} Local name of the attribute (without the prefix).
+ *
+ * Returns:
+ * {String} An attribute value or and empty string if none found.
+ */
+ getAttributeNS: function(node, uri, name) {
+ var attributeValue = "";
+ if(node.getAttributeNS) {
+ attributeValue = node.getAttributeNS(uri, name) || "";
+ } else {
+ var attributeNode = this.getAttributeNodeNS(node, uri, name);
+ if(attributeNode) {
+ attributeValue = attributeNode.nodeValue;
+ }
+ }
+ return attributeValue;
+ },
+
+ /**
+ * APIMethod: getChildValue
+ * Get the value of the first child node if it exists, or return an
+ * optional default string. Returns an empty string if no first child
+ * exists and no default value is supplied.
+ *
+ * Parameters:
+ * node - {DOMElement} The element used to look for a first child value.
+ * def - {String} Optional string to return in the event that no
+ * first child value exists.
+ *
+ * Returns:
+ * {String} The value of the first child of the given node.
+ */
+ getChildValue: function(node, def) {
+ var value;
+ if (node && node.firstChild && node.firstChild.nodeValue) {
+ value = node.firstChild.nodeValue;
+ } else {
+ value = (def != undefined) ? def : "";
+ }
+ return value;
+ },
+
+ /**
+ * APIMethod: concatChildValues
+ * Concatenate the value of all child nodes if any exist, or return an
+ * optional default string. Returns an empty string if no children
+ * exist and no default value is supplied. Not optimized for large
+ * numbers of child nodes.
+ *
+ * Parameters:
+ * node - {DOMElement} The element used to look for child values.
+ * def - {String} Optional string to return in the event that no
+ * child exist.
+ *
+ * Returns:
+ * {String} The concatenated value of all child nodes of the given node.
+ */
+ concatChildValues: function(node, def) {
+ var value = "";
+ var child = node.firstChild;
+ var childValue;
+ while(child) {
+ childValue = child.nodeValue;
+ if(childValue) {
+ value += childValue;
+ }
+ child = child.nextSibling;
+ }
+ if(value == "" && def != undefined) {
+ value = def;
+ }
+ return value;
+ },
+
+ /**
+ * APIMethod: hasAttributeNS
+ * Determine whether a node has a particular attribute matching the given
+ * name and namespace.
+ *
+ * Parameters:
+ * node - {Element} Node on which to search for an attribute.
+ * uri - {String} Namespace URI.
+ * name - {String} Local name of the attribute (without the prefix).
+ *
+ * Returns:
+ * {Boolean} The node has an attribute matching the name and namespace.
+ */
+ hasAttributeNS: function(node, uri, name) {
+ var found = false;
+ if(node.hasAttributeNS) {
+ found = node.hasAttributeNS(uri, name);
+ } else {
+ found = !!this.getAttributeNodeNS(node, uri, name);
+ }
+ return found;
+ },
+
+ /**
+ * APIMethod: setAttributeNS
+ * Adds a new attribute or changes the value of an attribute with the given
+ * namespace and name.
+ *
+ * Parameters:
+ * node - {Element} Element node on which to set the attribute.
+ * uri - {String} Namespace URI for the attribute.
+ * name - {String} Qualified name (prefix:localname) for the attribute.
+ * value - {String} Attribute value.
+ */
+ setAttributeNS: function(node, uri, name, value) {
+ if(node.setAttributeNS) {
+ node.setAttributeNS(uri, name, value);
+ } else {
+ if(this.xmldom) {
+ if(uri) {
+ var attribute = node.ownerDocument.createNode(
+ 2, name, uri
+ );
+ attribute.nodeValue = value;
+ node.setAttributeNode(attribute);
+ } else {
+ node.setAttribute(name, value);
+ }
+ } else {
+ throw "setAttributeNS not implemented";
+ }
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Format.XML"
+
+});
+/* ======================================================================
OpenLayers/Handler.js
====================================================================== */
@@ -7768,7 +16070,7 @@
}
// register for event handlers defined on this class.
var events = OpenLayers.Events.prototype.BROWSER_EVENTS;
- for (var i = 0; i < events.length; i++) {
+ for (var i=0, len=events.length; i<len; i++) {
if (this[events[i]]) {
this.register(events[i], this[events[i]]);
}
@@ -7790,7 +16092,7 @@
}
// unregister event handlers defined on this class.
var events = OpenLayers.Events.prototype.BROWSER_EVENTS;
- for (var i = 0; i < events.length; i++) {
+ for (var i=0, len=events.length; i<len; i++) {
if (this[events[i]]) {
this.unregister(events[i], this[events[i]]);
}
@@ -7924,7 +16226,13 @@
* Constant: Z_INDEX_BASE
* {Object} Base z-indexes for different classes of thing
*/
- Z_INDEX_BASE: { BaseLayer: 100, Overlay: 325, Popup: 750, Control: 1000 },
+ Z_INDEX_BASE: {
+ BaseLayer: 100,
+ Overlay: 325,
+ Feature: 725,
+ Popup: 750,
+ Control: 1000
+ },
/**
* Constant: EVENT_TYPES
@@ -7964,6 +16272,7 @@
* - *movestart* triggered after the start of a drag, pan, or zoom
* - *move* triggered after each drag, pan, or zoom
* - *moveend* triggered after a drag, pan, or zoom completes
+ * - *zoomend* triggered after a zoom completes
* - *popupopen* triggered after a popup opens
* - *popupclose* triggered after a popup opens
* - *addmarker* triggered after a marker has been added
@@ -8097,6 +16406,13 @@
zoom: 0,
/**
+ * Property: panRatio
+ * {Float} The ratio of the current extent within
+ * which panning will tween.
+ */
+ panRatio: 1.5,
+
+ /**
* Property: viewRequestID
* {String} Used to store a unique identifier that changes when the map
* view changes. viewRequestID should be used when adding data
@@ -8304,6 +16620,7 @@
this.id = OpenLayers.Util.createUniqueID("OpenLayers.Map_");
this.div = OpenLayers.Util.getElement(div);
+ OpenLayers.Element.addClass(this.div, 'olMap');
// the viewPortDiv is the outermost div we modify
var id = this.div.id + "_OpenLayers_ViewPort";
@@ -8325,7 +16642,8 @@
this.events = new OpenLayers.Events(this,
this.div,
this.EVENT_TYPES,
- this.fallThrough);
+ this.fallThrough,
+ {includeXY: true});
this.updateSize();
if(this.eventListeners instanceof Object) {
this.events.on(this.eventListeners);
@@ -8354,7 +16672,7 @@
// check existing links for equivalent url
var addNode = true;
var nodes = document.getElementsByTagName('link');
- for(var i=0; i<nodes.length; ++i) {
+ for(var i=0, len=nodes.length; i<len; ++i) {
if(OpenLayers.Util.isEquivalentUrl(nodes.item(i).href,
this.theme)) {
addNode = false;
@@ -8386,7 +16704,7 @@
}
}
- for(var i=0; i < this.controls.length; i++) {
+ for(var i=0, len=this.controls.length; i<len; i++) {
this.addControlToMap(this.controls[i]);
}
@@ -8639,7 +16957,7 @@
*/
getLayer: function(id) {
var foundLayer = null;
- for (var i = 0; i < this.layers.length; i++) {
+ for (var i=0, len=this.layers.length; i<len; i++) {
var layer = this.layers[i];
if (layer.id == id) {
foundLayer = layer;
@@ -8667,7 +16985,7 @@
* Reset each layer's z-index based on layer's array index
*/
resetLayersZIndex: function() {
- for (var i = 0; i < this.layers.length; i++) {
+ for (var i=0, len=this.layers.length; i<len; i++) {
var layer = this.layers[i];
this.setLayerZIndex(layer, i);
}
@@ -8680,7 +16998,7 @@
* layer - {<OpenLayers.Layer>}
*/
addLayer: function (layer) {
- for(var i=0; i < this.layers.length; i++) {
+ for(var i=0, len=this.layers.length; i <len; i++) {
if (this.layers[i] == layer) {
var msg = OpenLayers.i18n('layerAlreadyAdded',
{'layerName':layer.name});
@@ -8724,7 +17042,7 @@
* layers - {Array(<OpenLayers.Layer>)}
*/
addLayers: function (layers) {
- for (var i = 0; i < layers.length; i++) {
+ for (var i=0, len=layers.length; i<len; i++) {
this.addLayer(layers[i]);
}
},
@@ -8775,7 +17093,7 @@
if(this.baseLayer == layer) {
this.baseLayer = null;
if(setNewBaseLayer) {
- for(var i=0; i < this.layers.length; i++) {
+ for(var i=0, len=this.layers.length; i<len; i++) {
var iLayer = this.layers[i];
if (iLayer.isBaseLayer) {
this.setBaseLayer(iLayer);
@@ -8836,7 +17154,7 @@
if (base != idx) {
this.layers.splice(base, 1);
this.layers.splice(idx, 0, layer);
- for (var i = 0; i < this.layers.length; i++) {
+ for (var i=0, len=this.layers.length; i<len; i++) {
this.setLayerZIndex(this.layers[i], i);
}
this.events.triggerEvent("changelayer", {
@@ -8854,7 +17172,7 @@
*
* Paremeters:
* layer - {<OpenLayers.Layer>}
- * idx - {int}
+ * delta - {int}
*/
raiseLayer: function (layer, delta) {
var idx = this.getLayerIndex(layer) + delta;
@@ -8986,7 +17304,7 @@
*/
getControl: function (id) {
var returnControl = null;
- for(var i=0; i < this.controls.length; i++) {
+ for(var i=0, len=this.controls.length; i<len; i++) {
var control = this.controls[i];
if (control.id == id) {
returnControl = control;
@@ -9101,7 +17419,7 @@
*/
updateSize: function() {
// the div might have moved on the page, also
- this.events.element.offsets = null;
+ this.events.clearMouseCache();
var newSize = this.getCurrentSize();
var oldSize = this.getSize();
if (oldSize == null) {
@@ -9113,7 +17431,7 @@
this.size = newSize;
//notify layers of mapresize
- for(var i=0; i < this.layers.length; i++) {
+ for(var i=0, len=this.layers.length; i<len; i++) {
this.layers[i].onMapResize();
}
@@ -9235,11 +17553,7 @@
* false.
*/
pan: function(dx, dy, options) {
- // this should be pushed to applyDefaults and extend
- if (!options) {
- options = {};
- }
- OpenLayers.Util.applyDefaults(options, {
+ options = OpenLayers.Util.applyDefaults(options, {
animate: true,
dragging: false
});
@@ -9270,11 +17584,18 @@
* lonlat - {<OpenLayers.Lonlat>}
*/
panTo: function(lonlat) {
- if (this.panMethod && this.getExtent().containsLonLat(lonlat)) {
+ if (this.panMethod && this.getExtent().scale(this.panRatio).containsLonLat(lonlat)) {
if (!this.panTween) {
this.panTween = new OpenLayers.Tween(this.panMethod);
}
var center = this.getCenter();
+
+ // center will not change, don't do nothing
+ if (lonlat.lon == center.lon &&
+ lonlat.lat == center.lat) {
+ return;
+ }
+
var from = {
lon: center.lon,
lat: center.lat
@@ -9436,7 +17757,7 @@
bounds = this.baseLayer.getExtent();
- for (var i = 0; i < this.layers.length; i++) {
+ for (var i=0, len=this.layers.length; i<len; i++) {
var layer = this.layers[i];
if (!layer.isBaseLayer) {
var inRange = layer.calculateInRange();
@@ -9455,13 +17776,16 @@
}
if (inRange && layer.visibility) {
layer.moveTo(bounds, zoomChanged, dragging);
+ layer.events.triggerEvent("moveend",
+ {"zoomChanged": zoomChanged}
+ );
}
}
}
if (zoomChanged) {
//redraw popups
- for (var i = 0; i < this.popups.length; i++) {
+ for (var i=0, len=this.popups.length; i<len; i++) {
this.popups[i].updatePosition();
}
}
@@ -9591,13 +17915,25 @@
/**
* APIMethod: getMaxExtent
+ *
+ * Parameters:
+ * options - {Object}
*
+ * Allowed Options:
+ * restricted - {Boolean} If true, returns restricted extent (if it is
+ * available.)
+ *
* Returns:
- * {<OpenLayers.Bounds>}
+ * {<OpenLayers.Bounds>} The maxExtent property as set on the current
+ * baselayer, unless the 'restricted' option is set, in which case
+ * the 'restrictedExtent' option from the map is returned (if it
+ * is set).
*/
- getMaxExtent: function () {
+ getMaxExtent: function (options) {
var maxExtent = null;
- if (this.baseLayer != null) {
+ if(options && options.restricted && this.restrictedExtent){
+ maxExtent = this.restrictedExtent;
+ } else if (this.baseLayer != null) {
maxExtent = this.baseLayer.maxExtent;
}
return maxExtent;
@@ -9660,6 +17996,21 @@
return resolution;
},
+ /**
+ * APIMethod: getUnits
+ *
+ * Returns:
+ * {Float} The current units of the map.
+ * If no baselayer is set, returns null.
+ */
+ getUnits: function () {
+ var units = null;
+ if (this.baseLayer != null) {
+ units = this.baseLayer.units;
+ }
+ return units;
+ },
+
/**
* APIMethod: getScale
*
@@ -9792,8 +18143,13 @@
*
* Parameters:
* bounds - {<OpenLayers.Bounds>}
+ * closest - {Boolean} Find the zoom level that most closely fits the
+ * specified bounds. Note that this may result in a zoom that does
+ * not exactly contain the entire extent.
+ * Default is false.
+ *
*/
- zoomToExtent: function(bounds) {
+ zoomToExtent: function(bounds, closest) {
var center = bounds.getCenterLonLat();
if (this.baseLayer.wrapDateLine) {
var maxExtent = this.getMaxExtent();
@@ -9817,15 +18173,28 @@
//
center = bounds.getCenterLonLat().wrapDateLine(maxExtent);
}
- this.setCenter(center, this.getZoomForExtent(bounds));
+ this.setCenter(center, this.getZoomForExtent(bounds, closest));
},
/**
* APIMethod: zoomToMaxExtent
* Zoom to the full extent and recenter.
+ *
+ * Parameters:
+ * options -
+ *
+ * Allowed Options:
+ * restricted - {Boolean} True to zoom to restricted extent if it is
+ * set. Defaults to true.
*/
- zoomToMaxExtent: function() {
- this.zoomToExtent(this.getMaxExtent());
+ zoomToMaxExtent: function(options) {
+ //restricted is true by default
+ var restricted = (options) ? options.restricted : true;
+
+ var maxExtent = this.getMaxExtent({
+ 'restricted': restricted
+ });
+ this.zoomToExtent(maxExtent);
},
/**
@@ -9834,8 +18203,13 @@
*
* Parameters:
* scale - {float}
+ * closest - {Boolean} Find the zoom level that most closely fits the
+ * specified scale. Note that this may result in a zoom that does
+ * not exactly contain the entire extent.
+ * Default is false.
+ *
*/
- zoomToScale: function(scale) {
+ zoomToScale: function(scale, closest) {
var res = OpenLayers.Util.getResolutionFromScale(scale,
this.baseLayer.units);
var size = this.getSize();
@@ -9847,7 +18221,7 @@
center.lat - h_deg / 2,
center.lon + w_deg / 2,
center.lat + h_deg / 2);
- this.zoomToExtent(extent);
+ this.zoomToExtent(extent, closest);
},
/********************************************************/
@@ -10108,9 +18482,9 @@
/**
* Constructor: OpenLayers.Marker
- * Paraemeters:
+ * Parameters:
+ * lonlat - {<OpenLayers.LonLat>} the position of this marker
* icon - {<OpenLayers.Icon>} the icon for this marker
- * lonlat - {<OpenLayers.LonLat>} the position of this marker
*/
initialize: function(lonlat, icon) {
this.lonlat = lonlat;
@@ -10262,6 +18636,2223 @@
/* ======================================================================
+ OpenLayers/Popup/AnchoredBubble.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/Popup/Anchored.js
+ */
+
+/**
+ * Class: OpenLayers.Popup.AnchoredBubble
+ *
+ * Inherits from:
+ * - <OpenLayers.Popup.Anchored>
+ */
+OpenLayers.Popup.AnchoredBubble =
+ OpenLayers.Class(OpenLayers.Popup.Anchored, {
+
+ /**
+ * Property: rounded
+ * {Boolean} Has the popup been rounded yet?
+ */
+ rounded: false,
+
+ /**
+ * Constructor: OpenLayers.Popup.AnchoredBubble
+ *
+ * Parameters:
+ * id - {String}
+ * lonlat - {<OpenLayers.LonLat>}
+ * contentSize - {<OpenLayers.Size>}
+ * contentHTML - {String}
+ * anchor - {Object} Object to which we'll anchor the popup. Must expose
+ * a 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>)
+ * (Note that this is generally an <OpenLayers.Icon>).
+ * closeBox - {Boolean}
+ * closeBoxCallback - {Function} Function to be called on closeBox click.
+ */
+ initialize:function(id, lonlat, contentSize, contentHTML, anchor, closeBox,
+ closeBoxCallback) {
+
+ this.padding = new OpenLayers.Bounds(
+ 0, OpenLayers.Popup.AnchoredBubble.CORNER_SIZE,
+ 0, OpenLayers.Popup.AnchoredBubble.CORNER_SIZE
+ );
+ OpenLayers.Popup.Anchored.prototype.initialize.apply(this, arguments);
+ },
+
+ /**
+ * Method: draw
+ *
+ * Parameters:
+ * px - {<OpenLayers.Pixel>}
+ *
+ * Returns:
+ * {DOMElement} Reference to a div that contains the drawn popup.
+ */
+ draw: function(px) {
+
+ OpenLayers.Popup.Anchored.prototype.draw.apply(this, arguments);
+
+ this.setContentHTML();
+
+ //set the popup color and opacity
+ this.setBackgroundColor();
+ this.setOpacity();
+
+ return this.div;
+ },
+
+ /**
+ * Method: updateRelativePosition
+ * The popup has been moved to a new relative location, in which case
+ * we will want to re-do the rico corners.
+ */
+ updateRelativePosition: function() {
+ this.setRicoCorners();
+ },
+
+ /**
+ * APIMethod: setSize
+ *
+ * Parameters:
+ * contentSize - {<OpenLayers.Size>} the new size for the popup's
+ * contents div (in pixels).
+ */
+ setSize:function(contentSize) {
+ OpenLayers.Popup.Anchored.prototype.setSize.apply(this, arguments);
+
+ this.setRicoCorners();
+ },
+
+ /**
+ * APIMethod: setBackgroundColor
+ *
+ * Parameters:
+ * color - {String}
+ */
+ setBackgroundColor:function(color) {
+ if (color != undefined) {
+ this.backgroundColor = color;
+ }
+
+ if (this.div != null) {
+ if (this.contentDiv != null) {
+ this.div.style.background = "transparent";
+ OpenLayers.Rico.Corner.changeColor(this.groupDiv,
+ this.backgroundColor);
+ }
+ }
+ },
+
+ /**
+ * APIMethod: setOpacity
+ *
+ * Parameters:
+ * opacity - {float}
+ */
+ setOpacity:function(opacity) {
+ OpenLayers.Popup.Anchored.prototype.setOpacity.call(this, opacity);
+
+ if (this.div != null) {
+ if (this.groupDiv != null) {
+ OpenLayers.Rico.Corner.changeOpacity(this.groupDiv,
+ this.opacity);
+ }
+ }
+ },
+
+ /**
+ * Method: setBorder
+ * Always sets border to 0. Bubble Popups can not have a border.
+ *
+ * Parameters:
+ * border - {Integer}
+ */
+ setBorder:function(border) {
+ this.border = 0;
+ },
+
+ /**
+ * Method: setRicoCorners
+ * Update RICO corners according to the popup's current relative postion.
+ */
+ setRicoCorners:function() {
+
+ var corners = this.getCornersToRound(this.relativePosition);
+ var options = {corners: corners,
+ color: this.backgroundColor,
+ bgColor: "transparent",
+ blend: false};
+
+ if (!this.rounded) {
+ OpenLayers.Rico.Corner.round(this.div, options);
+ this.rounded = true;
+ } else {
+ OpenLayers.Rico.Corner.reRound(this.groupDiv, options);
+ //set the popup color and opacity
+ this.setBackgroundColor();
+ this.setOpacity();
+ }
+ },
+
+ /**
+ * Method: getCornersToRound
+ *
+ * Returns:
+ * {String} The proper corners string ("tr tl bl br") for rico to round.
+ */
+ getCornersToRound:function() {
+
+ var corners = ['tl', 'tr', 'bl', 'br'];
+
+ //we want to round all the corners _except_ the opposite one.
+ var corner = OpenLayers.Bounds.oppositeQuadrant(this.relativePosition);
+ OpenLayers.Util.removeItem(corners, corner);
+
+ return corners.join(" ");
+ },
+
+ CLASS_NAME: "OpenLayers.Popup.AnchoredBubble"
+});
+
+/**
+ * Constant: CORNER_SIZE
+ * {Integer} 5. Border space for the RICO corners.
+ */
+OpenLayers.Popup.AnchoredBubble.CORNER_SIZE = 5;
+
+/* ======================================================================
+ OpenLayers/Popup/Framed.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/Popup/Anchored.js
+ */
+
+/**
+ * Class: OpenLayers.Popup.Framed
+ *
+ * Inherits from:
+ * - <OpenLayers.Popup.Anchored>
+ */
+OpenLayers.Popup.Framed =
+ OpenLayers.Class(OpenLayers.Popup.Anchored, {
+
+ /**
+ * Property: imageSrc
+ * {String} location of the image to be used as the popup frame
+ */
+ imageSrc: null,
+
+ /**
+ * Property: imageSize
+ * {<OpenLayers.Size>} Size (measured in pixels) of the image located
+ * by the 'imageSrc' property.
+ */
+ imageSize: null,
+
+ /**
+ * APIProperty: isAlphaImage
+ * {Boolean} The image has some alpha and thus needs to use the alpha
+ * image hack. Note that setting this to true will have no noticeable
+ * effect in FF or IE7 browsers, but will all but crush the ie6
+ * browser.
+ * Default is false.
+ */
+ isAlphaImage: false,
+
+ /**
+ * Property: positionBlocks
+ * {Object} Hash of different position blocks (Object/Hashs). Each block
+ * will be keyed by a two-character 'relativePosition'
+ * code string (ie "tl", "tr", "bl", "br"). Block properties are
+ * 'offset', 'padding' (self-explanatory), and finally the 'blocks'
+ * parameter, which is an array of the block objects.
+ *
+ * Each block object must have 'size', 'anchor', and 'position'
+ * properties.
+ *
+ * Note that positionBlocks should never be modified at runtime.
+ */
+ positionBlocks: null,
+
+ /**
+ * Property: blocks
+ * {Array[Object]} Array of objects, each of which is one "block" of the
+ * popup. Each block has a 'div' and an 'image' property, both of
+ * which are DOMElements, and the latter of which is appended to the
+ * former. These are reused as the popup goes changing positions for
+ * great economy and elegance.
+ */
+ blocks: null,
+
+ /**
+ * APIProperty: fixedRelativePosition
+ * {Boolean} We want the framed popup to work dynamically placed relative
+ * to its anchor but also in just one fixed position. A well designed
+ * framed popup will have the pixels and logic to display itself in
+ * any of the four relative positions, but (understandably), this will
+ * not be the case for all of them. By setting this property to 'true',
+ * framed popup will not recalculate for the best placement each time
+ * it's open, but will always open the same way.
+ * Note that if this is set to true, it is generally advisable to also
+ * set the 'panIntoView' property to true so that the popup can be
+ * scrolled into view (since it will often be offscreen on open)
+ * Default is false.
+ */
+ fixedRelativePosition: false,
+
+ /**
+ * Constructor: OpenLayers.Popup.Framed
+ *
+ * Parameters:
+ * id - {String}
+ * lonlat - {<OpenLayers.LonLat>}
+ * contentSize - {<OpenLayers.Size>}
+ * contentHTML - {String}
+ * anchor - {Object} Object to which we'll anchor the popup. Must expose
+ * a 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>)
+ * (Note that this is generally an <OpenLayers.Icon>).
+ * closeBox - {Boolean}
+ * closeBoxCallback - {Function} Function to be called on closeBox click.
+ */
+ initialize:function(id, lonlat, contentSize, contentHTML, anchor, closeBox,
+ closeBoxCallback) {
+
+ OpenLayers.Popup.Anchored.prototype.initialize.apply(this, arguments);
+
+ if (this.fixedRelativePosition) {
+ //based on our decided relativePostion, set the current padding
+ // this keeps us from getting into trouble
+ this.updateRelativePosition();
+
+ //make calculateRelativePosition always returnt the specified
+ // fiexed position.
+ this.calculateRelativePosition = function(px) {
+ return this.relativePosition;
+ };
+ }
+
+ this.contentDiv.style.position = "absolute";
+ this.contentDiv.style.zIndex = 1;
+
+ if (closeBox) {
+ this.closeDiv.style.zIndex = 1;
+ }
+
+ this.groupDiv.style.position = "absolute";
+ this.groupDiv.style.top = "0px";
+ this.groupDiv.style.left = "0px";
+ this.groupDiv.style.height = "100%";
+ this.groupDiv.style.width = "100%";
+ },
+
+ /**
+ * APIMethod: destroy
+ */
+ destroy: function() {
+ this.imageSrc = null;
+ this.imageSize = null;
+ this.isAlphaImage = null;
+
+ this.fixedRelativePosition = false;
+ this.positionBlocks = null;
+
+ //remove our blocks
+ for(var i = 0; i < this.blocks.length; i++) {
+ var block = this.blocks[i];
+
+ if (block.image) {
+ block.div.removeChild(block.image);
+ }
+ block.image = null;
+
+ if (block.div) {
+ this.groupDiv.removeChild(block.div);
+ }
+ block.div = null;
+ }
+ this.blocks = null;
+
+ OpenLayers.Popup.Anchored.prototype.destroy.apply(this, arguments);
+ },
+
+ /**
+ * APIMethod: setBackgroundColor
+ */
+ setBackgroundColor:function(color) {
+ //does nothing since the framed popup's entire scheme is based on a
+ // an image -- changing the background color makes no sense.
+ },
+
+ /**
+ * APIMethod: setBorder
+ */
+ setBorder:function() {
+ //does nothing since the framed popup's entire scheme is based on a
+ // an image -- changing the popup's border makes no sense.
+ },
+
+ /**
+ * Method: setOpacity
+ * Sets the opacity of the popup.
+ *
+ * Parameters:
+ * opacity - {float} A value between 0.0 (transparent) and 1.0 (solid).
+ */
+ setOpacity:function(opacity) {
+ //does nothing since we suppose that we'll never apply an opacity
+ // to a framed popup
+ },
+
+ /**
+ * APIMethod: setSize
+ * Overridden here, because we need to update the blocks whenever the size
+ * of the popup has changed.
+ *
+ * Parameters:
+ * contentSize - {<OpenLayers.Size>} the new size for the popup's
+ * contents div (in pixels).
+ */
+ setSize:function(contentSize) {
+ OpenLayers.Popup.Anchored.prototype.setSize.apply(this, arguments);
+
+ this.updateBlocks();
+ },
+
+ /**
+ * Method: updateRelativePosition
+ * When the relative position changes, we need to set the new padding
+ * BBOX on the popup, reposition the close div, and update the blocks.
+ */
+ updateRelativePosition: function() {
+
+ //update the padding
+ this.padding = this.positionBlocks[this.relativePosition].padding;
+
+ //update the position of our close box to new padding
+ if (this.closeDiv) {
+ // use the content div's css padding to determine if we should
+ // padd the close div
+ var contentDivPadding = this.getContentDivPadding();
+
+ this.closeDiv.style.right = contentDivPadding.right +
+ this.padding.right + "px";
+ this.closeDiv.style.top = contentDivPadding.top +
+ this.padding.top + "px";
+ }
+
+ this.updateBlocks();
+ },
+
+ /**
+ * Method: calculateNewPx
+ * Besides the standard offset as determined by the Anchored class, our
+ * Framed popups have a special 'offset' property for each of their
+ * positions, which is used to offset the popup relative to its anchor.
+ *
+ * Parameters:
+ * px - {<OpenLayers.Pixel>}
+ *
+ * Returns:
+ * {<OpenLayers.Pixel>} The the new px position of the popup on the screen
+ * relative to the passed-in px.
+ */
+ calculateNewPx:function(px) {
+ var newPx = OpenLayers.Popup.Anchored.prototype.calculateNewPx.apply(
+ this, arguments
+ );
+
+ newPx = newPx.offset(this.positionBlocks[this.relativePosition].offset);
+
+ return newPx;
+ },
+
+ /**
+ * Method: createBlocks
+ */
+ createBlocks: function() {
+ this.blocks = [];
+
+ //since all positions contain the same number of blocks, we can
+ // just pick the first position and use its blocks array to create
+ // our blocks array
+ var firstPosition = null;
+ for(var key in this.positionBlocks) {
+ firstPosition = key;
+ break;
+ }
+
+ var position = this.positionBlocks[firstPosition];
+ for (var i = 0; i < position.blocks.length; i++) {
+
+ var block = {};
+ this.blocks.push(block);
+
+ var divId = this.id + '_FrameDecorationDiv_' + i;
+ block.div = OpenLayers.Util.createDiv(divId,
+ null, null, null, "absolute", null, "hidden", null
+ );
+
+ var imgId = this.id + '_FrameDecorationImg_' + i;
+ var imageCreator =
+ (this.isAlphaImage) ? OpenLayers.Util.createAlphaImageDiv
+ : OpenLayers.Util.createImage;
+
+ block.image = imageCreator(imgId,
+ null, this.imageSize, this.imageSrc,
+ "absolute", null, null, null
+ );
+
+ block.div.appendChild(block.image);
+ this.groupDiv.appendChild(block.div);
+ }
+ },
+
+ /**
+ * Method: updateBlocks
+ * Internal method, called on initialize and when the popup's relative
+ * position has changed. This function takes care of re-positioning
+ * the popup's blocks in their appropropriate places.
+ */
+ updateBlocks: function() {
+ if (!this.blocks) {
+ this.createBlocks();
+ }
+
+ if (this.size && this.relativePosition) {
+ var position = this.positionBlocks[this.relativePosition];
+ for (var i = 0; i < position.blocks.length; i++) {
+
+ var positionBlock = position.blocks[i];
+ var block = this.blocks[i];
+
+ // adjust sizes
+ var l = positionBlock.anchor.left;
+ var b = positionBlock.anchor.bottom;
+ var r = positionBlock.anchor.right;
+ var t = positionBlock.anchor.top;
+
+ //note that we use the isNaN() test here because if the
+ // size object is initialized with a "auto" parameter, the
+ // size constructor calls parseFloat() on the string,
+ // which will turn it into NaN
+ //
+ var w = (isNaN(positionBlock.size.w)) ? this.size.w - (r + l)
+ : positionBlock.size.w;
+
+ var h = (isNaN(positionBlock.size.h)) ? this.size.h - (b + t)
+ : positionBlock.size.h;
+
+ block.div.style.width = w + 'px';
+ block.div.style.height = h + 'px';
+
+ block.div.style.left = (l != null) ? l + 'px' : '';
+ block.div.style.bottom = (b != null) ? b + 'px' : '';
+ block.div.style.right = (r != null) ? r + 'px' : '';
+ block.div.style.top = (t != null) ? t + 'px' : '';
+
+ block.image.style.left = positionBlock.position.x + 'px';
+ block.image.style.top = positionBlock.position.y + 'px';
+ }
+
+ this.contentDiv.style.left = this.padding.left + "px";
+ this.contentDiv.style.top = this.padding.top + "px";
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Popup.Framed"
+});
+/* ======================================================================
+ 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 ar 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: {},
+
+ /**
+ * 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);
+ },
+
+ /**
+ * 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 org = extent.left/resolution + " " +
+ (extent.top/resolution - this.size.h);
+ 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)+xOffset).toFixed();
+ node.style.top = ((geometry.y/resolution)-(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).toFixed(),
+ (bbox.bottom/resolution).toFixed(),
+ (bbox.right/resolution).toFixed(),
+ (bbox.top/resolution).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).toFixed() - radius) + "px";
+ node.style.top = ((geometry.y /resolution).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);
+ y = (comp.y/resolution);
+ 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;
+ y = comp.y / resolution;
+ 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 + "px";
+ node.style.top = geometry.y/resolution + "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;
+ y = comp.y / resolution;
+ 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
====================================================================== */
@@ -10304,14 +20895,45 @@
* the image will be hidden behind the frame.
*/
frame: null,
-
/**
* Property: layerAlphaHack
* {Boolean} True if the png alpha hack needs to be applied on the layer's div.
*/
layerAlphaHack: null,
+
+ /**
+ * Property: isBackBuffer
+ * {Boolean} Is this tile a back buffer tile?
+ */
+ isBackBuffer: false,
+
+ /**
+ * Property: lastRatio
+ * {Float} Used in transition code only. This is the previous ratio
+ * of the back buffer tile resolution to the map resolution. Compared
+ * with the current ratio to determine if zooming occurred.
+ */
+ lastRatio: 1,
+ /**
+ * Property: isFirstDraw
+ * {Boolean} Is this the first time the tile is being drawn?
+ * This is used to force resetBackBuffer to synchronize
+ * the backBufferTile with the foreground tile the first time
+ * the foreground tile loads so that if the user zooms
+ * before the layer has fully loaded, the backBufferTile for
+ * tiles that have been loaded can be used.
+ */
+ isFirstDraw: true,
+
+ /**
+ * Property: backBufferTile
+ * {<OpenLayers.Tile>} A clone of the tile used to create transition
+ * effects when the tile is moved or changes resolution.
+ */
+ backBufferTile: null,
+
/** TBD 3.0 - reorder the parameters to the init function to remove
* URL. the getUrl() function on the layer gets called on
* each draw(), so no need to specify it here.
@@ -10353,12 +20975,22 @@
this.frame.removeChild(this.imgDiv);
this.imgDiv.map = null;
}
+ this.imgDiv.urls = null;
}
this.imgDiv = null;
if ((this.frame != null) && (this.frame.parentNode == this.layer.div)) {
this.layer.div.removeChild(this.frame);
}
this.frame = null;
+
+ /* clean up the backBufferTile if it exists */
+ if (this.backBufferTile) {
+ this.backBufferTile.destroy();
+ this.backBufferTile = null;
+ }
+
+ this.layer.events.unregister("loadend", this, this.resetBackBuffer);
+
OpenLayers.Tile.prototype.destroy.apply(this, arguments);
},
@@ -10401,8 +21033,47 @@
if (this.layer != this.layer.map.baseLayer && this.layer.reproject) {
this.bounds = this.getBoundsFromBaseLayer(this.position);
}
- if (!OpenLayers.Tile.prototype.draw.apply(this, arguments)) {
- return false;
+ var drawTile = OpenLayers.Tile.prototype.draw.apply(this, arguments);
+
+ if (OpenLayers.Util.indexOf(this.layer.SUPPORTED_TRANSITIONS, this.layer.transitionEffect) != -1) {
+ if (drawTile) {
+ //we use a clone of this tile to create a double buffer for visual
+ //continuity. The backBufferTile is used to create transition
+ //effects while the tile in the grid is repositioned and redrawn
+ if (!this.backBufferTile) {
+ this.backBufferTile = this.clone();
+ this.backBufferTile.hide();
+ // this is important. It allows the backBuffer to place itself
+ // appropriately in the DOM. The Image subclass needs to put
+ // the backBufferTile behind the main tile so the tiles can
+ // load over top and display as soon as they are loaded.
+ this.backBufferTile.isBackBuffer = true;
+
+ // potentially end any transition effects when the tile loads
+ this.events.register('loadend', this, this.resetBackBuffer);
+
+ // clear transition back buffer tile only after all tiles in
+ // this layer have loaded to avoid visual glitches
+ this.layer.events.register("loadend", this, this.resetBackBuffer);
+ }
+ // run any transition effects
+ this.startTransition();
+ } else {
+ // if we aren't going to draw the tile, then the backBuffer should
+ // be hidden too!
+ if (this.backBufferTile) {
+ this.backBufferTile.clear();
+ }
+ }
+ } else {
+ if (drawTile && this.isFirstDraw) {
+ this.events.register('loadend', this, this.showTile);
+ this.isFirstDraw = false;
+ }
+ }
+
+ if (!drawTile) {
+ return false;
}
if (this.isLoading) {
@@ -10416,6 +21087,42 @@
return this.renderTile();
},
+ /**
+ * Method: resetBackBuffer
+ * Triggered by two different events, layer loadend, and tile loadend.
+ * In any of these cases, we check to see if we can hide the
+ * backBufferTile yet and update its parameters to match the
+ * foreground tile.
+ *
+ * Basic logic:
+ * - If the backBufferTile hasn't been drawn yet, reset it
+ * - If layer is still loading, show foreground tile but don't hide
+ * the backBufferTile yet
+ * - If layer is done loading, reset backBuffer tile and show
+ * foreground tile
+ */
+ resetBackBuffer: function() {
+ this.showTile();
+ if (this.backBufferTile &&
+ (this.isFirstDraw || !this.layer.numLoadingTiles)) {
+ this.isFirstDraw = false;
+ // check to see if the backBufferTile is within the max extents
+ // before rendering it
+ var maxExtent = this.layer.maxExtent;
+ var withinMaxExtent = (maxExtent &&
+ this.bounds.intersectsBounds(maxExtent, false));
+ if (withinMaxExtent) {
+ this.backBufferTile.position = this.position;
+ this.backBufferTile.bounds = this.bounds;
+ this.backBufferTile.size = this.size;
+ this.backBufferTile.imageSize = this.layer.imageSize || this.size;
+ this.backBufferTile.imageOffset = this.layer.imageOffset;
+ this.backBufferTile.resolution = this.layer.getResolution();
+ this.backBufferTile.renderTile();
+ }
+ }
+ },
+
/**
* Method: renderTile
* Internal function to actually initialize the image tile,
@@ -10428,6 +21135,11 @@
this.imgDiv.viewRequestID = this.layer.map.viewRequestID;
+ // needed for changing to a different serve for onload error
+ if (this.layer.url instanceof Array) {
+ this.imgDiv.urls = this.layer.url.slice();
+ }
+
this.url = this.layer.getURL(this.bounds);
// position the frame
OpenLayers.Util.modifyDOMElement(this.frame,
@@ -10685,6 +21397,201 @@
OpenLayers.Util.getBrowserName() == "safari" ||
OpenLayers.Util.getBrowserName() == "opera");
/* ======================================================================
+ OpenLayers/Tile/WFS.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/Tile.js
+ * @requires OpenLayers/Request/XMLHttpRequest.js
+ */
+
+/**
+ * Class: OpenLayers.Tile.WFS
+ * Instances of OpenLayers.Tile.WFS are used to manage the image tiles
+ * used by various layers. Create a new image tile with the
+ * <OpenLayers.Tile.WFS> constructor.
+ *
+ * Inherits from:
+ * - <OpenLayers.Tile>
+ */
+OpenLayers.Tile.WFS = OpenLayers.Class(OpenLayers.Tile, {
+
+ /**
+ * Property: features
+ * {Array(<OpenLayers.Feature>)} list of features in this tile
+ */
+ features: null,
+
+ /**
+ * Property: url
+ * {String}
+ */
+ url: null,
+
+ /**
+ * Property: request
+ * {<OpenLayers.Request.XMLHttpRequest>}
+ */
+ request: null,
+
+ /** TBD 3.0 - reorder the parameters to the init function to put URL
+ * as last, so we can continue to call tile.initialize()
+ * without changing the arguments.
+ *
+ * Constructor: OpenLayers.Tile.WFS
+ * Constructor for a new <OpenLayers.Tile.WFS> instance.
+ *
+ * Parameters:
+ * layer - {<OpenLayers.Layer>} layer that the tile will go in.
+ * position - {<OpenLayers.Pixel>}
+ * bounds - {<OpenLayers.Bounds>}
+ * url - {<String>}
+ * size - {<OpenLayers.Size>}
+ */
+ initialize: function(layer, position, bounds, url, size) {
+ OpenLayers.Tile.prototype.initialize.apply(this, arguments);
+ this.url = url;
+ this.features = [];
+ },
+
+ /**
+ * APIMethod: destroy
+ * nullify references to prevent circular references and memory leaks
+ */
+ destroy: function() {
+ OpenLayers.Tile.prototype.destroy.apply(this, arguments);
+ this.destroyAllFeatures();
+ this.features = null;
+ this.url = null;
+ if(this.request) {
+ this.request.abort();
+ //this.request.destroy();
+ this.request = null;
+ }
+ },
+
+ /**
+ * Method: clear
+ * Clear the tile of any bounds/position-related data so that it can
+ * be reused in a new location.
+ */
+ clear: function() {
+ this.destroyAllFeatures();
+ },
+
+ /**
+ * Method: draw
+ * Check that a tile should be drawn, and load features for it.
+ */
+ draw:function() {
+ if (OpenLayers.Tile.prototype.draw.apply(this, arguments)) {
+ if (this.isLoading) {
+ //if already loading, send 'reload' instead of 'loadstart'.
+ this.events.triggerEvent("reload");
+ } else {
+ this.isLoading = true;
+ this.events.triggerEvent("loadstart");
+ }
+ this.loadFeaturesForRegion(this.requestSuccess);
+ }
+ },
+
+ /**
+ * Method: loadFeaturesForRegion
+ * Abort any pending requests and issue another request for data.
+ *
+ * Input are function pointers for what to do on success and failure.
+ *
+ * Parameters:
+ * success - {function}
+ * failure - {function}
+ */
+ loadFeaturesForRegion:function(success, failure) {
+ if(this.request) {
+ this.request.abort();
+ }
+ this.request = OpenLayers.Request.GET({
+ url: this.url,
+ success: success,
+ failure: failure,
+ scope: this
+ });
+ },
+
+ /**
+ * Method: requestSuccess
+ * Called on return from request succcess. Adds results via
+ * layer.addFeatures in vector mode, addResults otherwise.
+ *
+ * Parameters:
+ * request - {<OpenLayers.Request.XMLHttpRequest>}
+ */
+ requestSuccess:function(request) {
+ if (this.features) {
+ var doc = request.responseXML;
+ if (!doc || !doc.documentElement) {
+ doc = request.responseText;
+ }
+ if (this.layer.vectorMode) {
+ this.layer.addFeatures(this.layer.formatObject.read(doc));
+ } else {
+ var xml = new OpenLayers.Format.XML();
+ if (typeof doc == "string") {
+ doc = xml.read(doc);
+ }
+ var resultFeatures = xml.getElementsByTagNameNS(
+ doc, "http://www.opengis.net/gml", "featureMember"
+ );
+ this.addResults(resultFeatures);
+ }
+ }
+ if (this.events) {
+ this.events.triggerEvent("loadend");
+ }
+
+ //request produced with success, we can delete the request object.
+ //this.request.destroy();
+ this.request = null;
+ },
+
+ /**
+ * Method: addResults
+ * Construct new feature via layer featureClass constructor, and add to
+ * this.features.
+ *
+ * Parameters:
+ * results - {Object}
+ */
+ addResults: function(results) {
+ for (var i=0; i < results.length; i++) {
+ var feature = new this.layer.featureClass(this.layer,
+ results[i]);
+ this.features.push(feature);
+ }
+ },
+
+
+ /**
+ * Method: destroyAllFeatures
+ * Iterate through and call destroy() on each feature, removing it from
+ * the local array
+ */
+ destroyAllFeatures: function() {
+ while(this.features.length > 0) {
+ var feature = this.features.shift();
+ feature.destroy();
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Tile.WFS"
+ }
+);
+/* ======================================================================
OpenLayers/Control/OverviewMap.js
====================================================================== */
@@ -10778,7 +21685,7 @@
* resolution at which to zoom farther in on the overview map.
*/
maxRatio: 32,
-
+
/**
* APIProperty: mapOptions
* {Object} An object containing any non-default properties to be sent to
@@ -10794,6 +21701,12 @@
handlers: null,
/**
+ * Property: resolutionFactor
+ * {Object}
+ */
+ resolutionFactor: 1,
+
+ /**
* Constructor: OpenLayers.Control.OverviewMap
* Create a new overview map
*
@@ -10930,7 +21843,7 @@
var eventsToStop = ['dblclick','mousedown'];
- for (var i = 0; i < eventsToStop.length; i++) {
+ for (var i=0, len=eventsToStop.length; i<len; i++) {
OpenLayers.Event.observe(this.maximizeDiv,
eventsToStop[i],
@@ -11092,6 +22005,13 @@
Math.max(mapExtent.bottom, maxExtent.bottom),
Math.min(mapExtent.right, maxExtent.right),
Math.min(mapExtent.top, maxExtent.top));
+
+ if (this.ovmap.getProjection() != this.map.getProjection()) {
+ testExtent = testExtent.transform(
+ this.map.getProjectionObject(),
+ this.ovmap.getProjectionObject() );
+ }
+
var resRatio = this.ovmap.getResolution() / this.map.getResolution();
return ((resRatio > this.minRatio) &&
(resRatio <= this.maxRatio) &&
@@ -11113,8 +22033,16 @@
// zoom out overview map
targetRes = this.maxRatio * mapRes;
}
- this.ovmap.setCenter(this.map.center,
- this.ovmap.getZoomForResolution(targetRes));
+ var center;
+ if (this.ovmap.getProjection() != this.map.getProjection()) {
+ center = this.map.center.clone();
+ center.transform(this.map.getProjectionObject(),
+ this.ovmap.getProjectionObject() );
+ } else {
+ center = this.map.center;
+ }
+ this.ovmap.setCenter(center, this.ovmap.getZoomForResolution(
+ targetRes * this.resolutionFactor));
this.updateRectToMap();
},
@@ -11176,6 +22104,15 @@
}
});
+ if (this.ovmap.getProjection() != this.map.getProjection()) {
+ var sourceUnits = this.map.getProjectionObject().getUnits() ||
+ this.map.units || this.map.baseLayer.units;
+ var targetUnits = this.ovmap.getProjectionObject().getUnits() ||
+ this.ovmap.units || this.ovmap.baseLayer.units;
+ this.resolutionFactor = sourceUnits && targetUnits ?
+ OpenLayers.INCHES_PER_UNIT[sourceUnits] /
+ OpenLayers.INCHES_PER_UNIT[targetUnits] : 1;
+ }
},
/**
@@ -11183,14 +22120,16 @@
* Updates the extent rectangle position and size to match the map extent
*/
updateRectToMap: function() {
- // The base layer for overview map needs to be in the same projection
- // as the base layer for the main map. This should be made more robust.
- if(this.map.units != 'degrees') {
- if(this.ovmap.getProjection() && (this.map.getProjection() != this.ovmap.getProjection())) {
- alert(OpenLayers.i18n("sameProjection"));
- }
+ // If the projections differ we need to reproject
+ var bounds;
+ if (this.ovmap.getProjection() != this.map.getProjection()) {
+ bounds = this.map.getExtent().transform(
+ this.map.getProjectionObject(),
+ this.ovmap.getProjectionObject() );
+ } else {
+ bounds = this.map.getExtent();
}
- var pxBounds = this.getRectBoundsFromMapBounds(this.map.getExtent());
+ var pxBounds = this.getRectBoundsFromMapBounds(bounds);
if (pxBounds) {
this.setRectPxBounds(pxBounds);
}
@@ -11202,6 +22141,11 @@
*/
updateMapToRect: function() {
var lonLatBounds = this.getMapBoundsFromRectBounds(this.rectPxBounds);
+ if (this.ovmap.getProjection() != this.map.getProjection()) {
+ lonLatBounds = lonLatBounds.transform(
+ this.ovmap.getProjectionObject(),
+ this.map.getProjectionObject() );
+ }
this.map.panTo(lonLatBounds.getCenterLonLat());
},
@@ -11341,6 +22285,1306 @@
CLASS_NAME: 'OpenLayers.Control.OverviewMap'
});
/* ======================================================================
+ OpenLayers/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/Util.js
+ * @requires OpenLayers/Marker.js
+ * @requires OpenLayers/Popup/AnchoredBubble.js
+ */
+
+/**
+ * Class: OpenLayers.Feature
+ * Features are combinations of geography and attributes. The OpenLayers.Feature
+ * class specifically combines a marker and a lonlat.
+ */
+OpenLayers.Feature = OpenLayers.Class({
+
+ /**
+ * Property: layer
+ * {<OpenLayers.Layer>}
+ */
+ layer: null,
+
+ /**
+ * Property: id
+ * {String}
+ */
+ id: null,
+
+ /**
+ * Property: lonlat
+ * {<OpenLayers.LonLat>}
+ */
+ lonlat: null,
+
+ /**
+ * Property: data
+ * {Object}
+ */
+ data: null,
+
+ /**
+ * Property: marker
+ * {<OpenLayers.Marker>}
+ */
+ marker: null,
+
+ /**
+ * APIProperty: popupClass
+ * {<OpenLayers.Class>} The class which will be used to instantiate
+ * a new Popup. Default is <OpenLayers.Popup.AnchoredBubble>.
+ */
+ popupClass: OpenLayers.Popup.AnchoredBubble,
+
+ /**
+ * Property: popup
+ * {<OpenLayers.Popup>}
+ */
+ popup: null,
+
+ /**
+ * Constructor: OpenLayers.Feature
+ * Constructor for features.
+ *
+ * Parameters:
+ * layer - {<OpenLayers.Layer>}
+ * lonlat - {<OpenLayers.LonLat>}
+ * data - {Object}
+ *
+ * Returns:
+ * {<OpenLayers.Feature>}
+ */
+ initialize: function(layer, lonlat, data) {
+ this.layer = layer;
+ this.lonlat = lonlat;
+ this.data = (data != null) ? data : {};
+ this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
+ },
+
+ /**
+ * Method: destroy
+ * nullify references to prevent circular references and memory leaks
+ */
+ destroy: function() {
+
+ //remove the popup from the map
+ if ((this.layer != null) && (this.layer.map != null)) {
+ if (this.popup != null) {
+ this.layer.map.removePopup(this.popup);
+ }
+ }
+
+ this.layer = null;
+ this.id = null;
+ this.lonlat = null;
+ this.data = null;
+ if (this.marker != null) {
+ this.destroyMarker(this.marker);
+ this.marker = null;
+ }
+ if (this.popup != null) {
+ this.destroyPopup(this.popup);
+ this.popup = null;
+ }
+ },
+
+ /**
+ * Method: onScreen
+ *
+ * Returns:
+ * {Boolean} Whether or not the feature is currently visible on screen
+ * (based on its 'lonlat' property)
+ */
+ onScreen:function() {
+
+ var onScreen = false;
+ if ((this.layer != null) && (this.layer.map != null)) {
+ var screenBounds = this.layer.map.getExtent();
+ onScreen = screenBounds.containsLonLat(this.lonlat);
+ }
+ return onScreen;
+ },
+
+
+ /**
+ * Method: createMarker
+ * Based on the data associated with the Feature, create and return a marker object.
+ *
+ * Returns:
+ * {<OpenLayers.Marker>} A Marker Object created from the 'lonlat' and 'icon' properties
+ * set in this.data. If no 'lonlat' is set, returns null. If no
+ * 'icon' is set, OpenLayers.Marker() will load the default image.
+ *
+ * Note - this.marker is set to return value
+ *
+ */
+ createMarker: function() {
+
+ if (this.lonlat != null) {
+ this.marker = new OpenLayers.Marker(this.lonlat, this.data.icon);
+ }
+ return this.marker;
+ },
+
+ /**
+ * Method: destroyMarker
+ * Destroys marker.
+ * If user overrides the createMarker() function, s/he should be able
+ * to also specify an alternative function for destroying it
+ */
+ destroyMarker: function() {
+ this.marker.destroy();
+ },
+
+ /**
+ * Method: createPopup
+ * Creates a popup object created from the 'lonlat', 'popupSize',
+ * and 'popupContentHTML' properties set in this.data. It uses
+ * this.marker.icon as default anchor.
+ *
+ * If no 'lonlat' is set, returns null.
+ * If no this.marker has been created, no anchor is sent.
+ *
+ * Note - the returned popup object is 'owned' by the feature, so you
+ * cannot use the popup's destroy method to discard the popup.
+ * Instead, you must use the feature's destroyPopup
+ *
+ * Note - this.popup is set to return value
+ *
+ * Parameters:
+ * closeBox - {Boolean} create popup with closebox or not
+ *
+ * Returns:
+ * {<OpenLayers.Popup>} Returns the created popup, which is also set
+ * as 'popup' property of this feature. Will be of whatever type
+ * specified by this feature's 'popupClass' property, but must be
+ * of type <OpenLayers.Popup>.
+ *
+ */
+ createPopup: function(closeBox) {
+
+ if (this.lonlat != null) {
+
+ var id = this.id + "_popup";
+ var anchor = (this.marker) ? this.marker.icon : null;
+
+ if (!this.popup) {
+ this.popup = new this.popupClass(id,
+ this.lonlat,
+ this.data.popupSize,
+ this.data.popupContentHTML,
+ anchor,
+ closeBox);
+ }
+ if (this.data.overflow != null) {
+ this.popup.contentDiv.style.overflow = this.data.overflow;
+ }
+
+ this.popup.feature = this;
+ }
+ return this.popup;
+ },
+
+
+ /**
+ * Method: destroyPopup
+ * Destroys the popup created via createPopup.
+ *
+ * As with the marker, if user overrides the createPopup() function, s/he
+ * should also be able to override the destruction
+ */
+ destroyPopup: function() {
+ if (this.popup) {
+ this.popup.feature = null;
+ this.popup.destroy();
+ this.popup = null;
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Feature"
+});
+/* ======================================================================
+ OpenLayers/Format/WMC.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/Format/XML.js
+ */
+
+/**
+ * Class: OpenLayers.Format.WMC
+ * Read and write Web Map Context documents.
+ *
+ * Inherits from:
+ * - <OpenLayers.Format.XML>
+ */
+OpenLayers.Format.WMC = OpenLayers.Class({
+
+ /**
+ * APIProperty: defaultVersion
+ * {String} Version number to assume if none found. Default is "1.1.0".
+ */
+ defaultVersion: "1.1.0",
+
+ /**
+ * APIProperty: version
+ * {String} Specify a version string if one is known.
+ */
+ version: null,
+
+ /**
+ * Property: layerOptions
+ * {Object} Default options for layers created by the parser. These
+ * options are overridden by the options which are read from the
+ * capabilities document.
+ */
+ layerOptions: null,
+
+ /**
+ * Property: parser
+ * {Object} Instance of the versioned parser. Cached for multiple read and
+ * write calls of the same version.
+ */
+ parser: null,
+
+ /**
+ * Constructor: OpenLayers.Format.WMC
+ * Create a new parser for WMC docs.
+ *
+ * Parameters:
+ * options - {Object} An optional object whose properties will be set on
+ * this instance.
+ */
+ initialize: function(options) {
+ OpenLayers.Util.extend(this, options);
+ this.options = options;
+ },
+
+ /**
+ * APIMethod: read
+ * Read WMC data from a string, and return an object with map properties
+ * and a list of layers.
+ *
+ * Parameters:
+ * data - {String} or {DOMElement} data to read/parse.
+ * options - {Object} The options object must contain a map property. If
+ * the map property is a string, it must be the id of a dom element
+ * where the new map will be placed. If the map property is an
+ * <OpenLayers.Map>, the layers from the context document will be added
+ * to the map.
+ *
+ * Returns:
+ * {<OpenLayers.Map>} A map based on the context.
+ */
+ read: function(data, options) {
+ if(typeof data == "string") {
+ data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
+ }
+ var root = data.documentElement;
+ var version = this.version;
+ if(!version) {
+ version = root.getAttribute("version");
+ if(!version) {
+ version = this.defaultVersion;
+ }
+ }
+ if(!this.parser || this.parser.VERSION != version) {
+ var format = OpenLayers.Format.WMC[
+ "v" + version.replace(/\./g, "_")
+ ];
+ if(!format) {
+ throw "Can't find a WMC parser for version " +
+ version;
+ }
+ this.parser = new format(this.options);
+ }
+ var context = this.parser.read(data, options);
+ var map;
+ if(options.map) {
+ this.context = context;
+ if(options.map instanceof OpenLayers.Map) {
+ map = this.mergeContextToMap(context, options.map);
+ } else {
+ map = this.contextToMap(context, options.map);
+ }
+ } else {
+ // not documented as part of the API, provided as a non-API option
+ map = context;
+ }
+ return map;
+ },
+
+ /**
+ * Method: contextToMap
+ * Create a map given a context object.
+ *
+ * Parameters:
+ * context - {Object} The context object.
+ * id - {String | Element} The dom element or element id that will contain
+ * the map.
+ *
+ * Returns:
+ * {<OpenLayers.Map>} A map based on the context object.
+ */
+ contextToMap: function(context, id) {
+ var map = new OpenLayers.Map(id, {
+ maxExtent: context.maxExtent,
+ projection: context.projection
+ });
+ map.addLayers(context.layers);
+ map.setCenter(
+ context.bounds.getCenterLonLat(),
+ map.getZoomForExtent(context.bounds, true)
+ );
+ return map;
+ },
+
+ /**
+ * Method: mergeContextToMap
+ * Add layers from a context object to a map.
+ *
+ * Parameters:
+ * context - {Object} The context object.
+ * map - {<OpenLayers.Map>} The map.
+ *
+ * Returns:
+ * {<OpenLayers.Map>} The same map with layers added.
+ */
+ mergeContextToMap: function(context, map) {
+ map.addLayers(context.layers);
+ return map;
+ },
+
+ /**
+ * APIMethod: write
+ * Write a WMC document given a map.
+ *
+ * Parameters:
+ * obj - {<OpenLayers.Map> | Object} A map or context object.
+ * options - {Object} Optional configuration object.
+ *
+ * Returns:
+ * {String} A WMC document string.
+ */
+ write: function(obj, options) {
+ if(obj.CLASS_NAME == "OpenLayers.Map") {
+ obj = this.mapToContext(obj);
+ }
+ var version = (options && options.version) ||
+ this.version || this.defaultVersion;
+ if(!this.parser || this.parser.VERSION != version) {
+ var format = OpenLayers.Format.WMC[
+ "v" + version.replace(/\./g, "_")
+ ];
+ if(!format) {
+ throw "Can't find a WMS capabilities parser for version " +
+ version;
+ }
+ this.parser = new format(this.options);
+ }
+ var wmc = this.parser.write(obj, options);
+ return wmc;
+ },
+
+ /**
+ * Method: mapToContext
+ * Create a context object given a map.
+ *
+ * Parameters:
+ * map - {<OpenLayers.Map>} The map.
+ *
+ * Returns:
+ * {Object} A context object.
+ */
+ mapToContext: function(map) {
+ var context = {
+ bounds: map.getExtent(),
+ maxExtent: map.maxExtent,
+ projection: map.projection,
+ layers: map.layers,
+ size: map.getSize()
+ };
+ return context;
+ },
+
+ CLASS_NAME: "OpenLayers.Format.WMC"
+
+});
+/* ======================================================================
+ OpenLayers/Format/WMC/v1.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/Format/XML.js
+ */
+
+/**
+ * Class: OpenLayers.Format.WMC.v1
+ * Superclass for WMC version 1 parsers.
+ *
+ * Inherits from:
+ * - <OpenLayers.Format.XML>
+ */
+OpenLayers.Format.WMC.v1 = OpenLayers.Class(OpenLayers.Format.XML, {
+
+ /**
+ * Property: namespaces
+ * {Object} Mapping of namespace aliases to namespace URIs.
+ */
+ namespaces: {
+ ol: "http://openlayers.org/context",
+ wmc: "http://www.opengis.net/context",
+ sld: "http://www.opengis.net/sld",
+ xlink: "http://www.w3.org/1999/xlink",
+ xsi: "http://www.w3.org/2001/XMLSchema-instance"
+ },
+
+ /**
+ * Property: schemaLocation
+ * {String} Schema location for a particular minor version.
+ */
+ schemaLocation: "",
+
+ /**
+ * Method: getNamespacePrefix
+ * Get the namespace prefix for a given uri from the <namespaces> object.
+ *
+ * Returns:
+ * {String} A namespace prefix or null if none found.
+ */
+ getNamespacePrefix: function(uri) {
+ var prefix = null;
+ if(uri == null) {
+ prefix = this.namespaces[this.defaultPrefix];
+ } else {
+ for(prefix in this.namespaces) {
+ if(this.namespaces[prefix] == uri) {
+ break;
+ }
+ }
+ }
+ return prefix;
+ },
+
+ /**
+ * Property: defaultPrefix
+ */
+ defaultPrefix: "wmc",
+
+ /**
+ * Property: rootPrefix
+ * {String} Prefix on the root node that maps to the context namespace URI.
+ */
+ rootPrefix: null,
+
+ /**
+ * Property: defaultStyleName
+ * {String} Style name used if layer has no style param. Default is "".
+ */
+ defaultStyleName: "",
+
+ /**
+ * Property: defaultStyleTitle
+ * {String} Default style title. Default is "Default".
+ */
+ defaultStyleTitle: "Default",
+
+ /**
+ * Constructor: OpenLayers.Format.WMC.v1
+ * Instances of this class are not created directly. Use the
+ * <OpenLayers.Format.WMC> constructor instead.
+ *
+ * Parameters:
+ * options - {Object} An optional object whose properties will be set on
+ * this instance.
+ */
+ initialize: function(options) {
+ OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
+ },
+
+ /**
+ * Method: read
+ * Read capabilities data from a string, and return a list of layers.
+ *
+ * Parameters:
+ * data - {String} or {DOMElement} data to read/parse.
+ *
+ * Returns:
+ * {Array} List of named layers.
+ */
+ read: function(data) {
+ if(typeof data == "string") {
+ data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
+ }
+ var root = data.documentElement;
+ this.rootPrefix = root.prefix;
+ var context = {
+ version: root.getAttribute("version")
+ };
+ this.runChildNodes(context, root);
+ return context;
+ },
+
+ /**
+ * Method: runChildNodes
+ */
+ runChildNodes: function(obj, node) {
+ var children = node.childNodes;
+ var childNode, processor, prefix, local;
+ for(var i=0, len=children.length; i<len; ++i) {
+ childNode = children[i];
+ if(childNode.nodeType == 1) {
+ prefix = this.getNamespacePrefix(childNode.namespaceURI);
+ local = childNode.nodeName.split(":").pop();
+ processor = this["read_" + prefix + "_" + local];
+ if(processor) {
+ processor.apply(this, [obj, childNode]);
+ }
+ }
+ }
+ },
+
+ /**
+ * Method: read_wmc_General
+ */
+ read_wmc_General: function(context, node) {
+ this.runChildNodes(context, node);
+ },
+
+ /**
+ * Method: read_wmc_BoundingBox
+ */
+ read_wmc_BoundingBox: function(context, node) {
+ context.projection = node.getAttribute("SRS");
+ context.bounds = new OpenLayers.Bounds(
+ parseFloat(node.getAttribute("minx")),
+ parseFloat(node.getAttribute("miny")),
+ parseFloat(node.getAttribute("maxx")),
+ parseFloat(node.getAttribute("maxy"))
+ );
+ },
+
+ /**
+ * Method: read_wmc_LayerList
+ */
+ read_wmc_LayerList: function(context, node) {
+ context.layers = [];
+ this.runChildNodes(context, node);
+ },
+
+ /**
+ * Method: read_wmc_Layer
+ */
+ read_wmc_Layer: function(context, node) {
+ var layerInfo = {
+ params: {},
+ options: {
+ visibility: (node.getAttribute("hidden") != "1"),
+ queryable: (node.getAttribute("queryable") == "1")
+
+ },
+ formats: [],
+ styles: []
+ };
+ this.runChildNodes(layerInfo, node);
+ // set properties common to multiple objects on layer options/params
+ layerInfo.params.layers = layerInfo.name;
+ layerInfo.options.maxExtent = layerInfo.maxExtent;
+ // create the layer
+ var layer = this.getLayerFromInfo(layerInfo);
+ context.layers.push(layer);
+ },
+
+ /**
+ * Method: getLayerFromInfo
+ * Create a WMS layer from a layerInfo object.
+ *
+ * Parameters:
+ * layerInfo - {Object} An object representing a WMS layer.
+ *
+ * Returns:
+ * {<OpenLayers.Layer.WMS>} A WMS layer.
+ */
+ getLayerFromInfo: function(layerInfo) {
+ var options = layerInfo.options;
+ if (this.layerOptions) {
+ OpenLayers.Util.applyDefaults(options, this.layerOptions);
+ }
+ var layer = new OpenLayers.Layer.WMS(
+ layerInfo.title,
+ layerInfo.href,
+ layerInfo.params,
+ options
+ );
+ return layer;
+ },
+
+ /**
+ * Method: read_wmc_Extension
+ */
+ read_wmc_Extension: function(obj, node) {
+ this.runChildNodes(obj, node);
+ },
+
+ /**
+ * Method: read_ol_units
+ */
+ read_ol_units: function(layerInfo, node) {
+ layerInfo.options.units = this.getChildValue(node);
+ },
+
+ /**
+ * Method: read_ol_maxExtent
+ */
+ read_ol_maxExtent: function(obj, node) {
+ var bounds = new OpenLayers.Bounds(
+ node.getAttribute("minx"), node.getAttribute("miny"),
+ node.getAttribute("maxx"), node.getAttribute("maxy")
+ );
+ obj.maxExtent = bounds;
+ },
+
+ /**
+ * Method: read_ol_transparent
+ */
+ read_ol_transparent: function(layerInfo, node) {
+ layerInfo.params.transparent = this.getChildValue(node);
+ },
+
+ /**
+ * Method: read_ol_numZoomLevels
+ */
+ read_ol_numZoomLevels: function(layerInfo, node) {
+ layerInfo.options.numZoomLevels = parseInt(this.getChildValue(node));
+ },
+
+ /**
+ * Method: read_ol_opacity
+ */
+ read_ol_opacity: function(layerInfo, node) {
+ layerInfo.options.opacity = parseFloat(this.getChildValue(node));
+ },
+
+ /**
+ * Method: read_ol_singleTile
+ */
+ read_ol_singleTile: function(layerInfo, node) {
+ layerInfo.options.singleTile = (this.getChildValue(node) == "true");
+ },
+
+ /**
+ * Method: read_ol_isBaseLayer
+ */
+ read_ol_isBaseLayer: function(layerInfo, node) {
+ layerInfo.options.isBaseLayer = (this.getChildValue(node) == "true");
+ },
+
+ /**
+ * Method: read_ol_displayInLayerSwitcher
+ */
+ read_ol_displayInLayerSwitcher: function(layerInfo, node) {
+ layerInfo.options.displayInLayerSwitcher =
+ (this.getChildValue(node) == "true");
+ },
+
+ /**
+ * Method: read_wmc_Server
+ */
+ read_wmc_Server: function(layerInfo, node) {
+ layerInfo.params.version = node.getAttribute("version");
+ this.runChildNodes(layerInfo, node);
+ },
+
+ /**
+ * Method: read_wmc_FormatList
+ */
+ read_wmc_FormatList: function(layerInfo, node) {
+ this.runChildNodes(layerInfo, node);
+ },
+
+ /**
+ * Method: read_wmc_Format
+ */
+ read_wmc_Format: function(layerInfo, node) {
+ var format = this.getChildValue(node);
+ layerInfo.formats.push(format);
+ if(node.getAttribute("current") == "1") {
+ layerInfo.params.format = format;
+ }
+ },
+
+ /**
+ * Method: read_wmc_StyleList
+ */
+ read_wmc_StyleList: function(layerInfo, node) {
+ this.runChildNodes(layerInfo, node);
+ },
+
+ /**
+ * Method: read_wmc_Style
+ */
+ read_wmc_Style: function(layerInfo, node) {
+ var style = {};
+ this.runChildNodes(style, node);
+ if(node.getAttribute("current") == "1") {
+ // three style types to consider
+ // 1) linked SLD
+ // 2) inline SLD
+ // 3) named style
+ // running child nodes always gets name, optionally gets href or body
+ if(style.href) {
+ layerInfo.params.sld = style.href;
+ } else if(style.body) {
+ layerInfo.params.sld_body = style.body;
+ } else {
+ layerInfo.params.styles = style.name;
+ }
+ }
+ layerInfo.styles.push(style);
+ },
+
+ /**
+ * Method: read_wmc_SLD
+ */
+ read_wmc_SLD: function(style, node) {
+ this.runChildNodes(style, node);
+ // style either comes back with an href or a body property
+ },
+
+ /**
+ * Method: read_sld_StyledLayerDescriptor
+ */
+ read_sld_StyledLayerDescriptor: function(sld, node) {
+ var xml = OpenLayers.Format.XML.prototype.write.apply(this, [node]);
+ sld.body = xml;
+ },
+
+ /**
+ * Method: read_wmc_OnlineResource
+ */
+ read_wmc_OnlineResource: function(obj, node) {
+ obj.href = this.getAttributeNS(
+ node, this.namespaces.xlink, "href"
+ );
+ },
+
+ /**
+ * Method: read_wmc_Name
+ */
+ read_wmc_Name: function(obj, node) {
+ var name = this.getChildValue(node);
+ if(name) {
+ obj.name = name;
+ }
+ },
+
+ /**
+ * Method: read_wmc_Title
+ */
+ read_wmc_Title: function(obj, node) {
+ var title = this.getChildValue(node);
+ if(title) {
+ obj.title = title;
+ }
+ },
+
+ /**
+ * Method: read_wmc_MetadataURL
+ */
+ read_wmc_MetadataURL: function(layerInfo, node) {
+ var metadataURL = {};
+ var links = node.getElementsByTagName("OnlineResource");
+ if(links.length > 0) {
+ this.read_wmc_OnlineResource(metadataURL, links[0]);
+ }
+ layerInfo.options.metadataURL = metadataURL.href;
+
+ },
+
+ /**
+ * Method: read_wmc_Abstract
+ */
+ read_wmc_Abstract: function(obj, node) {
+ var abst = this.getChildValue(node);
+ if(abst) {
+ obj["abstract"] = abst;
+ }
+ },
+
+ /**
+ * Method: read_wmc_LatLonBoundingBox
+ */
+ read_wmc_LatLonBoundingBox: function(layer, node) {
+ layer.llbbox = [
+ parseFloat(node.getAttribute("minx")),
+ parseFloat(node.getAttribute("miny")),
+ parseFloat(node.getAttribute("maxx")),
+ parseFloat(node.getAttribute("maxy"))
+ ];
+ },
+
+ /**
+ * Method: read_wmc_LegendURL
+ */
+ read_wmc_LegendURL: function(style, node) {
+ var legend = {
+ width: node.getAttribute('width'),
+ height: node.getAttribute('height')
+ };
+ var links = node.getElementsByTagName("OnlineResource");
+ if(links.length > 0) {
+ this.read_wmc_OnlineResource(legend, links[0]);
+ }
+ style.legend = legend;
+ },
+
+ /**
+ * Method: write
+ *
+ * Parameters:
+ * context - {Object} An object representing the map context.
+ * options - {Object} Optional object.
+ *
+ * Returns:
+ * {String} A WMC document string.
+ */
+ write: function(context, options) {
+ var root = this.createElementDefaultNS("ViewContext");
+ this.setAttributes(root, {
+ version: this.VERSION,
+ id: (options && typeof options.id == "string") ?
+ options.id :
+ OpenLayers.Util.createUniqueID("OpenLayers_Context_")
+ });
+
+ // add schemaLocation attribute
+ this.setAttributeNS(
+ root, this.namespaces.xsi,
+ "xsi:schemaLocation", this.schemaLocation
+ );
+
+ // required General element
+ root.appendChild(this.write_wmc_General(context));
+
+ // required LayerList element
+ root.appendChild(this.write_wmc_LayerList(context));
+
+ return OpenLayers.Format.XML.prototype.write.apply(this, [root]);
+ },
+
+ /**
+ * Method: createElementDefaultNS
+ * Shorthand for createElementNS with namespace from <defaultPrefix>.
+ * Can optionally be used to set attributes and a text child value.
+ *
+ * Parameters:
+ * name - {String} The qualified node name.
+ * childValue - {String} Optional value for text child node.
+ * attributes - {Object} Optional object representing attributes.
+ *
+ * Returns:
+ * {Element} An element node.
+ */
+ createElementDefaultNS: function(name, childValue, attributes) {
+ var node = this.createElementNS(
+ this.namespaces[this.defaultPrefix],
+ name
+ );
+ if(childValue) {
+ node.appendChild(this.createTextNode(childValue));
+ }
+ if(attributes) {
+ this.setAttributes(node, attributes);
+ }
+ return node;
+ },
+
+ /**
+ * Method: setAttributes
+ * Set multiple attributes given key value pairs from an object.
+ *
+ * Parameters:
+ * node - {Element} An element node.
+ * obj - {Object} An object whose properties represent attribute names and
+ * values represent attribute values.
+ */
+ setAttributes: function(node, obj) {
+ var value;
+ for(var name in obj) {
+ value = obj[name].toString();
+ if(value.match(/[A-Z]/)) {
+ // safari lowercases attributes with setAttribute
+ this.setAttributeNS(node, null, name, value);
+ } else {
+ node.setAttribute(name, value);
+ }
+ }
+ },
+
+ /**
+ * Method: write_wmc_General
+ * Create a General node given an context object.
+ *
+ * Parameters:
+ * context - {Object} Context object.
+ *
+ * Returns:
+ * {Element} A WMC General element node.
+ */
+ write_wmc_General: function(context) {
+ var node = this.createElementDefaultNS("General");
+
+ // optional Window element
+ if(context.size) {
+ node.appendChild(this.createElementDefaultNS(
+ "Window", null,
+ {
+ width: context.size.w,
+ height: context.size.h
+ }
+ ));
+ }
+
+ // required BoundingBox element
+ var bounds = context.bounds;
+ node.appendChild(this.createElementDefaultNS(
+ "BoundingBox", null,
+ {
+ minx: bounds.left.toPrecision(10),
+ miny: bounds.bottom.toPrecision(10),
+ maxx: bounds.right.toPrecision(10),
+ maxy: bounds.top.toPrecision(10),
+ SRS: context.projection
+ }
+ ));
+
+ // required Title element
+ node.appendChild(this.createElementDefaultNS(
+ "Title", context.title
+ ));
+
+ // OpenLayers specific map properties
+ node.appendChild(this.write_ol_MapExtension(context));
+
+ return node;
+ },
+
+ /**
+ * Method: write_ol_MapExtension
+ */
+ write_ol_MapExtension: function(context) {
+ var node = this.createElementDefaultNS("Extension");
+
+ var bounds = context.maxExtent;
+ if(bounds) {
+ var maxExtent = this.createElementNS(
+ this.namespaces.ol, "ol:maxExtent"
+ );
+ this.setAttributes(maxExtent, {
+ minx: bounds.left.toPrecision(10),
+ miny: bounds.bottom.toPrecision(10),
+ maxx: bounds.right.toPrecision(10),
+ maxy: bounds.top.toPrecision(10)
+ });
+ node.appendChild(maxExtent);
+ }
+
+ return node;
+ },
+
+ /**
+ * Method: write_wmc_LayerList
+ * Create a LayerList node given an context object.
+ *
+ * Parameters:
+ * context - {Object} Context object.
+ *
+ * Returns:
+ * {Element} A WMC LayerList element node.
+ */
+ write_wmc_LayerList: function(context) {
+ var list = this.createElementDefaultNS("LayerList");
+
+ var layer;
+ for(var i=0, len=context.layers.length; i<len; ++i) {
+ layer = context.layers[i];
+ if(layer instanceof OpenLayers.Layer.WMS) {
+ list.appendChild(this.write_wmc_Layer(layer));
+ }
+ }
+
+ return list;
+ },
+
+ /**
+ * Method: write_wmc_Layer
+ * Create a Layer node given a layer object.
+ *
+ * Parameters:
+ * layer - {<OpenLayers.Layer.WMS>} Layer object.
+ *
+ * Returns:
+ * {Element} A WMC Layer element node.
+ */
+ write_wmc_Layer: function(layer) {
+ var node = this.createElementDefaultNS(
+ "Layer", null, {
+ queryable: layer.queryable ? "1" : "0",
+ hidden: layer.visibility ? "0" : "1"
+ }
+ );
+
+ // required Server element
+ node.appendChild(this.write_wmc_Server(layer));
+
+ // required Name element
+ node.appendChild(this.createElementDefaultNS(
+ "Name", layer.params["LAYERS"]
+ ));
+
+ // required Title element
+ node.appendChild(this.createElementDefaultNS(
+ "Title", layer.name
+ ));
+
+ // optional MetadataURL element
+ if (layer.metadataURL) {
+ node.appendChild(this.write_wmc_MetadataURL(layer));
+ }
+
+ // optional FormatList element
+ node.appendChild(this.write_wmc_FormatList(layer));
+
+ // optional StyleList element
+ node.appendChild(this.write_wmc_StyleList(layer));
+
+ // OpenLayers specific properties go in an Extension element
+ node.appendChild(this.write_wmc_LayerExtension(layer));
+
+ return node;
+ },
+
+ /**
+ * Method: write_wmc_LayerExtension
+ * Add OpenLayers specific layer parameters to an Extension element.
+ *
+ * Parameters:
+ * layer - {<OpenLayers.Layer.WMS>} A WMS layer.
+ *
+ * Returns:
+ * {Element} A WMC Extension element (for a layer).
+ */
+ write_wmc_LayerExtension: function(layer) {
+ var node = this.createElementDefaultNS("Extension");
+
+ var bounds = layer.maxExtent;
+ var maxExtent = this.createElementNS(
+ this.namespaces.ol, "ol:maxExtent"
+ );
+ this.setAttributes(maxExtent, {
+ minx: bounds.left.toPrecision(10),
+ miny: bounds.bottom.toPrecision(10),
+ maxx: bounds.right.toPrecision(10),
+ maxy: bounds.top.toPrecision(10)
+ });
+ node.appendChild(maxExtent);
+
+ var param = layer.params["TRANSPARENT"];
+ if(param) {
+ var trans = this.createElementNS(
+ this.namespaces.ol, "ol:transparent"
+ );
+ trans.appendChild(this.createTextNode(param));
+ node.appendChild(trans);
+ }
+
+ var properties = [
+ "numZoomLevels", "units", "isBaseLayer",
+ "opacity", "displayInLayerSwitcher", "singleTile"
+ ];
+ var child;
+ for(var i=0, len=properties.length; i<len; ++i) {
+ child = this.createOLPropertyNode(layer, properties[i]);
+ if(child) {
+ node.appendChild(child);
+ }
+ }
+
+ return node;
+ },
+
+ /**
+ * Method: createOLPropertyNode
+ * Create a node representing an OpenLayers property. If the property is
+ * null or undefined, null will be returned.
+ *
+ * Parameters:
+ * object - {Object} An object.
+ * prop - {String} A property.
+ *
+ * Returns:
+ * {Element} A property node.
+ */
+ createOLPropertyNode: function(obj, prop) {
+ var node = null;
+ if(obj[prop] != null) {
+ node = this.createElementNS(this.namespaces.ol, "ol:" + prop);
+ node.appendChild(this.createTextNode(obj[prop].toString()));
+ }
+ return node;
+ },
+
+ /**
+ * Method: write_wmc_Server
+ * Create a Server node given a layer object.
+ *
+ * Parameters:
+ * layer - {<OpenLayers.Layer.WMS>} Layer object.
+ *
+ * Returns:
+ * {Element} A WMC Server element node.
+ */
+ write_wmc_Server: function(layer) {
+ var node = this.createElementDefaultNS("Server");
+ this.setAttributes(node, {
+ service: "OGC:WMS",
+ version: layer.params["VERSION"]
+ });
+
+ // required OnlineResource element
+ node.appendChild(this.write_wmc_OnlineResource(layer.url));
+
+ return node;
+ },
+
+ /**
+ * Method: write_wmc_MetadataURL
+ * Create a MetadataURL node given a layer object.
+ *
+ * Parameters:
+ * layer - {<OpenLayers.Layer.WMS>} Layer object.
+ *
+ * Returns:
+ * {Element} A WMC metadataURL element node.
+ */
+ write_wmc_MetadataURL: function(layer) {
+ var node = this.createElementDefaultNS("MetadataURL");
+
+ // required OnlineResource element
+ node.appendChild(this.write_wmc_OnlineResource(layer.metadataURL));
+
+ return node;
+ },
+
+ /**
+ * Method: write_wmc_FormatList
+ * Create a FormatList node given a layer.
+ *
+ * Parameters:
+ * layer - {<OpenLayers.Layer.WMS>} Layer object.
+ *
+ * Returns:
+ * {Element} A WMC FormatList element node.
+ */
+ write_wmc_FormatList: function(layer) {
+ var node = this.createElementDefaultNS("FormatList");
+ node.appendChild(this.createElementDefaultNS(
+ "Format", layer.params["FORMAT"], {current: "1"}
+ ));
+
+ return node;
+ },
+
+ /**
+ * Method: write_wmc_StyleList
+ * Create a StyleList node given a layer.
+ *
+ * Parameters:
+ * layer - {<OpenLayers.Layer.WMS>} Layer object.
+ *
+ * Returns:
+ * {Element} A WMC StyleList element node.
+ */
+ write_wmc_StyleList: function(layer) {
+ var node = this.createElementDefaultNS("StyleList");
+ var style = this.createElementDefaultNS(
+ "Style", null, {current: "1"}
+ );
+
+ // Style can come from one of three places (prioritized as below):
+ // 1) an SLD parameter
+ // 2) and SLD_BODY parameter
+ // 3) the STYLES parameter
+
+ if(layer.params["SLD"]) {
+ // create link from SLD parameter
+ var sld = this.createElementDefaultNS("SLD");
+ var link = this.write_wmc_OnlineResource(layer.params["SLD"]);
+ sld.appendChild(link);
+ style.appendChild(sld);
+ } else if(layer.params["SLD_BODY"]) {
+ // include sld fragment from SLD_BODY parameter
+ var sld = this.createElementDefaultNS("SLD");
+ var body = layer.params["SLD_BODY"];
+ // read in body as xml doc - assume proper namespace declarations
+ var doc = OpenLayers.Format.XML.prototype.read.apply(this, [body]);
+ // append to StyledLayerDescriptor node
+ var imported = doc.documentElement;
+ if(sld.ownerDocument && sld.ownerDocument.importNode) {
+ imported = sld.ownerDocument.importNode(imported, true);
+ }
+ sld.appendChild(imported);
+ style.appendChild(sld);
+ } else {
+ // use name(s) from STYLES parameter
+ var name = layer.params["STYLES"] ?
+ layer.params["STYLES"] : this.defaultStyleName;
+
+ style.appendChild(this.createElementDefaultNS("Name", name));
+ style.appendChild(this.createElementDefaultNS(
+ "Title", this.defaultStyleTitle
+ ));
+ }
+ node.appendChild(style);
+ return node;
+ },
+
+ /**
+ * Method: write_wmc_OnlineResource
+ * Create an OnlineResource node given a URL.
+ *
+ * Parameters:
+ * href - {String} URL for the resource.
+ *
+ * Returns:
+ * {Element} A WMC OnlineResource element node.
+ */
+ write_wmc_OnlineResource: function(href) {
+ var node = this.createElementDefaultNS("OnlineResource");
+ this.setAttributeNS(node, this.namespaces.xlink, "xlink:type", "simple");
+ this.setAttributeNS(node, this.namespaces.xlink, "xlink:href", href);
+ return node;
+ },
+
+ CLASS_NAME: "OpenLayers.Format.WMC.v1"
+
+});
+/* ======================================================================
OpenLayers/Handler/Click.js
====================================================================== */
@@ -11436,6 +23680,13 @@
down: null,
/**
+ * Property: rightclickTimerId
+ * {Number} The id of the right mouse timeout waiting to clear the
+ * <delayedEvent>.
+ */
+ rightclickTimerId: null,
+
+ /**
* Constructor: OpenLayers.Handler.Click
* Create a new click handler.
*
@@ -11471,8 +23722,80 @@
* {Boolean} Continue propagating this event.
*/
mousedown: null,
+
+ /**
+ * Method: mouseup
+ * Handle mouseup. Installed to support collection of right mouse events.
+ *
+ * Returns:
+ * {Boolean} Continue propagating this event.
+ */
+ mouseup: function (evt) {
+ var propagate = true;
+
+ // Collect right mouse clicks from the mouseup
+ // IE - ignores the second right click in mousedown so using
+ // mouseup instead
+ if (this.checkModifiers(evt) &&
+ this.control.handleRightClicks &&
+ OpenLayers.Event.isRightClick(evt)) {
+ propogate = this.rightclick(evt);
+ }
+
+ return propagate;
+ },
/**
+ * Method: rightclick
+ * Handle rightclick. For a dblrightclick, we get two clicks so we need
+ * to always register for dblrightclick to properly handle single
+ * clicks.
+ *
+ * Returns:
+ * {Boolean} Continue propagating this event.
+ */
+ rightclick: function(evt) {
+ if(this.passesTolerance(evt)) {
+ if(this.rightclickTimerId != null) {
+ //Second click received before timeout this must be
+ // a double click
+ this.clearTimer();
+ this.callback('dblrightclick', [evt]);
+ return !this.stopDouble;
+ } else {
+ //Set the rightclickTimerId, send evt only if double is
+ // true else trigger single
+ var clickEvent = this['double'] ?
+ OpenLayers.Util.extend({}, evt) :
+ this.callback('rightclick', [evt]);
+
+ var delayedRightCall = OpenLayers.Function.bind(
+ this.delayedRightCall,
+ this,
+ clickEvent
+ );
+ this.rightclickTimerId = window.setTimeout(
+ delayedRightCall, this.delay
+ );
+ }
+ }
+ return !this.stopSingle;
+ },
+
+ /**
+ * Method: delayedRightCall
+ * Sets <rightclickTimerId> to null. And optionally triggers the
+ * rightclick callback if evt is set.
+ */
+ delayedRightCall: function(evt) {
+ this.rightclickTimerId = null;
+ if (evt) {
+ this.callback('rightclick', [evt]);
+ }
+ return !this.stopSingle;
+ },
+
+ /**
* Method: dblclick
* Handle dblclick. For a dblclick, we get two clicks in some browsers
* (FF) and one in others (IE). So we need to always register for
@@ -11657,6 +23980,23 @@
* {Function}
*/
oldOnselectstart: null,
+
+ /**
+ * Property: interval
+ * {Integer} In order to increase performance, an interval (in
+ * milliseconds) can be set to reduce the number of drag events
+ * called. If set, a new drag event will not be set until the
+ * interval has passed.
+ * Defaults to 0, meaning no interval.
+ */
+ interval: 0,
+
+ /**
+ * Property: timeoutId
+ * {String} The id of the timeout used for the mousedown interval.
+ * This is "private", and should be left alone.
+ */
+ timeoutId: null,
/**
* Constructor: OpenLayers.Handler.Drag
@@ -11782,20 +24122,29 @@
* {Boolean} Let the event propagate.
*/
mousemove: function (evt) {
- if (this.started) {
- if(evt.xy.x != this.last.x || evt.xy.y != this.last.y) {
- this.dragging = true;
- this.move(evt);
- this.callback("move", [evt.xy]);
- if(!this.oldOnselectstart) {
- this.oldOnselectstart = document.onselectstart;
- document.onselectstart = function() {return false;};
- }
- this.last = evt.xy;
+ if (this.started && !this.timeoutId && (evt.xy.x != this.last.x || evt.xy.y != this.last.y)) {
+ if (this.interval > 0) {
+ this.timeoutId = setTimeout(OpenLayers.Function.bind(this.removeTimeout, this), this.interval);
}
+ this.dragging = true;
+ this.move(evt);
+ this.callback("move", [evt.xy]);
+ if(!this.oldOnselectstart) {
+ this.oldOnselectstart = document.onselectstart;
+ document.onselectstart = function() {return false;};
+ }
+ this.last = this.evt.xy;
}
return true;
},
+
+ /**
+ * Method: removeTimeout
+ * Private. Called by mousemove() to remove the drag timeout.
+ */
+ removeTimeout: function() {
+ this.timeoutId = null;
+ },
/**
* Method: mouseup
@@ -11908,6 +24257,680 @@
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) {
+ 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(previouslyIn || (click && this.lastFeature)) {
+ 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/Keyboard.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
+ * @requires OpenLayers/Events.js
+ */
+
+/**
+ * Class: OpenLayers.handler.Keyboard
+ * A handler for keyboard events. Create a new instance with the
+ * <OpenLayers.Handler.Keyboard> constructor.
+ *
+ * Inherits from:
+ * - <OpenLayers.Handler>
+ */
+OpenLayers.Handler.Keyboard = OpenLayers.Class(OpenLayers.Handler, {
+
+ /* http://www.quirksmode.org/js/keys.html explains key x-browser
+ key handling quirks in pretty nice detail */
+
+ /**
+ * Constant: KEY_EVENTS
+ * keydown, keypress, keyup
+ */
+ KEY_EVENTS: ["keydown", "keyup"],
+
+ /**
+ * Property: eventListener
+ * {Function}
+ */
+ eventListener: null,
+
+ /**
+ * Constructor: OpenLayers.Handler.Keyboard
+ * Returns a new keyboard handler.
+ *
+ * Parameters:
+ * control - {<OpenLayers.Control>} The control that is making use of
+ * this handler. If a handler is being used without a control, the
+ * handlers setMap method must be overridden to deal properly with
+ * the map.
+ * callbacks - {Object} An object containing a single function to be
+ * called when the drag operation is finished. The callback should
+ * expect to recieve a single argument, the pixel location of the event.
+ * Callbacks for 'keydown', 'keypress', and 'keyup' are supported.
+ * options - {Object} Optional object whose properties will be set on the
+ * handler.
+ */
+ initialize: function(control, callbacks, options) {
+ OpenLayers.Handler.prototype.initialize.apply(this, arguments);
+ // cache the bound event listener method so it can be unobserved later
+ this.eventListener = OpenLayers.Function.bindAsEventListener(
+ this.handleKeyEvent, this
+ );
+ },
+
+ /**
+ * Method: destroy
+ */
+ destroy: function() {
+ this.deactivate();
+ this.eventListener = null;
+ OpenLayers.Handler.prototype.destroy.apply(this, arguments);
+ },
+
+ /**
+ * Method: activate
+ */
+ activate: function() {
+ if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
+ for (var i=0, len=this.KEY_EVENTS.length; i<len; i++) {
+ OpenLayers.Event.observe(
+ document, this.KEY_EVENTS[i], this.eventListener);
+ }
+ return true;
+ } else {
+ return false;
+ }
+ },
+
+ /**
+ * Method: deactivate
+ */
+ deactivate: function() {
+ var deactivated = false;
+ if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
+ for (var i=0, len=this.KEY_EVENTS.length; i<len; i++) {
+ OpenLayers.Event.stopObserving(
+ document, this.KEY_EVENTS[i], this.eventListener);
+ }
+ deactivated = true;
+ }
+ return deactivated;
+ },
+
+ /**
+ * Method: handleKeyEvent
+ */
+ handleKeyEvent: function (evt) {
+ if (this.checkModifiers(evt)) {
+ this.callback(evt.type, [evt]);
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Handler.Keyboard"
+});
+/* ======================================================================
OpenLayers/Handler/MouseWheel.js
====================================================================== */
@@ -12016,7 +25039,7 @@
}
if (!overLayerDiv) {
- for(var i=0; i < this.map.layers.length; i++) {
+ for(var i=0, len=this.map.layers.length; i<len; i++) {
// Are we in the layer div? Note that we have two cases
// here: one is to catch EventPane layers, which have a
// pane above the layer (layer.pane)
@@ -12197,6 +25220,23 @@
opacity: null,
/**
+ * APIProperty: alwaysInRange
+ * {Boolean} If a layer's display should not be scale-based, this should
+ * be set to true. This will cause the layer, as an overlay, to always
+ * be 'active', by always returning true from the calculateInRange()
+ * function.
+ *
+ * If not explicitly specified for a layer, its value will be
+ * determined on startup in initResolutions() based on whether or not
+ * any scale-specific properties have been set as options on the
+ * layer. If no scale-specific options have been set on the layer, we
+ * assume that it should always be in range.
+ *
+ * See #987 for more info.
+ */
+ alwaysInRange: null,
+
+ /**
* Constant: EVENT_TYPES
* {Array(String)} Supported application event types. Register a listener
* for a particular event with the following syntax:
@@ -12216,8 +25256,12 @@
* - *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
+ * argument has a zoomChanged boolean property which tells that the
+ * zoom has changed.
*/
- EVENT_TYPES: ["loadstart", "loadend", "loadcancel", "visibilitychanged"],
+ EVENT_TYPES: ["loadstart", "loadend", "loadcancel", "visibilitychanged",
+ "moveend"],
/**
* APIProperty: events
@@ -12774,14 +25818,20 @@
*
* Returns:
* {Boolean} The layer is displayable at the current map's current
- * resolution.
+ * resolution. Note that if 'alwaysInRange' is true for the layer,
+ * this function will always return true.
*/
calculateInRange: function() {
var inRange = false;
- if (this.map) {
- var resolution = this.map.getResolution();
- inRange = ( (resolution >= this.minResolution) &&
- (resolution <= this.maxResolution) );
+
+ if (this.alwaysInRange) {
+ inRange = true;
+ } else {
+ if (this.map) {
+ var resolution = this.map.getResolution();
+ inRange = ( (resolution >= this.minResolution) &&
+ (resolution <= this.maxResolution) );
+ }
}
return inRange;
},
@@ -12836,27 +25886,60 @@
'numZoomLevels', 'maxZoomLevel'
);
+ //these are the properties which do *not* imply that user wishes
+ // this layer to be scale-dependant
+ var notScaleProps = ['projection', 'units'];
+
+ //should the layer be scale-dependant? default is false -- this will
+ // only be set true if we find that the user has specified a property
+ // from the 'props' array that is not in 'notScaleProps'
+ var useInRange = false;
+
// First we create a new object where we will store all of the
// resolution-related properties that we find in either the layer's
// 'options' array or from the map.
//
var confProps = {};
- for(var i=0; i < props.length; i++) {
+ for(var i=0, len=props.length; i<len; i++) {
var property = props[i];
+
+ // If the layer had one of these properties set *and* it is
+ // a scale property (is not a non-scale property), then we assume
+ // the user did intend to use scale-dependant display (useInRange).
+ if (this.options[property] &&
+ OpenLayers.Util.indexOf(notScaleProps, property) == -1) {
+ useInRange = true;
+ }
+
confProps[property] = this.options[property] || this.map[property];
}
- // Do not use the scales/resolutions at the map level if
- // minScale/minResolution and maxScale/maxResolution are
- // specified at the layer level
- if (this.options.minScale != null &&
- this.options.maxScale != null &&
+ //only automatically set 'alwaysInRange' if the user hasn't already
+ // set it (to true or false, since the default is null). If user did
+ // not intend to use scale-dependant display then we set they layer
+ // as alwaysInRange. This means calculateInRange() will always return
+ // true and the layer will never be turned off due to scale changes.
+ //
+ if (this.alwaysInRange == null) {
+ this.alwaysInRange = !useInRange;
+ }
+
+ // Do not use the scales array set at the map level if
+ // either minScale or maxScale or both are set at the
+ // layer level
+ if ((this.options.minScale != null ||
+ this.options.maxScale != null) &&
this.options.scales == null) {
+
confProps.scales = null;
}
- if (this.options.minResolution != null &&
- this.options.maxResolution != null &&
+ // Do not use the resolutions array set at the map level if
+ // either minResolution or maxResolution or both are set at the
+ // layer level
+ if ((this.options.minResolution != null ||
+ this.options.maxResolution != null) &&
this.options.resolutions == null) {
+
confProps.resolutions = null;
}
@@ -12874,7 +25957,7 @@
//preset levels
if (confProps.scales != null) {
confProps.resolutions = [];
- for(var i = 0; i < confProps.scales.length; i++) {
+ for(var i=0, len=confProps.scales.length; i<len; i++) {
var scale = confProps.scales[i];
confProps.resolutions[i] =
OpenLayers.Util.getResolutionFromScale(scale,
@@ -12960,7 +26043,7 @@
this.minResolution = confProps.resolutions[lastIndex];
this.scales = [];
- for(var i = 0; i < confProps.resolutions.length; i++) {
+ for(var i=0, len=confProps.resolutions.length; i<len; i++) {
this.scales[i] =
OpenLayers.Util.getScaleFromResolution(confProps.resolutions[i],
confProps.units);
@@ -13083,7 +26166,7 @@
var highRes = this.resolutions[lowZoom];
var lowRes = this.resolutions[highZoom];
var res;
- for(var i=0; i<this.resolutions.length; ++i) {
+ for(var i=0, len=this.resolutions.length; i<len; ++i) {
res = this.resolutions[i];
if(res >= resolution) {
highRes = res;
@@ -13104,7 +26187,7 @@
} else {
var diff;
var minDiff = Number.POSITIVE_INFINITY;
- for(var i=0; i < this.resolutions.length; i++) {
+ for(var i=0, len=this.resolutions.length; i<len; i++) {
if (closest) {
diff = Math.abs(this.resolutions[i] - resolution);
if (diff > minDiff) {
@@ -13189,7 +26272,7 @@
setOpacity: function(opacity) {
if (opacity != this.opacity) {
this.opacity = opacity;
- for(var i=0; i<this.div.childNodes.length; ++i) {
+ for(var i=0, len=this.div.childNodes.length; i<len; ++i) {
var element = this.div.childNodes[i].firstChild;
OpenLayers.Util.modifyDOMElement(element, null, null, null,
null, null, null, opacity);
@@ -13198,6 +26281,16 @@
},
/**
+ * Method: getZIndex
+ *
+ * Returns:
+ * {Integer} the z-index of this layer
+ */
+ getZIndex: function () {
+ return this.div.style.zIndex;
+ },
+
+ /**
* Method: setZIndex
*
* Parameters:
@@ -13367,6 +26460,542 @@
});
/* ======================================================================
+ OpenLayers/Popup/FramedCloud.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/Popup/Framed.js
+ * @requires OpenLayers/Util.js
+ */
+
+/**
+ * Class: OpenLayers.Popup.FramedCloud
+ *
+ * Inherits from:
+ * - <OpenLayers.Popup.Framed>
+ */
+OpenLayers.Popup.FramedCloud =
+ OpenLayers.Class(OpenLayers.Popup.Framed, {
+
+ /**
+ * Property: contentDisplayClass
+ * {String} The CSS class of the popup content div.
+ */
+ contentDisplayClass: "olFramedCloudPopupContent",
+
+ /**
+ * APIProperty: autoSize
+ * {Boolean} Framed Cloud is autosizing by default.
+ */
+ autoSize: true,
+
+ /**
+ * APIProperty: panMapIfOutOfView
+ * {Boolean} Framed Cloud does pan into view by default.
+ */
+ panMapIfOutOfView: true,
+
+ /**
+ * APIProperty: imageSize
+ * {<OpenLayers.Size>}
+ */
+ imageSize: new OpenLayers.Size(676, 736),
+
+ /**
+ * APIProperty: isAlphaImage
+ * {Boolean} The FramedCloud does not use an alpha image (in honor of the
+ * good ie6 folk out there)
+ */
+ isAlphaImage: false,
+
+ /**
+ * APIProperty: fixedRelativePosition
+ * {Boolean} The Framed Cloud popup works in just one fixed position.
+ */
+ fixedRelativePosition: false,
+
+ /**
+ * Property: positionBlocks
+ * {Object} Hash of differen position blocks, keyed by relativePosition
+ * two-character code string (ie "tl", "tr", "bl", "br")
+ */
+ positionBlocks: {
+ "tl": {
+ 'offset': new OpenLayers.Pixel(44, 0),
+ 'padding': new OpenLayers.Bounds(8, 40, 8, 9),
+ 'blocks': [
+ { // top-left
+ size: new OpenLayers.Size('auto', 'auto'),
+ anchor: new OpenLayers.Bounds(0, 51, 22, 0),
+ position: new OpenLayers.Pixel(0, 0)
+ },
+ { //top-right
+ size: new OpenLayers.Size(22, 'auto'),
+ anchor: new OpenLayers.Bounds(null, 50, 0, 0),
+ position: new OpenLayers.Pixel(-638, 0)
+ },
+ { //bottom-left
+ size: new OpenLayers.Size('auto', 21),
+ anchor: new OpenLayers.Bounds(0, 32, 80, null),
+ position: new OpenLayers.Pixel(0, -629)
+ },
+ { //bottom-right
+ size: new OpenLayers.Size(22, 21),
+ anchor: new OpenLayers.Bounds(null, 32, 0, null),
+ position: new OpenLayers.Pixel(-638, -629)
+ },
+ { // stem
+ size: new OpenLayers.Size(81, 54),
+ anchor: new OpenLayers.Bounds(null, 0, 0, null),
+ position: new OpenLayers.Pixel(0, -668)
+ }
+ ]
+ },
+ "tr": {
+ 'offset': new OpenLayers.Pixel(-45, 0),
+ 'padding': new OpenLayers.Bounds(8, 40, 8, 9),
+ 'blocks': [
+ { // top-left
+ size: new OpenLayers.Size('auto', 'auto'),
+ anchor: new OpenLayers.Bounds(0, 51, 22, 0),
+ position: new OpenLayers.Pixel(0, 0)
+ },
+ { //top-right
+ size: new OpenLayers.Size(22, 'auto'),
+ anchor: new OpenLayers.Bounds(null, 50, 0, 0),
+ position: new OpenLayers.Pixel(-638, 0)
+ },
+ { //bottom-left
+ size: new OpenLayers.Size('auto', 21),
+ anchor: new OpenLayers.Bounds(0, 32, 22, null),
+ position: new OpenLayers.Pixel(0, -629)
+ },
+ { //bottom-right
+ size: new OpenLayers.Size(22, 21),
+ anchor: new OpenLayers.Bounds(null, 32, 0, null),
+ position: new OpenLayers.Pixel(-638, -629)
+ },
+ { // stem
+ size: new OpenLayers.Size(81, 54),
+ anchor: new OpenLayers.Bounds(0, 0, null, null),
+ position: new OpenLayers.Pixel(-215, -668)
+ }
+ ]
+ },
+ "bl": {
+ 'offset': new OpenLayers.Pixel(45, 0),
+ 'padding': new OpenLayers.Bounds(8, 9, 8, 40),
+ 'blocks': [
+ { // top-left
+ size: new OpenLayers.Size('auto', 'auto'),
+ anchor: new OpenLayers.Bounds(0, 21, 22, 32),
+ position: new OpenLayers.Pixel(0, 0)
+ },
+ { //top-right
+ size: new OpenLayers.Size(22, 'auto'),
+ anchor: new OpenLayers.Bounds(null, 21, 0, 32),
+ position: new OpenLayers.Pixel(-638, 0)
+ },
+ { //bottom-left
+ size: new OpenLayers.Size('auto', 21),
+ anchor: new OpenLayers.Bounds(0, 0, 22, null),
+ position: new OpenLayers.Pixel(0, -629)
+ },
+ { //bottom-right
+ size: new OpenLayers.Size(22, 21),
+ anchor: new OpenLayers.Bounds(null, 0, 0, null),
+ position: new OpenLayers.Pixel(-638, -629)
+ },
+ { // stem
+ size: new OpenLayers.Size(81, 54),
+ anchor: new OpenLayers.Bounds(null, null, 0, 0),
+ position: new OpenLayers.Pixel(-101, -674)
+ }
+ ]
+ },
+ "br": {
+ 'offset': new OpenLayers.Pixel(-44, 0),
+ 'padding': new OpenLayers.Bounds(8, 9, 8, 40),
+ 'blocks': [
+ { // top-left
+ size: new OpenLayers.Size('auto', 'auto'),
+ anchor: new OpenLayers.Bounds(0, 21, 22, 32),
+ position: new OpenLayers.Pixel(0, 0)
+ },
+ { //top-right
+ size: new OpenLayers.Size(22, 'auto'),
+ anchor: new OpenLayers.Bounds(null, 21, 0, 32),
+ position: new OpenLayers.Pixel(-638, 0)
+ },
+ { //bottom-left
+ size: new OpenLayers.Size('auto', 21),
+ anchor: new OpenLayers.Bounds(0, 0, 22, null),
+ position: new OpenLayers.Pixel(0, -629)
+ },
+ { //bottom-right
+ size: new OpenLayers.Size(22, 21),
+ anchor: new OpenLayers.Bounds(null, 0, 0, null),
+ position: new OpenLayers.Pixel(-638, -629)
+ },
+ { // stem
+ size: new OpenLayers.Size(81, 54),
+ anchor: new OpenLayers.Bounds(0, null, null, 0),
+ position: new OpenLayers.Pixel(-311, -674)
+ }
+ ]
+ }
+ },
+
+ /**
+ * APIProperty: minSize
+ * {<OpenLayers.Size>}
+ */
+ minSize: new OpenLayers.Size(105, 10),
+
+ /**
+ * APIProperty: maxSize
+ * {<OpenLayers.Size>}
+ */
+ maxSize: new OpenLayers.Size(600, 660),
+
+ /**
+ * Constructor: OpenLayers.Popup.FramedCloud
+ *
+ * Parameters:
+ * id - {String}
+ * lonlat - {<OpenLayers.LonLat>}
+ * contentSize - {<OpenLayers.Size>}
+ * contentHTML - {String}
+ * anchor - {Object} Object to which we'll anchor the popup. Must expose
+ * a 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>)
+ * (Note that this is generally an <OpenLayers.Icon>).
+ * closeBox - {Boolean}
+ * closeBoxCallback - {Function} Function to be called on closeBox click.
+ */
+ initialize:function(id, lonlat, contentSize, contentHTML, anchor, closeBox,
+ closeBoxCallback) {
+
+ this.imageSrc = OpenLayers.Util.getImagesLocation() + 'cloud-popup-relative.png';
+ OpenLayers.Popup.Framed.prototype.initialize.apply(this, arguments);
+ this.contentDiv.className = this.contentDisplayClass;
+ },
+
+ /**
+ * APIMethod: destroy
+ */
+ destroy: function() {
+ OpenLayers.Popup.Framed.prototype.destroy.apply(this, arguments);
+ },
+
+ CLASS_NAME: "OpenLayers.Popup.FramedCloud"
+});
+/* ======================================================================
+ OpenLayers/Control/DragFeature.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/Handler/Drag.js
+ * @requires OpenLayers/Handler/Feature.js
+ */
+
+/**
+ * Class: OpenLayers.Control.DragFeature
+ * Move a feature with a drag. Create a new control with the
+ * <OpenLayers.Control.DragFeature> constructor.
+ *
+ * Inherits From:
+ * - <OpenLayers.Control>
+ */
+OpenLayers.Control.DragFeature = OpenLayers.Class(OpenLayers.Control, {
+
+ /**
+ * APIProperty: geometryTypes
+ * {Array(String)} To restrict dragging to a limited set of geometry types,
+ * send a list of strings corresponding to the geometry class names.
+ */
+ geometryTypes: null,
+
+ /**
+ * APIProperty: onStart
+ * {Function} Define this function if you want to know when a drag starts.
+ * The function should expect to receive two arguments: the feature
+ * that is about to be dragged and the pixel location of the mouse.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>} The feature that is about to be
+ * dragged.
+ * pixel - {<OpenLayers.Pixel>} The pixel location of the mouse.
+ */
+ onStart: function(feature, pixel) {},
+
+ /**
+ * APIProperty: onDrag
+ * {Function} Define this function if you want to know about each move of a
+ * feature. The function should expect to receive two arguments: the
+ * feature that is being dragged and the pixel location of the mouse.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>} The feature that was dragged.
+ * pixel - {<OpenLayers.Pixel>} The pixel location of the mouse.
+ */
+ onDrag: function(feature, pixel) {},
+
+ /**
+ * APIProperty: onComplete
+ * {Function} Define this function if you want to know when a feature is
+ * done dragging. The function should expect to receive two arguments:
+ * the feature that is being dragged and the pixel location of the
+ * mouse.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>} The feature that was dragged.
+ * pixel - {<OpenLayers.Pixel>} The pixel location of the mouse.
+ */
+ onComplete: function(feature, pixel) {},
+
+ /**
+ * Property: layer
+ * {<OpenLayers.Layer.Vector>}
+ */
+ layer: null,
+
+ /**
+ * Property: feature
+ * {<OpenLayers.Feature.Vector>}
+ */
+ feature: null,
+
+ /**
+ * Property: dragCallbacks
+ * {Object} The functions that are sent to the drag handler for callback.
+ */
+ dragCallbacks: {},
+
+ /**
+ * Property: featureCallbacks
+ * {Object} The functions that are sent to the feature handler for callback.
+ */
+ featureCallbacks: {},
+
+ /**
+ * Property: lastPixel
+ * {<OpenLayers.Pixel>}
+ */
+ lastPixel: null,
+
+ /**
+ * Constructor: OpenLayers.Control.DragFeature
+ * Create a new control to drag features.
+ *
+ * Parameters:
+ * layer - {<OpenLayers.Layer.Vector>} The layer containing features to be
+ * dragged.
+ * options - {Object} Optional object whose properties will be set on the
+ * control.
+ */
+ initialize: function(layer, options) {
+ OpenLayers.Control.prototype.initialize.apply(this, [options]);
+ this.layer = layer;
+ this.handlers = {
+ drag: new OpenLayers.Handler.Drag(
+ this, OpenLayers.Util.extend({
+ down: this.downFeature,
+ move: this.moveFeature,
+ up: this.upFeature,
+ out: this.cancel,
+ done: this.doneDragging
+ }, this.dragCallbacks)
+ ),
+ feature: new OpenLayers.Handler.Feature(
+ this, this.layer, OpenLayers.Util.extend({
+ over: this.overFeature,
+ out: this.outFeature
+ }, this.featureCallbacks),
+ {geometryTypes: this.geometryTypes}
+ )
+ };
+ },
+
+ /**
+ * APIMethod: destroy
+ * Take care of things that are not handled in superclass
+ */
+ destroy: function() {
+ this.layer = null;
+ OpenLayers.Control.prototype.destroy.apply(this, []);
+ },
+
+ /**
+ * APIMethod: activate
+ * Activate the control and the feature handler.
+ *
+ * Returns:
+ * {Boolean} Successfully activated the control and feature handler.
+ */
+ activate: function() {
+ return (this.handlers.feature.activate() &&
+ OpenLayers.Control.prototype.activate.apply(this, arguments));
+ },
+
+ /**
+ * APIMethod: deactivate
+ * Deactivate the control and all handlers.
+ *
+ * Returns:
+ * {Boolean} Successfully deactivated the control.
+ */
+ deactivate: function() {
+ // the return from the handlers is unimportant in this case
+ this.handlers.drag.deactivate();
+ this.handlers.feature.deactivate();
+ this.feature = null;
+ this.dragging = false;
+ this.lastPixel = null;
+ return OpenLayers.Control.prototype.deactivate.apply(this, arguments);
+ },
+
+ /**
+ * Method: overFeature
+ * Called when the feature handler detects a mouse-over on a feature.
+ * This activates the drag handler.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>} The selected feature.
+ */
+ overFeature: function(feature) {
+ if(!this.handlers.drag.dragging) {
+ this.feature = feature;
+ this.handlers.drag.activate();
+ this.over = true;
+ // TBD replace with CSS classes
+ this.map.div.style.cursor = "move";
+ } else {
+ if(this.feature.id == feature.id) {
+ this.over = true;
+ } else {
+ this.over = false;
+ }
+ }
+ },
+
+ /**
+ * Method: downFeature
+ * Called when the drag handler detects a mouse-down.
+ *
+ * Parameters:
+ * pixel - {<OpenLayers.Pixel>} Location of the mouse event.
+ */
+ downFeature: function(pixel) {
+ this.lastPixel = pixel;
+ this.onStart(this.feature, pixel);
+ },
+
+ /**
+ * Method: moveFeature
+ * Called when the drag handler detects a mouse-move. Also calls the
+ * optional onDrag method.
+ *
+ * Parameters:
+ * pixel - {<OpenLayers.Pixel>} Location of the mouse event.
+ */
+ moveFeature: function(pixel) {
+ var res = this.map.getResolution();
+ this.feature.geometry.move(res * (pixel.x - this.lastPixel.x),
+ res * (this.lastPixel.y - pixel.y));
+ this.layer.drawFeature(this.feature);
+ this.lastPixel = pixel;
+ this.onDrag(this.feature, pixel);
+ },
+
+ /**
+ * Method: upFeature
+ * Called when the drag handler detects a mouse-up. Also calls the
+ * optional onComplete method.
+ *
+ * Parameters:
+ * pixel - {<OpenLayers.Pixel>} Location of the mouse event.
+ */
+ upFeature: function(pixel) {
+ if(!this.over) {
+ this.handlers.drag.deactivate();
+ this.feature = null;
+ // TBD replace with CSS classes
+ this.map.div.style.cursor = "default";
+ } else {
+ // the drag handler itself resetted the cursor, so
+ // set it back to "move" here
+ this.map.div.style.cursor = "move";
+ }
+ },
+
+ /**
+ * Method: doneDragging
+ * Called when the drag handler is done dragging.
+ *
+ * Parameters:
+ * pixel - {<OpenLayers.Pixel>} The last event pixel location. If this event
+ * came from a mouseout, this may not be in the map viewport.
+ */
+ doneDragging: function(pixel) {
+ this.onComplete(this.feature, pixel);
+ },
+
+ /**
+ * Method: outFeature
+ * Called when the feature handler detects a mouse-out on a feature.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>} The feature that the mouse left.
+ */
+ outFeature: function(feature) {
+ if(!this.handlers.drag.dragging) {
+ this.over = false;
+ this.handlers.drag.deactivate();
+ // TBD replace with CSS classes
+ this.map.div.style.cursor = "default";
+ this.feature = null;
+ } else {
+ if(this.feature.id == feature.id) {
+ this.over = false;
+ }
+ }
+ },
+
+ /**
+ * Method: cancel
+ * Called when the drag handler detects a mouse-out (from the map viewport).
+ */
+ cancel: function() {
+ this.handlers.drag.deactivate();
+ this.over = false;
+ },
+
+ /**
+ * Method: setMap
+ * Set the map property for the control and all handlers.
+ *
+ * Parameters:
+ * map - {<OpenLayers.Map>} The control's map.
+ */
+ setMap: function(map) {
+ this.handlers.drag.setMap(map);
+ this.handlers.feature.setMap(map);
+ OpenLayers.Control.prototype.setMap.apply(this, arguments);
+ },
+
+ CLASS_NAME: "OpenLayers.Control.DragFeature"
+});
+/* ======================================================================
OpenLayers/Control/DragPan.js
====================================================================== */
@@ -13401,13 +27030,26 @@
panned: false,
/**
+ * Property: interval
+ * {Integer} The number of milliseconds that should ellapse before
+ * panning the map again. Set this to increase dragging performance.
+ * Defaults to 25 milliseconds.
+ */
+ interval: 25,
+
+ /**
* Method: draw
* Creates a Drag handler, using <panMap> and
* <panMapDone> as callbacks.
*/
draw: function() {
- this.handler = new OpenLayers.Handler.Drag(this,
- {"move": this.panMap, "done": this.panMapDone});
+ this.handler = new OpenLayers.Handler.Drag(this, {
+ "move": this.panMap,
+ "done": this.panMapDone
+ }, {
+ interval: this.interval
+ }
+ );
},
/**
@@ -13443,6 +27085,777 @@
CLASS_NAME: "OpenLayers.Control.DragPan"
});
/* ======================================================================
+ OpenLayers/Control/KeyboardDefaults.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/Handler/Keyboard.js
+ */
+
+/**
+ * Class: OpenLayers.Control.KeyboardDefaults
+ *
+ * Inherits from:
+ * - <OpenLayers.Control>
+ */
+OpenLayers.Control.KeyboardDefaults = OpenLayers.Class(OpenLayers.Control, {
+
+ /**
+ * APIProperty: slideFactor
+ * Pixels to slide by.
+ */
+ slideFactor: 75,
+
+ /**
+ * Constructor: OpenLayers.Control.KeyboardDefaults
+ */
+ initialize: function() {
+ OpenLayers.Control.prototype.initialize.apply(this, arguments);
+ },
+
+ /**
+ * APIMethod: destroy
+ */
+ destroy: function() {
+ if (this.handler) {
+ this.handler.destroy();
+ }
+ this.handler = null;
+
+ OpenLayers.Control.prototype.destroy.apply(this, arguments);
+ },
+
+ /**
+ * Method: draw
+ * Create handler.
+ */
+ draw: function() {
+ this.handler = new OpenLayers.Handler.Keyboard( this, {
+ "keydown": this.defaultKeyPress });
+ this.activate();
+ },
+
+ /**
+ * Method: defaultKeyPress
+ * When handling the key event, we only use evt.keyCode. This holds
+ * some drawbacks, though we get around them below. When interpretting
+ * the keycodes below (including the comments associated with them),
+ * consult the URL below. For instance, the Safari browser returns
+ * "IE keycodes", and so is supported by any keycode labeled "IE".
+ *
+ * Very informative URL:
+ * http://unixpapa.com/js/key.html
+ *
+ * Parameters:
+ * code - {Integer}
+ */
+ defaultKeyPress: function (evt) {
+ switch(evt.keyCode) {
+ case OpenLayers.Event.KEY_LEFT:
+ this.map.pan(-this.slideFactor, 0);
+ break;
+ case OpenLayers.Event.KEY_RIGHT:
+ this.map.pan(this.slideFactor, 0);
+ break;
+ case OpenLayers.Event.KEY_UP:
+ this.map.pan(0, -this.slideFactor);
+ break;
+ case OpenLayers.Event.KEY_DOWN:
+ this.map.pan(0, this.slideFactor);
+ break;
+
+ case 33: // Page Up. Same in all browsers.
+ var size = this.map.getSize();
+ this.map.pan(0, -0.75*size.h);
+ break;
+ case 34: // Page Down. Same in all browsers.
+ var size = this.map.getSize();
+ this.map.pan(0, 0.75*size.h);
+ break;
+ case 35: // End. Same in all browsers.
+ var size = this.map.getSize();
+ this.map.pan(0.75*size.w, 0);
+ break;
+ case 36: // Home. Same in all browsers.
+ var size = this.map.getSize();
+ this.map.pan(-0.75*size.w, 0);
+ break;
+
+ case 43: // +/= (ASCII), keypad + (ASCII, Opera)
+ case 61: // +/= (Mozilla, Opera, some ASCII)
+ case 187: // +/= (IE)
+ case 107: // keypad + (IE, Mozilla)
+ this.map.zoomIn();
+ break;
+ case 45: // -/_ (ASCII, Opera), keypad - (ASCII, Opera)
+ case 109: // -/_ (Mozilla), keypad - (Mozilla, IE)
+ case 189: // -/_ (IE)
+ case 95: // -/_ (some ASCII)
+ this.map.zoomOut();
+ break;
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Control.KeyboardDefaults"
+});
+/* ======================================================================
+ OpenLayers/Feature/Vector.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. */
+
+// TRASH THIS
+OpenLayers.State = {
+ /** states */
+ UNKNOWN: 'Unknown',
+ INSERT: 'Insert',
+ UPDATE: 'Update',
+ DELETE: 'Delete'
+};
+
+/**
+ * @requires OpenLayers/Feature.js
+ * @requires OpenLayers/Util.js
+ */
+
+/**
+ * Class: OpenLayers.Feature.Vector
+ * Vector features use the OpenLayers.Geometry classes as geometry description.
+ * They have an 'attributes' property, which is the data object, and a 'style'
+ * property, the default values of which are defined in the
+ * <OpenLayers.Feature.Vector.style> objects.
+ *
+ * Inherits from:
+ * - <OpenLayers.Feature>
+ */
+OpenLayers.Feature.Vector = OpenLayers.Class(OpenLayers.Feature, {
+
+ /**
+ * Property: fid
+ * {String}
+ */
+ fid: null,
+
+ /**
+ * APIProperty: geometry
+ * {<OpenLayers.Geometry>}
+ */
+ geometry: null,
+
+ /**
+ * APIProperty: attributes
+ * {Object} This object holds arbitrary properties that describe the
+ * feature.
+ */
+ attributes: null,
+
+ /**
+ * Property: state
+ * {String}
+ */
+ state: null,
+
+ /**
+ * APIProperty: style
+ * {Object}
+ */
+ style: null,
+
+ /**
+ * Property: renderIntent
+ * {String} rendering intent currently being used
+ */
+ renderIntent: "default",
+
+ /**
+ * Constructor: OpenLayers.Feature.Vector
+ * Create a vector feature.
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry>} The geometry that this feature
+ * represents.
+ * attributes - {Object} An optional object that will be mapped to the
+ * <attributes> property.
+ * style - {Object} An optional style object.
+ */
+ initialize: function(geometry, attributes, style) {
+ OpenLayers.Feature.prototype.initialize.apply(this,
+ [null, null, attributes]);
+ this.lonlat = null;
+ this.geometry = geometry ? geometry : null;
+ this.state = null;
+ this.attributes = {};
+ if (attributes) {
+ this.attributes = OpenLayers.Util.extend(this.attributes,
+ attributes);
+ }
+ this.style = style ? style : null;
+ },
+
+ /**
+ * Method: destroy
+ * nullify references to prevent circular references and memory leaks
+ */
+ destroy: function() {
+ if (this.layer) {
+ this.layer.removeFeatures(this);
+ this.layer = null;
+ }
+
+ this.geometry = null;
+ OpenLayers.Feature.prototype.destroy.apply(this, arguments);
+ },
+
+ /**
+ * Method: clone
+ * Create a clone of this vector feature. Does not set any non-standard
+ * properties.
+ *
+ * Returns:
+ * {<OpenLayers.Feature.Vector>} An exact clone of this vector feature.
+ */
+ clone: function () {
+ return new OpenLayers.Feature.Vector(
+ this.geometry ? this.geometry.clone() : null,
+ this.attributes,
+ this.style);
+ },
+
+ /**
+ * Method: onScreen
+ * Determine whether the feature is within the map viewport. This method
+ * tests for an intersection between the geometry and the viewport
+ * bounds. If a more effecient but less precise geometry bounds
+ * intersection is desired, call the method with the boundsOnly
+ * parameter true.
+ *
+ * Parameters:
+ * boundsOnly - {Boolean} Only test whether a feature's bounds intersects
+ * the viewport bounds. Default is false. If false, the feature's
+ * geometry must intersect the viewport for onScreen to return true.
+ *
+ * Returns:
+ * {Boolean} The feature is currently visible on screen (optionally
+ * based on its bounds if boundsOnly is true).
+ */
+ onScreen:function(boundsOnly) {
+ var onScreen = false;
+ if(this.layer && this.layer.map) {
+ var screenBounds = this.layer.map.getExtent();
+ if(boundsOnly) {
+ var featureBounds = this.geometry.getBounds();
+ onScreen = screenBounds.intersectsBounds(featureBounds);
+ } else {
+ var screenPoly = screenBounds.toGeometry();
+ onScreen = screenPoly.intersects(this.geometry);
+ }
+ }
+ return onScreen;
+ },
+
+ /**
+ * Method: createMarker
+ * HACK - we need to decide if all vector features should be able to
+ * create markers
+ *
+ * Returns:
+ * {<OpenLayers.Marker>} For now just returns null
+ */
+ createMarker: function() {
+ return null;
+ },
+
+ /**
+ * Method: destroyMarker
+ * HACK - we need to decide if all vector features should be able to
+ * delete markers
+ *
+ * If user overrides the createMarker() function, s/he should be able
+ * to also specify an alternative function for destroying it
+ */
+ destroyMarker: function() {
+ // pass
+ },
+
+ /**
+ * Method: createPopup
+ * HACK - we need to decide if all vector features should be able to
+ * create popups
+ *
+ * Returns:
+ * {<OpenLayers.Popup>} For now just returns null
+ */
+ createPopup: function() {
+ return null;
+ },
+
+ /**
+ * Method: atPoint
+ * Determins whether the feature intersects with the specified location.
+ *
+ * Parameters:
+ * lonlat - {<OpenLayers.LonLat>}
+ * toleranceLon - {float} Optional tolerance in Geometric Coords
+ * toleranceLat - {float} Optional tolerance in Geographic Coords
+ *
+ * Returns:
+ * {Boolean} Whether or not the feature is at the specified location
+ */
+ atPoint: function(lonlat, toleranceLon, toleranceLat) {
+ var atPoint = false;
+ if(this.geometry) {
+ atPoint = this.geometry.atPoint(lonlat, toleranceLon,
+ toleranceLat);
+ }
+ return atPoint;
+ },
+
+ /**
+ * Method: destroyPopup
+ * HACK - we need to decide if all vector features should be able to
+ * delete popups
+ */
+ destroyPopup: function() {
+ // pass
+ },
+
+ /**
+ * Method: move
+ * Moves the feature and redraws it at its new location
+ *
+ * Parameters:
+ * state - {OpenLayers.LonLat or OpenLayers.Pixel} the
+ * location to which to move the feature.
+ */
+ move: function(location) {
+
+ if(!this.layer || !this.geometry.move){
+ //do nothing if no layer or immoveable geometry
+ return;
+ }
+
+ var pixel;
+ if (location.CLASS_NAME == "OpenLayers.LonLat") {
+ pixel = this.layer.getViewPortPxFromLonLat(location);
+ } else {
+ pixel = location;
+ }
+
+ var lastPixel = this.layer.getViewPortPxFromLonLat(this.geometry.getBounds().getCenterLonLat());
+ var res = this.layer.map.getResolution();
+ this.geometry.move(res * (pixel.x - lastPixel.x),
+ res * (lastPixel.y - pixel.y));
+ this.layer.drawFeature(this);
+ return lastPixel;
+ },
+
+ /**
+ * Method: toState
+ * Sets the new state
+ *
+ * Parameters:
+ * state - {String}
+ */
+ toState: function(state) {
+ if (state == OpenLayers.State.UPDATE) {
+ switch (this.state) {
+ case OpenLayers.State.UNKNOWN:
+ case OpenLayers.State.DELETE:
+ this.state = state;
+ break;
+ case OpenLayers.State.UPDATE:
+ case OpenLayers.State.INSERT:
+ break;
+ }
+ } else if (state == OpenLayers.State.INSERT) {
+ switch (this.state) {
+ case OpenLayers.State.UNKNOWN:
+ break;
+ default:
+ this.state = state;
+ break;
+ }
+ } else if (state == OpenLayers.State.DELETE) {
+ switch (this.state) {
+ case OpenLayers.State.INSERT:
+ // the feature should be destroyed
+ break;
+ case OpenLayers.State.DELETE:
+ break;
+ case OpenLayers.State.UNKNOWN:
+ case OpenLayers.State.UPDATE:
+ this.state = state;
+ break;
+ }
+ } else if (state == OpenLayers.State.UNKNOWN) {
+ this.state = state;
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Feature.Vector"
+});
+
+
+/**
+ * Constant: OpenLayers.Feature.Vector.style
+ * OpenLayers features can have a number of style attributes. The 'default'
+ * style will typically be used if no other style is specified.
+ *
+ * Default style properties:
+ *
+ * - fillColor: "#ee9900",
+ * - fillOpacity: 0.4,
+ * - hoverFillColor: "white",
+ * - hoverFillOpacity: 0.8,
+ * - strokeColor: "#ee9900",
+ * - strokeOpacity: 1,
+ * - strokeWidth: 1,
+ * - strokeLinecap: "round", [butt | round | square]
+ * - strokeDashstyle: "solid", [dot | dash | dashdot | longdash | longdashdot | solid]
+ * - hoverStrokeColor: "red",
+ * - hoverStrokeOpacity: 1,
+ * - hoverStrokeWidth: 0.2,
+ * - pointRadius: 6,
+ * - hoverPointRadius: 1,
+ * - hoverPointUnit: "%",
+ * - pointerEvents: "visiblePainted"
+ * - cursor: ""
+ *
+ * Other style properties that have no default values:
+ *
+ * - externalGraphic,
+ * - graphicWidth,
+ * - graphicHeight,
+ * - graphicOpacity,
+ * - graphicXOffset,
+ * - graphicYOffset,
+ * - graphicName,
+ * - display
+ */
+OpenLayers.Feature.Vector.style = {
+ 'default': {
+ fillColor: "#ee9900",
+ fillOpacity: 0.4,
+ hoverFillColor: "white",
+ hoverFillOpacity: 0.8,
+ strokeColor: "#ee9900",
+ strokeOpacity: 1,
+ strokeWidth: 1,
+ strokeLinecap: "round",
+ strokeDashstyle: "solid",
+ hoverStrokeColor: "red",
+ hoverStrokeOpacity: 1,
+ hoverStrokeWidth: 0.2,
+ pointRadius: 6,
+ hoverPointRadius: 1,
+ hoverPointUnit: "%",
+ pointerEvents: "visiblePainted",
+ cursor: "inherit"
+ },
+ 'select': {
+ fillColor: "blue",
+ fillOpacity: 0.4,
+ hoverFillColor: "white",
+ hoverFillOpacity: 0.8,
+ strokeColor: "blue",
+ strokeOpacity: 1,
+ strokeWidth: 2,
+ strokeLinecap: "round",
+ strokeDashstyle: "solid",
+ hoverStrokeColor: "red",
+ hoverStrokeOpacity: 1,
+ hoverStrokeWidth: 0.2,
+ pointRadius: 6,
+ hoverPointRadius: 1,
+ hoverPointUnit: "%",
+ pointerEvents: "visiblePainted",
+ cursor: "pointer"
+ },
+ 'temporary': {
+ fillColor: "yellow",
+ fillOpacity: 0.2,
+ hoverFillColor: "white",
+ hoverFillOpacity: 0.8,
+ strokeColor: "yellow",
+ strokeOpacity: 1,
+ strokeLinecap: "round",
+ strokeWidth: 4,
+ strokeDashstyle: "solid",
+ hoverStrokeColor: "red",
+ hoverStrokeOpacity: 1,
+ hoverStrokeWidth: 0.2,
+ pointRadius: 6,
+ hoverPointRadius: 1,
+ hoverPointUnit: "%",
+ pointerEvents: "visiblePainted",
+ cursor: "inherit"
+ }
+};
+/* ======================================================================
+ OpenLayers/Feature/WFS.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/Feature.js
+ */
+
+/**
+ * Class: OpenLayers.Feature.WFS
+ * WFS handling class, for use as a featureClass on the WFS layer for handling
+ * 'point' WFS types. Good for subclassing when creating a custom WFS like
+ * XML application.
+ *
+ * Inherits from:
+ * - <OpenLayers.Feature>
+ */
+OpenLayers.Feature.WFS = OpenLayers.Class(OpenLayers.Feature, {
+
+ /**
+ * Constructor: OpenLayers.Feature.WFS
+ * Create a WFS feature.
+ *
+ * Parameters:
+ * layer - {<OpenLayers.Layer>}
+ * xmlNode - {XMLNode}
+ */
+ initialize: function(layer, xmlNode) {
+ var newArguments = arguments;
+ var data = this.processXMLNode(xmlNode);
+ newArguments = new Array(layer, data.lonlat, data);
+ OpenLayers.Feature.prototype.initialize.apply(this, newArguments);
+ this.createMarker();
+ this.layer.addMarker(this.marker);
+ },
+
+ /**
+ * Method: destroy
+ * nullify references to prevent circular references and memory leaks
+ */
+ destroy: function() {
+ if (this.marker != null) {
+ this.layer.removeMarker(this.marker);
+ }
+ OpenLayers.Feature.prototype.destroy.apply(this, arguments);
+ },
+
+ /**
+ * Method: processXMLNode
+ * When passed an xmlNode, parses it for a GML point, and passes
+ * back an object describing that point.
+ *
+ * For subclasses of Feature.WFS, this is the feature to change.
+ *
+ * Parameters:
+ * xmlNode - {XMLNode}
+ *
+ * Returns:
+ * {Object} Data Object with 'id', 'lonlat', and private properties set
+ */
+ processXMLNode: function(xmlNode) {
+ //this should be overridden by subclasses
+ // must return an Object with 'id' and 'lonlat' values set
+ var point = OpenLayers.Ajax.getElementsByTagNameNS(xmlNode, "http://www.opengis.net/gml", "gml", "Point");
+ var text = OpenLayers.Util.getXmlNodeValue(OpenLayers.Ajax.getElementsByTagNameNS(point[0], "http://www.opengis.net/gml","gml", "coordinates")[0]);
+ var floats = text.split(",");
+ return {lonlat: new OpenLayers.LonLat(parseFloat(floats[0]),
+ parseFloat(floats[1])),
+ id: null};
+
+ },
+
+ CLASS_NAME: "OpenLayers.Feature.WFS"
+});
+
+
+
+
+
+/* ======================================================================
+ OpenLayers/Format/WMC/v1_0_0.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/Format/WMC/v1.js
+ */
+
+/**
+ * Class: OpenLayers.Format.WMC.v1_0_0
+ * Read and write WMC version 1.0.0.
+ *
+ * Inherits from:
+ * - <OpenLayers.Format.WMC.v1>
+ */
+OpenLayers.Format.WMC.v1_0_0 = OpenLayers.Class(
+ OpenLayers.Format.WMC.v1, {
+
+ /**
+ * Constant: VERSION
+ * {String} 1.0.0
+ */
+ VERSION: "1.0.0",
+
+ /**
+ * Property: schemaLocation
+ * {String} http://www.opengis.net/context
+ * http://schemas.opengis.net/context/1.0.0/context.xsd
+ */
+ schemaLocation: "http://www.opengis.net/context http://schemas.opengis.net/context/1.0.0/context.xsd",
+
+ /**
+ * Constructor: OpenLayers.Format.WMC.v1_0_0
+ * Instances of this class are not created directly. Use the
+ * <OpenLayers.Format.WMC> constructor instead.
+ *
+ * Parameters:
+ * options - {Object} An optional object whose properties will be set on
+ * this instance.
+ */
+ initialize: function(options) {
+ OpenLayers.Format.WMC.v1.prototype.initialize.apply(
+ this, [options]
+ );
+ },
+
+ CLASS_NAME: "OpenLayers.Format.WMC.v1_0_0"
+
+});
+/* ======================================================================
+ OpenLayers/Format/WMC/v1_1_0.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/Format/WMC/v1.js
+ */
+
+/**
+ * Class: OpenLayers.Format.WMC.v1_1_0
+ * Read and write WMC version 1.1.0.
+ *
+ * Differences between 1.1.0 and 1.0.0:
+ * - 1.1.0 Layers have optional sld:MinScaleDenominator and
+ * sld:MaxScaleDenominator
+ *
+ * Inherits from:
+ * - <OpenLayers.Format.WMC.v1>
+ */
+OpenLayers.Format.WMC.v1_1_0 = OpenLayers.Class(
+ OpenLayers.Format.WMC.v1, {
+
+ /**
+ * Constant: VERSION
+ * {String} 1.1.0
+ */
+ VERSION: "1.1.0",
+
+ /**
+ * Property: schemaLocation
+ * {String} http://www.opengis.net/context
+ * http://schemas.opengis.net/context/1.1.0/context.xsd
+ */
+ schemaLocation: "http://www.opengis.net/context http://schemas.opengis.net/context/1.1.0/context.xsd",
+
+ /**
+ * Constructor: OpenLayers.Format.WMC.v1_1_0
+ * Instances of this class are not created directly. Use the
+ * <OpenLayers.Format.WMC> constructor instead.
+ *
+ * Parameters:
+ * options - {Object} An optional object whose properties will be set on
+ * this instance.
+ */
+ initialize: function(options) {
+ OpenLayers.Format.WMC.v1.prototype.initialize.apply(
+ this, [options]
+ );
+ },
+
+ /**
+ * Method: read_sld_MinScaleDenominator
+ * Read a sld:MinScaleDenominator node.
+ *
+ * Parameters:
+ * layerInfo - {Object} An object representing a layer.
+ * node - {Element} An element node.
+ */
+ read_sld_MinScaleDenominator: function(layerInfo, node) {
+ layerInfo.options.maxScale = this.getChildValue(node);
+ },
+
+ /**
+ * Method: read_sld_MaxScaleDenominator
+ * Read a sld:MaxScaleDenominator node.
+ *
+ * Parameters:
+ * layerInfo - {Object} An object representing a layer.
+ * node - {Element} An element node.
+ */
+ read_sld_MaxScaleDenominator: function(layerInfo, node) {
+ layerInfo.options.minScale = this.getChildValue(node);
+ },
+
+ /**
+ * Method: write_wmc_Layer
+ * Create a Layer node given a layer object. This method adds elements
+ * specific to version 1.1.0.
+ *
+ * Parameters:
+ * layer - {<OpenLayers.Layer.WMS>} Layer object.
+ *
+ * Returns:
+ * {Element} A WMC Layer element node.
+ */
+ write_wmc_Layer: function(layer) {
+ var node = OpenLayers.Format.WMC.v1.prototype.write_wmc_Layer.apply(
+ this, [layer]
+ );
+
+ // min/max scale denominator elements go before the 4th element in v1
+ if(layer.options.resolutions || layer.options.scales ||
+ layer.options.minResolution || layer.options.maxScale) {
+ var minSD = this.createElementNS(
+ this.namespaces.sld, "sld:MinScaleDenominator"
+ );
+ minSD.appendChild(this.createTextNode(layer.maxScale.toPrecision(10)));
+ node.insertBefore(minSD, node.childNodes[3]);
+ }
+
+ if(layer.options.resolutions || layer.options.scales ||
+ layer.options.maxResolution || layer.options.minScale) {
+ var maxSD = this.createElementNS(
+ this.namespaces.sld, "sld:MaxScaleDenominator"
+ );
+ maxSD.appendChild(this.createTextNode(layer.minScale.toPrecision(10)));
+ node.insertBefore(maxSD, node.childNodes[4]);
+ }
+
+ return node;
+
+ },
+
+ CLASS_NAME: "OpenLayers.Format.WMC.v1_1_0"
+
+});
+/* ======================================================================
OpenLayers/Handler/Box.js
====================================================================== */
@@ -13477,6 +27890,13 @@
* olHandlerBoxZoomBox
*/
boxDivClassName: 'olHandlerBoxZoomBox',
+
+ /**
+ * Property: boxCharacteristics
+ * {Object} Caches some box characteristics from css. This is used
+ * by the getBoxCharacteristics method.
+ */
+ boxCharacteristics: null,
/**
* Constructor: OpenLayers.Handler.Box
@@ -13532,16 +27952,28 @@
* Method: moveBox
*/
moveBox: function (xy) {
- var deltaX = Math.abs(this.dragHandler.start.x - xy.x);
- var deltaY = Math.abs(this.dragHandler.start.y - xy.y);
+ var startX = this.dragHandler.start.x;
+ var startY = this.dragHandler.start.y;
+ var deltaX = Math.abs(startX - xy.x);
+ var deltaY = Math.abs(startY - xy.y);
this.zoomBox.style.width = Math.max(1, deltaX) + "px";
this.zoomBox.style.height = Math.max(1, deltaY) + "px";
- if (xy.x < this.dragHandler.start.x) {
- this.zoomBox.style.left = xy.x+"px";
+ this.zoomBox.style.left = xy.x < startX ? xy.x+"px" : startX+"px";
+ this.zoomBox.style.top = xy.y < startY ? xy.y+"px" : startY+"px";
+
+ // depending on the box model, modify width and height to take borders
+ // of the box into account
+ var box = this.getBoxCharacteristics(deltaX, deltaY);
+ if (box.newBoxModel) {
+ if (xy.x > startX) {
+ this.zoomBox.style.width =
+ Math.max(1, deltaX - box.xOffset) + "px";
+ }
+ if (xy.y > startY) {
+ this.zoomBox.style.height =
+ Math.max(1, deltaY - box.yOffset) + "px";
+ }
}
- if (xy.y < this.dragHandler.start.y) {
- this.zoomBox.style.top = xy.y+"px";
- }
},
/**
@@ -13575,6 +28007,7 @@
removeBox: function() {
this.map.viewPortDiv.removeChild(this.zoomBox);
this.zoomBox = null;
+ this.boxCharacteristics = null;
},
/**
@@ -13600,10 +28033,1111 @@
return false;
}
},
+
+ getBoxCharacteristics: function(dx, dy) {
+ if (!this.boxCharacteristics) {
+ var xOffset = parseInt(OpenLayers.Element.getStyle(this.zoomBox,
+ "border-left-width")) + parseInt(OpenLayers.Element.getStyle(
+ this.zoomBox, "border-right-width")) + 1;
+ var yOffset = parseInt(OpenLayers.Element.getStyle(this.zoomBox,
+ "border-top-width")) + parseInt(OpenLayers.Element.getStyle(
+ this.zoomBox, "border-bottom-width")) + 1;
+ // all browsers use the new box model, except IE in quirks mode
+ var newBoxModel = OpenLayers.Util.getBrowserName() == "msie" ?
+ document.compatMode != "BackCompat" : true;
+ this.boxCharacteristics = {
+ xOffset: xOffset,
+ yOffset: yOffset,
+ newBoxModel: newBoxModel
+ }
+ }
+ return this.boxCharacteristics;
+ },
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: downFeature
+ * 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
+ ====================================================================== */
+
+/* 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/Layer.js
+ * @requires OpenLayers/Util.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.EventPane
+ * Base class for 3rd party layers. Create a new event pane layer with the
+ * <OpenLayers.Layer.EventPane> constructor.
+ *
+ * Inherits from:
+ * - <OpenLayers.Layer>
+ */
+OpenLayers.Layer.EventPane = OpenLayers.Class(OpenLayers.Layer, {
+
+ /**
+ * APIProperty: smoothDragPan
+ * {Boolean} smoothDragPan determines whether non-public/internal API
+ * methods are used for better performance while dragging EventPane
+ * layers. When not in sphericalMercator mode, the smoother dragging
+ * doesn't actually move north/south directly with the number of
+ * pixels moved, resulting in a slight offset when you drag your mouse
+ * north south with this option on. If this visual disparity bothers
+ * you, you should turn this option off, or use spherical mercator.
+ * Default is on.
+ */
+ smoothDragPan: true,
+
+ /**
+ * Property: isBaseLayer
+ * {Boolean} EventPaned layers are always base layers, by necessity.
+ */
+ isBaseLayer: true,
+
+ /**
+ * APIProperty: isFixed
+ * {Boolean} EventPaned layers are fixed by default.
+ */
+ isFixed: true,
+
+ /**
+ * Property: pane
+ * {DOMElement} A reference to the element that controls the events.
+ */
+ pane: null,
+
+
+ /**
+ * Property: mapObject
+ * {Object} This is the object which will be used to load the 3rd party library
+ * in the case of the google layer, this will be of type GMap,
+ * in the case of the ve layer, this will be of type VEMap
+ */
+ mapObject: null,
+
+
+ /**
+ * Constructor: OpenLayers.Layer.EventPane
+ * Create a new event pane layer
+ *
+ * Parameters:
+ * name - {String}
+ * options - {Object} Hashtable of extra options to tag onto the layer
+ */
+ initialize: function(name, options) {
+ OpenLayers.Layer.prototype.initialize.apply(this, arguments);
+ if (this.pane == null) {
+ this.pane = OpenLayers.Util.createDiv(this.div.id + "_EventPane");
+ }
+ },
+
+ /**
+ * APIMethod: destroy
+ * Deconstruct this layer.
+ */
+ destroy: function() {
+ this.mapObject = null;
+ OpenLayers.Layer.prototype.destroy.apply(this, arguments);
+ },
+
+
+ /**
+ * Method: setMap
+ * Set the map property for the layer. This is done through an accessor
+ * so that subclasses can override this and take special action once
+ * they have their map variable set.
+ *
+ * Parameters:
+ * map - {<OpenLayers.Map>}
+ */
+ setMap: function(map) {
+ OpenLayers.Layer.prototype.setMap.apply(this, arguments);
+
+ this.pane.style.zIndex = parseInt(this.div.style.zIndex) + 1;
+ this.pane.style.display = this.div.style.display;
+ this.pane.style.width="100%";
+ this.pane.style.height="100%";
+ if (OpenLayers.Util.getBrowserName() == "msie") {
+ this.pane.style.background =
+ "url(" + OpenLayers.Util.getImagesLocation() + "blank.gif)";
+ }
+
+ if (this.isFixed) {
+ this.map.viewPortDiv.appendChild(this.pane);
+ } else {
+ this.map.layerContainerDiv.appendChild(this.pane);
+ }
+
+ // once our layer has been added to the map, we can load it
+ this.loadMapObject();
+
+ // if map didn't load, display warning
+ if (this.mapObject == null) {
+ this.loadWarningMessage();
+ }
+ },
+
+ /**
+ * APIMethod: removeMap
+ * On being removed from the map, we'll like to remove the invisible 'pane'
+ * div that we added to it on creation.
+ *
+ * Parameters:
+ * map - {<OpenLayers.Map>}
+ */
+ removeMap: function(map) {
+ if (this.pane && this.pane.parentNode) {
+ this.pane.parentNode.removeChild(this.pane);
+ this.pane = null;
+ }
+ OpenLayers.Layer.prototype.removeMap.apply(this, arguments);
+ },
+
+ /**
+ * Method: loadWarningMessage
+ * If we can't load the map lib, then display an error message to the
+ * user and tell them where to go for help.
+ *
+ * This function sets up the layout for the warning message. Each 3rd
+ * party layer must implement its own getWarningHTML() function to
+ * provide the actual warning message.
+ */
+ loadWarningMessage:function() {
+
+ this.div.style.backgroundColor = "darkblue";
+
+ var viewSize = this.map.getSize();
+
+ var msgW = Math.min(viewSize.w, 300);
+ var msgH = Math.min(viewSize.h, 200);
+ var size = new OpenLayers.Size(msgW, msgH);
+
+ var centerPx = new OpenLayers.Pixel(viewSize.w/2, viewSize.h/2);
+
+ var topLeft = centerPx.add(-size.w/2, -size.h/2);
+
+ var div = OpenLayers.Util.createDiv(this.name + "_warning",
+ topLeft,
+ size,
+ null,
+ null,
+ null,
+ "auto");
+
+ div.style.padding = "7px";
+ div.style.backgroundColor = "yellow";
+
+ div.innerHTML = this.getWarningHTML();
+ this.div.appendChild(div);
+ },
+
+ /**
+ * Method: getWarningHTML
+ * To be implemented by subclasses.
+ *
+ * Returns:
+ * {String} String with information on why layer is broken, how to get
+ * it working.
+ */
+ getWarningHTML:function() {
+ //should be implemented by subclasses
+ return "";
+ },
+
+ /**
+ * Method: display
+ * Set the display on the pane
+ *
+ * Parameters:
+ * display - {Boolean}
+ */
+ display: function(display) {
+ OpenLayers.Layer.prototype.display.apply(this, arguments);
+ this.pane.style.display = this.div.style.display;
+ },
+
+ /**
+ * Method: setZIndex
+ * Set the z-index order for the pane.
+ *
+ * Parameters:
+ * zIndex - {int}
+ */
+ setZIndex: function (zIndex) {
+ OpenLayers.Layer.prototype.setZIndex.apply(this, arguments);
+ this.pane.style.zIndex = parseInt(this.div.style.zIndex) + 1;
+ },
+
+ /**
+ * Method: moveTo
+ * Handle calls to move the layer.
+ *
+ * Parameters:
+ * bounds - {<OpenLayers.Bounds>}
+ * zoomChanged - {Boolean}
+ * dragging - {Boolean}
+ */
+ moveTo:function(bounds, zoomChanged, dragging) {
+ OpenLayers.Layer.prototype.moveTo.apply(this, arguments);
+
+ if (this.mapObject != null) {
+
+ var newCenter = this.map.getCenter();
+ var newZoom = this.map.getZoom();
+
+ if (newCenter != null) {
+
+ var moOldCenter = this.getMapObjectCenter();
+ var oldCenter = this.getOLLonLatFromMapObjectLonLat(moOldCenter);
+
+ var moOldZoom = this.getMapObjectZoom();
+ var oldZoom= this.getOLZoomFromMapObjectZoom(moOldZoom);
+
+ if ( !(newCenter.equals(oldCenter)) ||
+ !(newZoom == oldZoom) ) {
+
+ if (dragging && this.dragPanMapObject &&
+ this.smoothDragPan) {
+ var oldPx = this.map.getViewPortPxFromLonLat(oldCenter);
+ var newPx = this.map.getViewPortPxFromLonLat(newCenter);
+ this.dragPanMapObject(newPx.x-oldPx.x, oldPx.y-newPx.y);
+ } else {
+ var center = this.getMapObjectLonLatFromOLLonLat(newCenter);
+ var zoom = this.getMapObjectZoomFromOLZoom(newZoom);
+ this.setMapObjectCenter(center, zoom, dragging);
+ }
+ }
+ }
+ }
+ },
+
+
+ /********************************************************/
+ /* */
+ /* Baselayer Functions */
+ /* */
+ /********************************************************/
+
+ /**
+ * Method: getLonLatFromViewPortPx
+ * Get a map location from a pixel location
+ *
+ * Parameters:
+ * viewPortPx - {<OpenLayers.Pixel>}
+ *
+ * Returns:
+ * {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in view
+ * port OpenLayers.Pixel, translated into lon/lat by map lib
+ * If the map lib is not loaded or not centered, returns null
+ */
+ getLonLatFromViewPortPx: function (viewPortPx) {
+ var lonlat = null;
+ if ( (this.mapObject != null) &&
+ (this.getMapObjectCenter() != null) ) {
+ var moPixel = this.getMapObjectPixelFromOLPixel(viewPortPx);
+ var moLonLat = this.getMapObjectLonLatFromMapObjectPixel(moPixel);
+ lonlat = this.getOLLonLatFromMapObjectLonLat(moLonLat);
+ }
+ return lonlat;
+ },
+
+
+ /**
+ * Method: getViewPortPxFromLonLat
+ * Get a pixel location from a map location
+ *
+ * Parameters:
+ * lonlat - {<OpenLayers.LonLat>}
+ *
+ * Returns:
+ * {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in
+ * OpenLayers.LonLat, translated into view port pixels by map lib
+ * If map lib is not loaded or not centered, returns null
+ */
+ getViewPortPxFromLonLat: function (lonlat) {
+ var viewPortPx = null;
+ if ( (this.mapObject != null) &&
+ (this.getMapObjectCenter() != null) ) {
+
+ var moLonLat = this.getMapObjectLonLatFromOLLonLat(lonlat);
+ var moPixel = this.getMapObjectPixelFromMapObjectLonLat(moLonLat);
+
+ viewPortPx = this.getOLPixelFromMapObjectPixel(moPixel);
+ }
+ return viewPortPx;
+ },
+
+ /********************************************************/
+ /* */
+ /* Translation Functions */
+ /* */
+ /* The following functions translate Map Object and */
+ /* OL formats for Pixel, LonLat */
+ /* */
+ /********************************************************/
+
+ //
+ // TRANSLATION: MapObject LatLng <-> OpenLayers.LonLat
+ //
+
+ /**
+ * Method: getOLLonLatFromMapObjectLonLat
+ * Get an OL style map location from a 3rd party style map location
+ *
+ * Parameters
+ * moLonLat - {Object}
+ *
+ * Returns:
+ * {<OpenLayers.LonLat>} An OpenLayers.LonLat, translated from the passed in
+ * MapObject LonLat
+ * Returns null if null value is passed in
+ */
+ getOLLonLatFromMapObjectLonLat: function(moLonLat) {
+ var olLonLat = null;
+ if (moLonLat != null) {
+ var lon = this.getLongitudeFromMapObjectLonLat(moLonLat);
+ var lat = this.getLatitudeFromMapObjectLonLat(moLonLat);
+ olLonLat = new OpenLayers.LonLat(lon, lat);
+ }
+ return olLonLat;
+ },
+
+ /**
+ * Method: getMapObjectLonLatFromOLLonLat
+ * Get a 3rd party map location from an OL map location.
+ *
+ * Parameters:
+ * olLonLat - {<OpenLayers.LonLat>}
+ *
+ * Returns:
+ * {Object} A MapObject LonLat, translated from the passed in
+ * OpenLayers.LonLat
+ * Returns null if null value is passed in
+ */
+ getMapObjectLonLatFromOLLonLat: function(olLonLat) {
+ var moLatLng = null;
+ if (olLonLat != null) {
+ moLatLng = this.getMapObjectLonLatFromLonLat(olLonLat.lon,
+ olLonLat.lat);
+ }
+ return moLatLng;
+ },
+
+
+ //
+ // TRANSLATION: MapObject Pixel <-> OpenLayers.Pixel
+ //
+
+ /**
+ * Method: getOLPixelFromMapObjectPixel
+ * Get an OL pixel location from a 3rd party pixel location.
+ *
+ * Parameters:
+ * moPixel - {Object}
+ *
+ * Returns:
+ * {<OpenLayers.Pixel>} An OpenLayers.Pixel, translated from the passed in
+ * MapObject Pixel
+ * Returns null if null value is passed in
+ */
+ getOLPixelFromMapObjectPixel: function(moPixel) {
+ var olPixel = null;
+ if (moPixel != null) {
+ var x = this.getXFromMapObjectPixel(moPixel);
+ var y = this.getYFromMapObjectPixel(moPixel);
+ olPixel = new OpenLayers.Pixel(x, y);
+ }
+ return olPixel;
+ },
+
+ /**
+ * Method: getMapObjectPixelFromOLPixel
+ * Get a 3rd party pixel location from an OL pixel location
+ *
+ * Parameters:
+ * olPixel - {<OpenLayers.Pixel>}
+ *
+ * Returns:
+ * {Object} A MapObject Pixel, translated from the passed in
+ * OpenLayers.Pixel
+ * Returns null if null value is passed in
+ */
+ getMapObjectPixelFromOLPixel: function(olPixel) {
+ var moPixel = null;
+ if (olPixel != null) {
+ moPixel = this.getMapObjectPixelFromXY(olPixel.x, olPixel.y);
+ }
+ return moPixel;
+ },
+
+ CLASS_NAME: "OpenLayers.Layer.EventPane"
+});
+/* ======================================================================
+ OpenLayers/Layer/FixedZoomLevels.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/Layer.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.FixedZoomLevels
+ * Some Layers will already have established zoom levels (like google
+ * or ve). Instead of trying to determine them and populate a resolutions[]
+ * Array with those values, we will hijack the resolution functionality
+ * here.
+ *
+ * When you subclass FixedZoomLevels:
+ *
+ * The initResolutions() call gets nullified, meaning no resolutions[] array
+ * is set up. Which would be a big problem getResolution() in Layer, since
+ * it merely takes map.zoom and indexes into resolutions[]... but....
+ *
+ * The getResolution() call is also overridden. Instead of using the
+ * resolutions[] array, we simply calculate the current resolution based
+ * on the current extent and the current map size. But how will we be able
+ * to calculate the current extent without knowing the resolution...?
+ *
+ * The getExtent() function is also overridden. Instead of calculating extent
+ * based on the center point and the current resolution, we instead
+ * calculate the extent by getting the lonlats at the top-left and
+ * bottom-right by using the getLonLatFromViewPortPx() translation function,
+ * taken from the pixel locations (0,0) and the size of the map. But how
+ * will we be able to do lonlat-px translation without resolution....?
+ *
+ * The getZoomForResolution() method is overridden. Instead of indexing into
+ * the resolutions[] array, we call OpenLayers.Layer.getExent(), passing in
+ * the desired resolution. With this extent, we then call getZoomForExtent()
+ *
+ *
+ * Whenever you implement a layer using OpenLayers.Layer.FixedZoomLevels,
+ * it is your responsibility to provide the following three functions:
+ *
+ * - getLonLatFromViewPortPx
+ * - getViewPortPxFromLonLat
+ * - getZoomForExtent
+ *
+ * ...those three functions should generally be provided by any reasonable
+ * API that you might be working from.
+ *
+ */
+OpenLayers.Layer.FixedZoomLevels = OpenLayers.Class({
+
+ /********************************************************/
+ /* */
+ /* Baselayer Functions */
+ /* */
+ /* The following functions must all be implemented */
+ /* by all base layers */
+ /* */
+ /********************************************************/
+
+ /**
+ * Constructor: OpenLayers.Layer.FixedZoomLevels
+ * Create a new fixed zoom levels layer.
+ */
+ initialize: function() {
+ //this class is only just to add the following functions...
+ // nothing to actually do here... but it is probably a good
+ // idea to have layers that use these functions call this
+ // inititalize() anyways, in case at some point we decide we
+ // do want to put some functionality or state in here.
+ },
+
+ /**
+ * Method: initResolutions
+ * Populate the resolutions array
+ */
+ initResolutions: function() {
+
+ var props = new Array('minZoomLevel', 'maxZoomLevel', 'numZoomLevels');
+
+ for(var i=0, len=props.length; i<len; i++) {
+ var property = props[i];
+ this[property] = (this.options[property] != null)
+ ? this.options[property]
+ : this.map[property];
+ }
+
+ if ( (this.minZoomLevel == null) ||
+ (this.minZoomLevel < this.MIN_ZOOM_LEVEL) ){
+ this.minZoomLevel = this.MIN_ZOOM_LEVEL;
+ }
+
+ var limitZoomLevels = this.MAX_ZOOM_LEVEL - this.minZoomLevel + 1;
+ if (this.numZoomLevels != null) {
+ this.numZoomLevels = Math.min(this.numZoomLevels, limitZoomLevels);
+ } else {
+ if (this.maxZoomLevel != null) {
+ var zoomDiff = this.maxZoomLevel - this.minZoomLevel + 1;
+ this.numZoomLevels = Math.min(zoomDiff, limitZoomLevels);
+ } else {
+ this.numZoomLevels = limitZoomLevels;
+ }
+ }
+
+ this.maxZoomLevel = this.minZoomLevel + this.numZoomLevels - 1;
+
+ if (this.RESOLUTIONS != null) {
+ var resolutionsIndex = 0;
+ this.resolutions = [];
+ for(var i= this.minZoomLevel; i <= this.maxZoomLevel; i++) {
+ this.resolutions[resolutionsIndex++] = this.RESOLUTIONS[i];
+ }
+ this.maxResolution = this.resolutions[0];
+ this.minResolution = this.resolutions[this.resolutions.length - 1];
+ }
+ },
+
+ /**
+ * APIMethod: getResolution
+ * Get the current map resolution
+ *
+ * Returns:
+ * {Float} Map units per Pixel
+ */
+ getResolution: function() {
+
+ if (this.resolutions != null) {
+ return OpenLayers.Layer.prototype.getResolution.apply(this, arguments);
+ } else {
+ var resolution = null;
+
+ var viewSize = this.map.getSize();
+ var extent = this.getExtent();
+
+ if ((viewSize != null) && (extent != null)) {
+ resolution = Math.max( extent.getWidth() / viewSize.w,
+ extent.getHeight() / viewSize.h );
+ }
+ return resolution;
+ }
+ },
+
+ /**
+ * APIMethod: getExtent
+ * Calculates using px-> lonlat translation functions on tl and br
+ * corners of viewport
+ *
+ * Returns:
+ * {<OpenLayers.Bounds>} A Bounds object which represents the lon/lat
+ * bounds of the current viewPort.
+ */
+ getExtent: function () {
+ var extent = null;
+
+
+ var size = this.map.getSize();
+
+ var tlPx = new OpenLayers.Pixel(0,0);
+ var tlLL = this.getLonLatFromViewPortPx(tlPx);
+
+ var brPx = new OpenLayers.Pixel(size.w, size.h);
+ var brLL = this.getLonLatFromViewPortPx(brPx);
+
+ if ((tlLL != null) && (brLL != null)) {
+ extent = new OpenLayers.Bounds(tlLL.lon,
+ brLL.lat,
+ brLL.lon,
+ tlLL.lat);
+ }
+
+ return extent;
+ },
+
+ /**
+ * Method: getZoomForResolution
+ * Get the zoom level for a given resolution
+ *
+ * Parameters:
+ * resolution - {Float}
+ *
+ * Returns:
+ * {Integer} A suitable zoom level for the specified resolution.
+ * If no baselayer is set, returns null.
+ */
+ getZoomForResolution: function(resolution) {
+
+ if (this.resolutions != null) {
+ return OpenLayers.Layer.prototype.getZoomForResolution.apply(this, arguments);
+ } else {
+ var extent = OpenLayers.Layer.prototype.getExtent.apply(this, []);
+ return this.getZoomForExtent(extent);
+ }
+ },
+
+
+
+
+ /********************************************************/
+ /* */
+ /* Translation Functions */
+ /* */
+ /* The following functions translate GMaps and OL */
+ /* formats for Pixel, LonLat, Bounds, and Zoom */
+ /* */
+ /********************************************************/
+
+
+ //
+ // TRANSLATION: MapObject Zoom <-> OpenLayers Zoom
+ //
+
+ /**
+ * Method: getOLZoomFromMapObjectZoom
+ * Get the OL zoom index from the map object zoom level
+ *
+ * Parameters:
+ * moZoom - {Integer}
+ *
+ * Returns:
+ * {Integer} An OpenLayers Zoom level, translated from the passed in zoom
+ * Returns null if null value is passed in
+ */
+ getOLZoomFromMapObjectZoom: function(moZoom) {
+ var zoom = null;
+ if (moZoom != null) {
+ zoom = moZoom - this.minZoomLevel;
+ }
+ return zoom;
+ },
+
+ /**
+ * Method: getMapObjectZoomFromOLZoom
+ * Get the map object zoom level from the OL zoom level
+ *
+ * Parameters:
+ * olZoom - {Integer}
+ *
+ * Returns:
+ * {Integer} A MapObject level, translated from the passed in olZoom
+ * Returns null if null value is passed in
+ */
+ getMapObjectZoomFromOLZoom: function(olZoom) {
+ var zoom = null;
+ if (olZoom != null) {
+ zoom = olZoom + this.minZoomLevel;
+ }
+ return zoom;
+ },
+
+ CLASS_NAME: "FixedZoomLevels.js"
+});
+
+/* ======================================================================
OpenLayers/Layer/HTTPRequest.js
====================================================================== */
@@ -13769,7 +29303,7 @@
*/
selectUrl: function(paramString, urls) {
var product = 1;
- for (var i = 0; i < paramString.length; i++) {
+ for (var i=0, len=paramString.length; i<len; i++) {
product *= paramString.charCodeAt(i) * this.URL_HASH_FACTOR;
product -= Math.floor(product);
}
@@ -13848,6 +29382,1072 @@
CLASS_NAME: "OpenLayers.Layer.HTTPRequest"
});
/* ======================================================================
+ OpenLayers/Layer/Image.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/Layer.js
+ * @requires OpenLayers/Tile/Image.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.Image
+ * Instances of OpenLayers.Layer.Image are used to display data from a web
+ * accessible image as a map layer. Create a new image layer with the
+ * <OpenLayers.Layer.Image> constructor. Inherits from <OpenLayers.Layer>.
+ */
+OpenLayers.Layer.Image = OpenLayers.Class(OpenLayers.Layer, {
+
+ /**
+ * Property: isBaseLayer
+ * {Boolean} The layer is a base layer. Default is true. Set this property
+ * in the layer options
+ */
+ isBaseLayer: true,
+
+ /**
+ * Property: url
+ * {String} URL of the image to use
+ */
+ url: null,
+
+ /**
+ * Property: extent
+ * {<OpenLayers.Bounds>} The image bounds in map units
+ */
+ extent: null,
+
+ /**
+ * Property: size
+ * {<OpenLayers.Size>} The image size in pixels
+ */
+ size: null,
+
+ /**
+ * Property: tile
+ * {<OpenLayers.Tile.Image>}
+ */
+ tile: null,
+
+ /**
+ * Property: aspectRatio
+ * {Float} The ratio of height/width represented by a single pixel in the
+ * graphic
+ */
+ aspectRatio: null,
+
+ /**
+ * Constructor: OpenLayers.Layer.Image
+ * Create a new image layer
+ *
+ * Parameters:
+ * name - {String} A name for the layer.
+ * url - {String} Relative or absolute path to the image
+ * extent - {<OpenLayers.Bounds>} The extent represented by the image
+ * size - {<OpenLayers.Size>} The size (in pixels) of the image
+ * options - {Object} Hashtable of extra options to tag onto the layer
+ */
+ initialize: function(name, url, extent, size, options) {
+ this.url = url;
+ this.extent = extent;
+ this.size = size;
+ OpenLayers.Layer.prototype.initialize.apply(this, [name, options]);
+
+ this.aspectRatio = (this.extent.getHeight() / this.size.h) /
+ (this.extent.getWidth() / this.size.w);
+ },
+
+ /**
+ * Method: destroy
+ * Destroy this layer
+ */
+ destroy: function() {
+ if (this.tile) {
+ this.tile.destroy();
+ this.tile = null;
+ }
+ OpenLayers.Layer.prototype.destroy.apply(this, arguments);
+ },
+
+ /**
+ * Method: clone
+ * Create a clone of this layer
+ *
+ * Paramters:
+ * obj - {Object} An optional layer (is this ever used?)
+ *
+ * Returns:
+ * {<OpenLayers.Layer.Image>} An exact copy of this layer
+ */
+ clone: function(obj) {
+
+ if(obj == null) {
+ obj = new OpenLayers.Layer.Image(this.name,
+ this.url,
+ this.extent,
+ this.size,
+ this.options);
+ }
+
+ //get all additions from superclasses
+ obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]);
+
+ // copy/set any non-init, non-simple values here
+
+ return obj;
+ },
+
+ /**
+ * APIMethod: setMap
+ *
+ * Parameters:
+ * map - {<OpenLayers.Map>}
+ */
+ setMap: function(map) {
+ /**
+ * If nothing to do with resolutions has been set, assume a single
+ * resolution determined by ratio*extent/size - if an image has a
+ * pixel aspect ratio different than one (as calculated above), the
+ * image will be stretched in one dimension only.
+ */
+ if( this.options.maxResolution == null ) {
+ this.options.maxResolution = this.aspectRatio *
+ this.extent.getWidth() /
+ this.size.w;
+ }
+ OpenLayers.Layer.prototype.setMap.apply(this, arguments);
+ },
+
+ /**
+ * Method: moveTo
+ * Create the tile for the image or resize it for the new resolution
+ *
+ * Parameters:
+ * bounds - {<OpenLayers.Bounds>}
+ * zoomChanged - {Boolean}
+ * dragging - {Boolean}
+ */
+ moveTo:function(bounds, zoomChanged, dragging) {
+ OpenLayers.Layer.prototype.moveTo.apply(this, arguments);
+
+ var firstRendering = (this.tile == null);
+
+ if(zoomChanged || firstRendering) {
+
+ //determine new tile size
+ this.setTileSize();
+
+ //determine new position (upper left corner of new bounds)
+ var ul = new OpenLayers.LonLat(this.extent.left, this.extent.top);
+ var ulPx = this.map.getLayerPxFromLonLat(ul);
+
+ if(firstRendering) {
+ //create the new tile
+ this.tile = new OpenLayers.Tile.Image(this, ulPx, this.extent,
+ null, this.tileSize);
+ } else {
+ //just resize the tile and set it's new position
+ this.tile.size = this.tileSize.clone();
+ this.tile.position = ulPx.clone();
+ }
+ this.tile.draw();
+ }
+ },
+
+ /**
+ * Set the tile size based on the map size.
+ */
+ setTileSize: function() {
+ var tileWidth = this.extent.getWidth() / this.map.getResolution();
+ var tileHeight = this.extent.getHeight() / this.map.getResolution();
+ this.tileSize = new OpenLayers.Size(tileWidth, tileHeight);
+ },
+
+ /**
+ * APIMethod: setUrl
+ *
+ * Parameters:
+ * newUrl - {String}
+ */
+ setUrl: function(newUrl) {
+ this.url = newUrl;
+ this.tile.draw();
+ },
+
+ /**
+ * APIMethod: getURL
+ * The url we return is always the same (the image itself never changes)
+ * so we can ignore the bounds parameter (it will always be the same,
+ * anyways)
+ *
+ * Parameters:
+ * bounds - {<OpenLayers.Bounds>}
+ */
+ getURL: function(bounds) {
+ return this.url;
+ },
+
+ CLASS_NAME: "OpenLayers.Layer.Image"
+});
+/* ======================================================================
+ OpenLayers/Layer/Markers.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/Layer.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.Markers
+ *
+ * Inherits from:
+ * - <OpenLayers.Layer>
+ */
+OpenLayers.Layer.Markers = OpenLayers.Class(OpenLayers.Layer, {
+
+ /**
+ * APIProperty: isBaseLayer
+ * {Boolean} Markers layer is never a base layer.
+ */
+ isBaseLayer: false,
+
+ /**
+ * Property: markers
+ * {Array(<OpenLayers.Marker>)} internal marker list
+ */
+ markers: null,
+
+
+ /**
+ * Property: drawn
+ * {Boolean} internal state of drawing. This is a workaround for the fact
+ * that the map does not call moveTo with a zoomChanged when the map is
+ * first starting up. This lets us catch the case where we have *never*
+ * drawn the layer, and draw it even if the zoom hasn't changed.
+ */
+ drawn: false,
+
+ /**
+ * Constructor: OpenLayers.Layer.Markers
+ * Create a Markers layer.
+ *
+ * Parameters:
+ * name - {String}
+ * options - {Object} Hashtable of extra options to tag onto the layer
+ */
+ initialize: function(name, options) {
+ OpenLayers.Layer.prototype.initialize.apply(this, arguments);
+ this.markers = [];
+ },
+
+ /**
+ * APIMethod: destroy
+ */
+ destroy: function() {
+ this.clearMarkers();
+ this.markers = null;
+ OpenLayers.Layer.prototype.destroy.apply(this, arguments);
+ },
+
+ /**
+ * APIMethod: setOpacity
+ * Sets the opacity for all the markers.
+ *
+ * Parameter:
+ * opacity - {Float}
+ */
+ setOpacity: function(opacity) {
+ if (opacity != this.opacity) {
+ this.opacity = opacity;
+ for (var i=0, len=this.markers.length; i<len; i++) {
+ this.markers[i].setOpacity(this.opacity);
+ }
+ }
+ },
+
+ /**
+ * Method: moveTo
+ *
+ * Parameters:
+ * bounds - {<OpenLayers.Bounds>}
+ * zoomChanged - {Boolean}
+ * dragging - {Boolean}
+ */
+ moveTo:function(bounds, zoomChanged, dragging) {
+ OpenLayers.Layer.prototype.moveTo.apply(this, arguments);
+
+ if (zoomChanged || !this.drawn) {
+ for(var i=0, len=this.markers.length; i<len; i++) {
+ this.drawMarker(this.markers[i]);
+ }
+ this.drawn = true;
+ }
+ },
+
+ /**
+ * APIMethod: addMarker
+ *
+ * Parameters:
+ * marker - {<OpenLayers.Marker>}
+ */
+ addMarker: function(marker) {
+ this.markers.push(marker);
+
+ if (this.opacity != null) {
+ marker.setOpacity(this.opacity);
+ }
+
+ if (this.map && this.map.getExtent()) {
+ marker.map = this.map;
+ this.drawMarker(marker);
+ }
+ },
+
+ /**
+ * APIMethod: removeMarker
+ *
+ * Parameters:
+ * marker - {<OpenLayers.Marker>}
+ */
+ removeMarker: function(marker) {
+ if (this.markers && this.markers.length) {
+ OpenLayers.Util.removeItem(this.markers, marker);
+ if ((marker.icon != null) && (marker.icon.imageDiv != null) &&
+ (marker.icon.imageDiv.parentNode == this.div) ) {
+ this.div.removeChild(marker.icon.imageDiv);
+ marker.drawn = false;
+ }
+ }
+ },
+
+ /**
+ * Method: clearMarkers
+ * This method removes all markers from a layer. The markers are not
+ * destroyed by this function, but are removed from the list of markers.
+ */
+ clearMarkers: function() {
+ if (this.markers != null) {
+ while(this.markers.length > 0) {
+ this.removeMarker(this.markers[0]);
+ }
+ }
+ },
+
+ /**
+ * Method: drawMarker
+ * Calculate the pixel location for the marker, create it, and
+ * add it to the layer's div
+ *
+ * Parameters:
+ * marker - {<OpenLayers.Marker>}
+ */
+ drawMarker: function(marker) {
+ var px = this.map.getLayerPxFromLonLat(marker.lonlat);
+ if (px == null) {
+ marker.display(false);
+ } else {
+ var markerImg = marker.draw(px);
+ if (!marker.drawn) {
+ this.div.appendChild(markerImg);
+ marker.drawn = true;
+ }
+ }
+ },
+
+ /**
+ * APIMethod: getDataExtent
+ * Calculates the max extent which includes all of the markers.
+ *
+ * Returns:
+ * {<OpenLayers.Bounds>}
+ */
+ getDataExtent: function () {
+ var maxExtent = null;
+
+ if ( this.markers && (this.markers.length > 0)) {
+ var maxExtent = new OpenLayers.Bounds();
+ for(var i=0, len=this.markers.length; i<len; i++) {
+ var marker = this.markers[i];
+ maxExtent.extend(marker.lonlat);
+ }
+ }
+
+ return maxExtent;
+ },
+
+ CLASS_NAME: "OpenLayers.Layer.Markers"
+});
+/* ======================================================================
+ OpenLayers/Layer/SphericalMercator.js
+ ====================================================================== */
+
+/**
+ * @requires OpenLayers/Layer.js
+ * @requires OpenLayers/Projection.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.SphericalMercator
+ * A mixin for layers that wraps up the pieces neccesary to have a coordinate
+ * conversion for working with commercial APIs which use a spherical
+ * mercator projection. Using this layer as a base layer, additional
+ * layers can be used as overlays if they are in the same projection.
+ *
+ * A layer is given properties of this object by setting the sphericalMercator
+ * property to true.
+ *
+ * More projection information:
+ * - http://spatialreference.org/ref/user/google-projection/
+ *
+ * Proj4 Text:
+ * +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0
+ * +k=1.0 +units=m +nadgrids=@null +no_defs
+ *
+ * WKT:
+ * 900913=PROJCS["WGS84 / Simple Mercator", GEOGCS["WGS 84",
+ * DATUM["WGS_1984", SPHEROID["WGS_1984", 6378137.0, 298.257223563]],
+ * PRIMEM["Greenwich", 0.0], UNIT["degree", 0.017453292519943295],
+ * AXIS["Longitude", EAST], AXIS["Latitude", NORTH]],
+ * PROJECTION["Mercator_1SP_Google"],
+ * PARAMETER["latitude_of_origin", 0.0], PARAMETER["central_meridian", 0.0],
+ * PARAMETER["scale_factor", 1.0], PARAMETER["false_easting", 0.0],
+ * PARAMETER["false_northing", 0.0], UNIT["m", 1.0], AXIS["x", EAST],
+ * AXIS["y", NORTH], AUTHORITY["EPSG","900913"]]
+ */
+OpenLayers.Layer.SphericalMercator = {
+
+ /**
+ * Method: getExtent
+ * Get the map's extent.
+ *
+ * Returns:
+ * {<OpenLayers.Bounds>} The map extent.
+ */
+ getExtent: function() {
+ var extent = null;
+ if (this.sphericalMercator) {
+ extent = this.map.calculateBounds();
+ } else {
+ extent = OpenLayers.Layer.FixedZoomLevels.prototype.getExtent.apply(this);
+ }
+ return extent;
+ },
+
+ /**
+ * Method: initMercatorParameters
+ * Set up the mercator parameters on the layer: resolutions,
+ * projection, units.
+ */
+ initMercatorParameters: function() {
+ // set up properties for Mercator - assume EPSG:900913
+ this.RESOLUTIONS = [];
+ var maxResolution = 156543.0339;
+ for(var zoom=0; zoom<=this.MAX_ZOOM_LEVEL; ++zoom) {
+ this.RESOLUTIONS[zoom] = maxResolution / Math.pow(2, zoom);
+ }
+ this.units = "m";
+ this.projection = "EPSG:900913";
+ },
+
+ /**
+ * APIMethod: forwardMercator
+ * Given a lon,lat in EPSG:4326, return a point in Spherical Mercator.
+ *
+ * Parameters:
+ * lon - {float}
+ * lat - {float}
+ *
+ * Returns:
+ * {<OpenLayers.LonLat>} The coordinates transformed to Mercator.
+ */
+ forwardMercator: function(lon, lat) {
+ var x = lon * 20037508.34 / 180;
+ var y = Math.log(Math.tan((90 + lat) * Math.PI / 360)) / (Math.PI / 180);
+
+ y = y * 20037508.34 / 180;
+
+ return new OpenLayers.LonLat(x, y);
+ },
+
+ /**
+ * APIMethod: inverseMercator
+ * Given a x,y in Spherical Mercator, return a point in EPSG:4326.
+ *
+ * Parameters:
+ * x - {float} A map x in Spherical Mercator.
+ * y - {float} A map y in Spherical Mercator.
+ *
+ * Returns:
+ * {<OpenLayers.LonLat>} The coordinates transformed to EPSG:4326.
+ */
+ inverseMercator: function(x, y) {
+
+ var lon = (x / 20037508.34) * 180;
+ var lat = (y / 20037508.34) * 180;
+
+ lat = 180/Math.PI * (2 * Math.atan(Math.exp(lat * Math.PI / 180)) - Math.PI / 2);
+
+ return new OpenLayers.LonLat(lon, lat);
+ },
+
+ /**
+ * Method: projectForward
+ * Given an object with x and y properties in EPSG:4326, modify the x,y
+ * properties on the object to be the Spherical Mercator projected
+ * coordinates.
+ *
+ * Parameters:
+ * point - {Object} An object with x and y properties.
+ *
+ * Returns:
+ * {Object} The point, with the x and y properties transformed to spherical
+ * mercator.
+ */
+ projectForward: function(point) {
+ var lonlat = OpenLayers.Layer.SphericalMercator.forwardMercator(point.x, point.y);
+ point.x = lonlat.lon;
+ point.y = lonlat.lat;
+ return point;
+ },
+
+ /**
+ * Method: projectInverse
+ * Given an object with x and y properties in Spherical Mercator, modify
+ * the x,y properties on the object to be the unprojected coordinates.
+ *
+ * Parameters:
+ * point - {Object} An object with x and y properties.
+ *
+ * Returns:
+ * {Object} The point, with the x and y properties transformed from
+ * spherical mercator to unprojected coordinates..
+ */
+ projectInverse: function(point) {
+ var lonlat = OpenLayers.Layer.SphericalMercator.inverseMercator(point.x, point.y);
+ point.x = lonlat.lon;
+ point.y = lonlat.lat;
+ return point;
+ }
+
+};
+
+/**
+ * Note: Two transforms declared
+ * Transforms from EPSG:4326 to EPSG:900913 and from EPSG:900913 to EPSG:4326
+ * are set by this class.
+ */
+OpenLayers.Projection.addTransform("EPSG:4326", "EPSG:900913",
+ OpenLayers.Layer.SphericalMercator.projectForward);
+OpenLayers.Projection.addTransform("EPSG:900913", "EPSG:4326",
+ OpenLayers.Layer.SphericalMercator.projectInverse);
+/* ======================================================================
+ OpenLayers/Control/DrawFeature.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.DrawFeature
+ * Draws features on a vector layer when active.
+ *
+ * Inherits from:
+ * - <OpenLayers.Control>
+ */
+OpenLayers.Control.DrawFeature = OpenLayers.Class(OpenLayers.Control, {
+
+ /**
+ * Property: layer
+ * {<OpenLayers.Layer.Vector>}
+ */
+ layer: null,
+
+ /**
+ * Property: callbacks
+ * {Object} The functions that are sent to the handler for callback
+ */
+ callbacks: null,
+
+ /**
+ * Constant: EVENT_TYPES
+ *
+ * Supported event types:
+ * - *featureadded* Triggered when a feature is added
+ */
+ EVENT_TYPES: ["featureadded"],
+
+ /**
+ * APIProperty: featureAdded
+ * {Function} Called after each feature is added
+ */
+ featureAdded: function() {},
+
+ /**
+ * APIProperty: handlerOptions
+ * {Object} Used to set non-default properties on the control's handler
+ */
+ handlerOptions: null,
+
+ /**
+ * Constructor: OpenLayers.Control.DrawFeature
+ *
+ * Parameters:
+ * layer - {<OpenLayers.Layer.Vector>}
+ * handler - {<OpenLayers.Handler>}
+ * options - {Object}
+ */
+ initialize: function(layer, handler, options) {
+
+ // concatenate events specific to vector with those from the base
+ this.EVENT_TYPES =
+ OpenLayers.Control.DrawFeature.prototype.EVENT_TYPES.concat(
+ OpenLayers.Control.prototype.EVENT_TYPES
+ );
+
+ OpenLayers.Control.prototype.initialize.apply(this, [options]);
+ this.callbacks = OpenLayers.Util.extend({done: this.drawFeature},
+ this.callbacks);
+ this.layer = layer;
+ this.handler = new handler(this, this.callbacks, this.handlerOptions);
+ },
+
+ /**
+ * Method: drawFeature
+ */
+ drawFeature: function(geometry) {
+ var feature = new OpenLayers.Feature.Vector(geometry);
+ this.layer.addFeatures([feature]);
+ this.featureAdded(feature);
+ this.events.triggerEvent("featureadded",{feature : feature});
+ },
+
+ CLASS_NAME: "OpenLayers.Control.DrawFeature"
+});
+/* ======================================================================
+ OpenLayers/Control/SelectFeature.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
+ * @requires OpenLayers/Handler/Feature.js
+ */
+
+/**
+ * Class: OpenLayers.Control.SelectFeature
+ * Selects vector features from a given layer on click or hover.
+ *
+ * Inherits from:
+ * - <OpenLayers.Control>
+ */
+OpenLayers.Control.SelectFeature = OpenLayers.Class(OpenLayers.Control, {
+
+ /**
+ * Property: multipleKey
+ * {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets
+ * the <multiple> property to true. Default is null.
+ */
+ multipleKey: null,
+
+ /**
+ * Property: toggleKey
+ * {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets
+ * the <toggle> property to true. Default is null.
+ */
+ toggleKey: null,
+
+ /**
+ * APIProperty: multiple
+ * {Boolean} Allow selection of multiple geometries. Default is false.
+ */
+ multiple: false,
+
+ /**
+ * APIProperty: clickout
+ * {Boolean} Unselect features when clicking outside any feature.
+ * Default is true.
+ */
+ clickout: true,
+
+ /**
+ * APIProperty: toggle
+ * {Boolean} Unselect a selected feature on click. Default is false. Only
+ * has meaning if hover is false.
+ */
+ toggle: false,
+
+ /**
+ * APIProperty: hover
+ * {Boolean} Select on mouse over and deselect on mouse out. If true, this
+ * ignores clicks and only listens to mouse moves.
+ */
+ hover: false,
+
+ /**
+ * APIProperty: box
+ * {Boolean} Allow feature selection by drawing a box.
+ */
+ box: false,
+
+ /**
+ * APIProperty: onSelect
+ * {Function} Optional function to be called when a feature is selected.
+ * The function should expect to be called with a feature.
+ */
+ onSelect: function() {},
+
+ /**
+ * APIProperty: onUnselect
+ * {Function} Optional function to be called when a feature is unselected.
+ * The function should expect to be called with a feature.
+ */
+ onUnselect: function() {},
+
+ /**
+ * APIProperty: geometryTypes
+ * {Array(String)} To restrict selecting to a limited set of geometry types,
+ * send a list of strings corresponding to the geometry class names.
+ */
+ geometryTypes: null,
+
+ /**
+ * Property: layer
+ * {<OpenLayers.Layer.Vector>}
+ */
+ layer: null,
+
+ /**
+ * APIProperty: callbacks
+ * {Object} The functions that are sent to the handlers.feature for callback
+ */
+ callbacks: null,
+
+ /**
+ * APIProperty: selectStyle
+ * {Object} Hash of styles
+ */
+ selectStyle: null,
+
+ /**
+ * Property: renderIntent
+ * {String} key used to retrieve the select style from the layer's
+ * style map.
+ */
+ renderIntent: "select",
+
+ /**
+ * Property: handlers
+ * {Object} Object with references to multiple <OpenLayers.Handler>
+ * instances.
+ */
+ handlers: null,
+
+ /**
+ * Constructor: <OpenLayers.Control.SelectFeature>
+ *
+ * Parameters:
+ * layer - {<OpenLayers.Layer.Vector>}
+ * options - {Object}
+ */
+ initialize: function(layer, options) {
+ OpenLayers.Control.prototype.initialize.apply(this, [options]);
+ this.layer = layer;
+ var callbacks = {
+ click: this.clickFeature,
+ clickout: this.clickoutFeature
+ };
+ if (this.hover) {
+ callbacks.over = this.overFeature;
+ callbacks.out = this.outFeature;
+ }
+
+ this.callbacks = OpenLayers.Util.extend(callbacks, this.callbacks);
+ this.handlers = {
+ feature: new OpenLayers.Handler.Feature(
+ this, layer, this.callbacks, {geometryTypes: this.geometryTypes}
+ )
+ };
+
+ if (this.box) {
+ this.handlers.box = new OpenLayers.Handler.Box(
+ this, {done: this.selectBox},
+ {boxDivClassName: "olHandlerBoxSelectFeature"}
+ );
+ }
+ },
+
+ /**
+ * Method: activate
+ * Activates the control.
+ *
+ * Returns:
+ * {Boolean} The control was effectively activated.
+ */
+ activate: function () {
+ if (!this.active) {
+ this.handlers.feature.activate();
+ if(this.box && this.handlers.box) {
+ this.handlers.box.activate();
+ }
+ }
+ return OpenLayers.Control.prototype.activate.apply(
+ this, arguments
+ );
+ },
+
+ /**
+ * Method: deactivate
+ * Deactivates the control.
+ *
+ * Returns:
+ * {Boolean} The control was effectively deactivated.
+ */
+ deactivate: function () {
+ if (this.active) {
+ this.handlers.feature.deactivate();
+ if(this.handlers.box) {
+ this.handlers.box.deactivate();
+ }
+ }
+ return OpenLayers.Control.prototype.deactivate.apply(
+ this, arguments
+ );
+ },
+
+ /**
+ * Method: unselectAll
+ * Unselect all selected features. To unselect all except for a single
+ * feature, set the options.except property to the feature.
+ *
+ * Parameters:
+ * options - {Object} Optional configuration object.
+ */
+ unselectAll: function(options) {
+ // we'll want an option to supress notification here
+ var feature;
+ for(var i=this.layer.selectedFeatures.length-1; i>=0; --i) {
+ feature = this.layer.selectedFeatures[i];
+ if(!options || options.except != feature) {
+ this.unselect(feature);
+ }
+ }
+ },
+
+ /**
+ * Method: clickFeature
+ * Called on click in a feature
+ * Only responds if this.hover is false.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>}
+ */
+ clickFeature: function(feature) {
+ if(!this.hover) {
+ var selected = (OpenLayers.Util.indexOf(this.layer.selectedFeatures,
+ feature) > -1);
+ if(selected) {
+ if(this.toggleSelect()) {
+ this.unselect(feature);
+ } else if(!this.multipleSelect()) {
+ this.unselectAll({except: feature});
+ }
+ } else {
+ if(!this.multipleSelect()) {
+ this.unselectAll({except: feature});
+ }
+ this.select(feature);
+ }
+ }
+ },
+
+ /**
+ * Method: multipleSelect
+ * Allow for multiple selected features based on <multiple> property and
+ * <multipleKey> event modifier.
+ *
+ * Returns:
+ * {Boolean} Allow for multiple selected features.
+ */
+ multipleSelect: function() {
+ return this.multiple || this.handlers.feature.evt[this.multipleKey];
+ },
+
+ /**
+ * Method: toggleSelect
+ * Event should toggle the selected state of a feature based on <toggle>
+ * property and <toggleKey> event modifier.
+ *
+ * Returns:
+ * {Boolean} Toggle the selected state of a feature.
+ */
+ toggleSelect: function() {
+ return this.toggle || this.handlers.feature.evt[this.toggleKey];
+ },
+
+ /**
+ * Method: clickoutFeature
+ * Called on click outside a previously clicked (selected) feature.
+ * Only responds if this.hover is false.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Vector.Feature>}
+ */
+ clickoutFeature: function(feature) {
+ if(!this.hover && this.clickout) {
+ this.unselectAll();
+ }
+ },
+
+ /**
+ * Method: overFeature
+ * Called on over a feature.
+ * Only responds if this.hover is true.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>}
+ */
+ overFeature: function(feature) {
+ if(this.hover &&
+ (OpenLayers.Util.indexOf(this.layer.selectedFeatures, feature) == -1)) {
+ this.select(feature);
+ }
+ },
+
+ /**
+ * Method: outFeature
+ * Called on out of a selected feature.
+ * Only responds if this.hover is true.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>}
+ */
+ outFeature: function(feature) {
+ if(this.hover) {
+ this.unselect(feature);
+ }
+ },
+
+ /**
+ * Method: select
+ * Add feature to the layer's selectedFeature array, render the feature as
+ * selected, and call the onSelect function.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>}
+ */
+ select: function(feature) {
+ var cont = this.layer.events.triggerEvent("beforefeatureselected", {
+ feature: feature
+ });
+ if(cont !== false) {
+ this.layer.selectedFeatures.push(feature);
+
+ var selectStyle = this.selectStyle || this.renderIntent;
+
+ this.layer.drawFeature(feature, selectStyle);
+ this.layer.events.triggerEvent("featureselected", {feature: feature});
+ this.onSelect(feature);
+ }
+ },
+
+ /**
+ * Method: unselect
+ * Remove feature from the layer's selectedFeature array, render the feature as
+ * normal, and call the onUnselect function.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>}
+ */
+ unselect: function(feature) {
+ // Store feature style for restoration later
+ this.layer.drawFeature(feature, "default");
+ OpenLayers.Util.removeItem(this.layer.selectedFeatures, feature);
+ this.layer.events.triggerEvent("featureunselected", {feature: feature});
+ this.onUnselect(feature);
+ },
+
+ /**
+ * Method: selectBox
+ * Callback from the handlers.box set up when <box> selection is true
+ * on.
+ *
+ * Parameters:
+ * position - {<OpenLayers.Bounds> || <OpenLayers.Pixel> }
+ */
+ selectBox: function(position) {
+ if (position instanceof OpenLayers.Bounds) {
+ var minXY = this.map.getLonLatFromPixel(
+ new OpenLayers.Pixel(position.left, position.bottom)
+ );
+ var maxXY = this.map.getLonLatFromPixel(
+ new OpenLayers.Pixel(position.right, position.top)
+ );
+ var bounds = new OpenLayers.Bounds(
+ minXY.lon, minXY.lat, maxXY.lon, maxXY.lat
+ );
+
+ // if multiple is false, first deselect currently selected features
+ if (!this.multipleSelect()) {
+ this.unselectAll();
+ }
+
+ // because we're using a box, we consider we want multiple selection
+ var prevMultiple = this.multiple;
+ this.multiple = true;
+ for(var i=0, len = this.layer.features.length; i<len; ++i) {
+ var feature = this.layer.features[i];
+ if (this.geometryTypes == null || OpenLayers.Util.indexOf(
+ this.geometryTypes, feature.geometry.CLASS_NAME) > -1) {
+ if (bounds.toGeometry().intersects(feature.geometry)) {
+ if (OpenLayers.Util.indexOf(this.layer.selectedFeatures, feature) == -1) {
+ this.select(feature);
+ }
+ }
+ }
+ }
+ this.multiple = prevMultiple;
+ }
+ },
+
+ /**
+ * Method: setMap
+ * Set the map property for the control.
+ *
+ * Parameters:
+ * map - {<OpenLayers.Map>}
+ */
+ setMap: function(map) {
+ this.handlers.feature.setMap(map);
+ if (this.box) {
+ this.handlers.box.setMap(map);
+ }
+ OpenLayers.Control.prototype.setMap.apply(this, arguments);
+ },
+
+ CLASS_NAME: "OpenLayers.Control.SelectFeature"
+});
+/* ======================================================================
OpenLayers/Control/ZoomBox.js
====================================================================== */
@@ -13931,6 +30531,1260 @@
CLASS_NAME: "OpenLayers.Control.ZoomBox"
});
/* ======================================================================
+ OpenLayers/Format/WKT.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/Format.js
+ * @requires OpenLayers/Feature/Vector.js
+ */
+
+/**
+ * Class: OpenLayers.Format.WKT
+ * Class for reading and writing Well-Known Text. Create a new instance
+ * with the <OpenLayers.Format.WKT> constructor.
+ *
+ * Inherits from:
+ * - <OpenLayers.Format>
+ */
+OpenLayers.Format.WKT = OpenLayers.Class(OpenLayers.Format, {
+
+ /**
+ * Constructor: OpenLayers.Format.WKT
+ * Create a new parser for WKT
+ *
+ * Parameters:
+ * options - {Object} An optional object whose properties will be set on
+ * this instance
+ *
+ * Returns:
+ * {<OpenLayers.Format.WKT>} A new WKT parser.
+ */
+ initialize: function(options) {
+ this.regExes = {
+ 'typeStr': /^\s*(\w+)\s*\(\s*(.*)\s*\)\s*$/,
+ 'spaces': /\s+/,
+ 'parenComma': /\)\s*,\s*\(/,
+ 'doubleParenComma': /\)\s*\)\s*,\s*\(\s*\(/, // can't use {2} here
+ 'trimParens': /^\s*\(?(.*?)\)?\s*$/
+ };
+ OpenLayers.Format.prototype.initialize.apply(this, [options]);
+ },
+
+ /**
+ * Method: read
+ * Deserialize a WKT string and return a vector feature or an
+ * array of vector features. Supports WKT for POINT, MULTIPOINT,
+ * LINESTRING, MULTILINESTRING, POLYGON, MULTIPOLYGON, and
+ * GEOMETRYCOLLECTION.
+ *
+ * Parameters:
+ * wkt - {String} A WKT string
+ *
+ * Returns:
+ * {<OpenLayers.Feature.Vector>|Array} A feature or array of features for
+ * GEOMETRYCOLLECTION WKT.
+ */
+ read: function(wkt) {
+ var features, type, str;
+ var matches = this.regExes.typeStr.exec(wkt);
+ if(matches) {
+ type = matches[1].toLowerCase();
+ str = matches[2];
+ if(this.parse[type]) {
+ features = this.parse[type].apply(this, [str]);
+ }
+ if (this.internalProjection && this.externalProjection) {
+ if (features &&
+ features.CLASS_NAME == "OpenLayers.Feature.Vector") {
+ features.geometry.transform(this.externalProjection,
+ this.internalProjection);
+ } else if (features &&
+ type != "geometrycollection" &&
+ typeof features == "object") {
+ for (var i=0, len=features.length; i<len; i++) {
+ var component = features[i];
+ component.geometry.transform(this.externalProjection,
+ this.internalProjection);
+ }
+ }
+ }
+ }
+ return features;
+ },
+
+ /**
+ * Method: write
+ * Serialize a feature or array of features into a WKT string.
+ *
+ * Parameters:
+ * features - {<OpenLayers.Feature.Vector>|Array} A feature or array of
+ * features
+ *
+ * Returns:
+ * {String} The WKT string representation of the input geometries
+ */
+ write: function(features) {
+ var collection, geometry, type, data, isCollection;
+ if(features.constructor == Array) {
+ collection = features;
+ isCollection = true;
+ } else {
+ collection = [features];
+ isCollection = false;
+ }
+ var pieces = [];
+ if(isCollection) {
+ pieces.push('GEOMETRYCOLLECTION(');
+ }
+ for(var i=0, len=collection.length; i<len; ++i) {
+ if(isCollection && i>0) {
+ pieces.push(',');
+ }
+ geometry = collection[i].geometry;
+ type = geometry.CLASS_NAME.split('.')[2].toLowerCase();
+ if(!this.extract[type]) {
+ return null;
+ }
+ if (this.internalProjection && this.externalProjection) {
+ geometry = geometry.clone();
+ geometry.transform(this.internalProjection,
+ this.externalProjection);
+ }
+ data = this.extract[type].apply(this, [geometry]);
+ pieces.push(type.toUpperCase() + '(' + data + ')');
+ }
+ if(isCollection) {
+ pieces.push(')');
+ }
+ return pieces.join('');
+ },
+
+ /**
+ * Object with properties corresponding to the geometry types.
+ * Property values are functions that do the actual data extraction.
+ */
+ extract: {
+ /**
+ * Return a space delimited string of point coordinates.
+ * @param {<OpenLayers.Geometry.Point>} point
+ * @returns {String} A string of coordinates representing the point
+ */
+ 'point': function(point) {
+ return point.x + ' ' + point.y;
+ },
+
+ /**
+ * Return a comma delimited string of point coordinates from a multipoint.
+ * @param {<OpenLayers.Geometry.MultiPoint>} multipoint
+ * @returns {String} A string of point coordinate strings representing
+ * the multipoint
+ */
+ 'multipoint': function(multipoint) {
+ var array = [];
+ for(var i=0, len=multipoint.components.length; i<len; ++i) {
+ array.push(this.extract.point.apply(this, [multipoint.components[i]]));
+ }
+ return array.join(',');
+ },
+
+ /**
+ * Return a comma delimited string of point coordinates from a line.
+ * @param {<OpenLayers.Geometry.LineString>} linestring
+ * @returns {String} A string of point coordinate strings representing
+ * the linestring
+ */
+ 'linestring': function(linestring) {
+ var array = [];
+ for(var i=0, len=linestring.components.length; i<len; ++i) {
+ array.push(this.extract.point.apply(this, [linestring.components[i]]));
+ }
+ return array.join(',');
+ },
+
+ /**
+ * Return a comma delimited string of linestring strings from a multilinestring.
+ * @param {<OpenLayers.Geometry.MultiLineString>} multilinestring
+ * @returns {String} A string of of linestring strings representing
+ * the multilinestring
+ */
+ 'multilinestring': function(multilinestring) {
+ var array = [];
+ for(var i=0, len=multilinestring.components.length; i<len; ++i) {
+ array.push('(' +
+ this.extract.linestring.apply(this, [multilinestring.components[i]]) +
+ ')');
+ }
+ return array.join(',');
+ },
+
+ /**
+ * Return a comma delimited string of linear ring arrays from a polygon.
+ * @param {<OpenLayers.Geometry.Polygon>} polygon
+ * @returns {String} An array of linear ring arrays representing the polygon
+ */
+ 'polygon': function(polygon) {
+ var array = [];
+ for(var i=0, len=polygon.components.length; i<len; ++i) {
+ array.push('(' +
+ this.extract.linestring.apply(this, [polygon.components[i]]) +
+ ')');
+ }
+ return array.join(',');
+ },
+
+ /**
+ * Return an array of polygon arrays from a multipolygon.
+ * @param {<OpenLayers.Geometry.MultiPolygon>} multipolygon
+ * @returns {Array} An array of polygon arrays representing
+ * the multipolygon
+ */
+ 'multipolygon': function(multipolygon) {
+ var array = [];
+ for(var i=0, len=multipolygon.components.length; i<len; ++i) {
+ array.push('(' +
+ this.extract.polygon.apply(this, [multipolygon.components[i]]) +
+ ')');
+ }
+ return array.join(',');
+ }
+
+ },
+
+ /**
+ * Object with properties corresponding to the geometry types.
+ * Property values are functions that do the actual parsing.
+ */
+ parse: {
+ /**
+ * Return point feature given a point WKT fragment.
+ * @param {String} str A WKT fragment representing the point
+ * @returns {<OpenLayers.Feature.Vector>} A point feature
+ * @private
+ */
+ 'point': function(str) {
+ var coords = OpenLayers.String.trim(str).split(this.regExes.spaces);
+ return new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(coords[0], coords[1])
+ );
+ },
+
+ /**
+ * Return a multipoint feature given a multipoint WKT fragment.
+ * @param {String} A WKT fragment representing the multipoint
+ * @returns {<OpenLayers.Feature.Vector>} A multipoint feature
+ * @private
+ */
+ 'multipoint': function(str) {
+ var points = OpenLayers.String.trim(str).split(',');
+ var components = [];
+ for(var i=0, len=points.length; i<len; ++i) {
+ components.push(this.parse.point.apply(this, [points[i]]).geometry);
+ }
+ return new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.MultiPoint(components)
+ );
+ },
+
+ /**
+ * Return a linestring feature given a linestring WKT fragment.
+ * @param {String} A WKT fragment representing the linestring
+ * @returns {<OpenLayers.Feature.Vector>} A linestring feature
+ * @private
+ */
+ 'linestring': function(str) {
+ var points = OpenLayers.String.trim(str).split(',');
+ var components = [];
+ for(var i=0, len=points.length; i<len; ++i) {
+ components.push(this.parse.point.apply(this, [points[i]]).geometry);
+ }
+ return new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.LineString(components)
+ );
+ },
+
+ /**
+ * Return a multilinestring feature given a multilinestring WKT fragment.
+ * @param {String} A WKT fragment representing the multilinestring
+ * @returns {<OpenLayers.Feature.Vector>} A multilinestring feature
+ * @private
+ */
+ 'multilinestring': function(str) {
+ var line;
+ var lines = OpenLayers.String.trim(str).split(this.regExes.parenComma);
+ var components = [];
+ for(var i=0, len=lines.length; i<len; ++i) {
+ line = lines[i].replace(this.regExes.trimParens, '$1');
+ components.push(this.parse.linestring.apply(this, [line]).geometry);
+ }
+ return new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.MultiLineString(components)
+ );
+ },
+
+ /**
+ * Return a polygon feature given a polygon WKT fragment.
+ * @param {String} A WKT fragment representing the polygon
+ * @returns {<OpenLayers.Feature.Vector>} A polygon feature
+ * @private
+ */
+ 'polygon': function(str) {
+ var ring, linestring, linearring;
+ var rings = OpenLayers.String.trim(str).split(this.regExes.parenComma);
+ var components = [];
+ for(var i=0, len=rings.length; i<len; ++i) {
+ ring = rings[i].replace(this.regExes.trimParens, '$1');
+ linestring = this.parse.linestring.apply(this, [ring]).geometry;
+ linearring = new OpenLayers.Geometry.LinearRing(linestring.components);
+ components.push(linearring);
+ }
+ return new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Polygon(components)
+ );
+ },
+
+ /**
+ * Return a multipolygon feature given a multipolygon WKT fragment.
+ * @param {String} A WKT fragment representing the multipolygon
+ * @returns {<OpenLayers.Feature.Vector>} A multipolygon feature
+ * @private
+ */
+ 'multipolygon': function(str) {
+ var polygon;
+ var polygons = OpenLayers.String.trim(str).split(this.regExes.doubleParenComma);
+ var components = [];
+ for(var i=0, len=polygons.length; i<len; ++i) {
+ polygon = polygons[i].replace(this.regExes.trimParens, '$1');
+ components.push(this.parse.polygon.apply(this, [polygon]).geometry);
+ }
+ return new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.MultiPolygon(components)
+ );
+ },
+
+ /**
+ * Return an array of features given a geometrycollection WKT fragment.
+ * @param {String} A WKT fragment representing the geometrycollection
+ * @returns {Array} An array of OpenLayers.Feature.Vector
+ * @private
+ */
+ 'geometrycollection': function(str) {
+ // separate components of the collection with |
+ str = str.replace(/,\s*([A-Za-z])/g, '|$1');
+ var wktArray = OpenLayers.String.trim(str).split('|');
+ var components = [];
+ for(var i=0, len=wktArray.length; i<len; ++i) {
+ components.push(OpenLayers.Format.WKT.prototype.read.apply(this,[wktArray[i]]));
+ }
+ return components;
+ }
+
+ },
+
+ CLASS_NAME: "OpenLayers.Format.WKT"
+});
+/* ======================================================================
+ OpenLayers/Layer/Boxes.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/Layer.js
+ * @requires OpenLayers/Layer/Markers.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.Boxes
+ * Draw divs as 'boxes' on the layer.
+ *
+ * Inherits from:
+ * - <OpenLayers.Layer.Markers>
+ */
+OpenLayers.Layer.Boxes = OpenLayers.Class(OpenLayers.Layer.Markers, {
+
+ /**
+ * Constructor: OpenLayers.Layer.Boxes
+ *
+ * Parameters:
+ * name - {String}
+ * options - {Object} Hashtable of extra options to tag onto the layer
+ */
+ initialize: function (name, options) {
+ OpenLayers.Layer.Markers.prototype.initialize.apply(this, arguments);
+ },
+
+ /**
+ * Method: drawMarker
+ * Calculate the pixel location for the marker, create it, and
+ * add it to the layer's div
+ *
+ * Parameters:
+ * marker - {<OpenLayers.Marker.Box>}
+ */
+ drawMarker: function(marker) {
+ var bounds = marker.bounds;
+ var topleft = this.map.getLayerPxFromLonLat(
+ new OpenLayers.LonLat(bounds.left, bounds.top));
+ var botright = this.map.getLayerPxFromLonLat(
+ new OpenLayers.LonLat(bounds.right, bounds.bottom));
+ if (botright == null || topleft == null) {
+ marker.display(false);
+ } else {
+ var sz = new OpenLayers.Size(
+ Math.max(1, botright.x - topleft.x),
+ Math.max(1, botright.y - topleft.y));
+ var markerDiv = marker.draw(topleft, sz);
+ if (!marker.drawn) {
+ this.div.appendChild(markerDiv);
+ marker.drawn = true;
+ }
+ }
+ },
+
+
+ /**
+ * APIMethod: removeMarker
+ *
+ * Parameters:
+ * marker - {<OpenLayers.Marker.Box>}
+ */
+ removeMarker: function(marker) {
+ OpenLayers.Util.removeItem(this.markers, marker);
+ if ((marker.div != null) &&
+ (marker.div.parentNode == this.div) ) {
+ this.div.removeChild(marker.div);
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Layer.Boxes"
+});
+/* ======================================================================
+ OpenLayers/Layer/GeoRSS.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/Layer/Markers.js
+ * @requires OpenLayers/Request/XMLHttpRequest.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.GeoRSS
+ * Add GeoRSS Point features to your map.
+ *
+ * Inherits from:
+ * - <OpenLayers.Layer.Markers>
+ * - <OpenLayers.Layer>
+ */
+OpenLayers.Layer.GeoRSS = OpenLayers.Class(OpenLayers.Layer.Markers, {
+
+ /**
+ * Property: location
+ * {String} store url of text file
+ */
+ location: null,
+
+ /**
+ * Property: features
+ * {Array(<OpenLayers.Feature>)}
+ */
+ features: null,
+
+ /**
+ * APIProperty: formatOptions
+ * {Object} Hash of options which should be passed to the format when it is
+ * created. Must be passed in the constructor.
+ */
+ formatOptions: null,
+
+ /**
+ * Property: selectedFeature
+ * {<OpenLayers.Feature>}
+ */
+ selectedFeature: null,
+
+ /**
+ * APIProperty: icon
+ * {<OpenLayers.Icon>}. This determines the Icon to be used on the map
+ * for this GeoRSS layer.
+ */
+ icon: null,
+
+ /**
+ * APIProperty: popupSize
+ * {<OpenLayers.Size>} This determines the size of GeoRSS popups. If
+ * not provided, defaults to 250px by 120px.
+ */
+ popupSize: null,
+
+ /**
+ * APIProperty: useFeedTitle
+ * {Boolean} Set layer.name to the first <title> element in the feed. Default is true.
+ */
+ useFeedTitle: true,
+
+ /**
+ * Constructor: OpenLayers.Layer.GeoRSS
+ * Create a GeoRSS Layer.
+ *
+ * Parameters:
+ * name - {String}
+ * location - {String}
+ * options - {Object}
+ */
+ initialize: function(name, location, options) {
+ OpenLayers.Layer.Markers.prototype.initialize.apply(this, [name, options]);
+ this.location = location;
+ this.features = [];
+ },
+
+ /**
+ * Method: destroy
+ */
+ destroy: function() {
+ // Warning: Layer.Markers.destroy() must be called prior to calling
+ // clearFeatures() here, otherwise we leak memory. Indeed, if
+ // Layer.Markers.destroy() is called after clearFeatures(), it won't be
+ // able to remove the marker image elements from the layer's div since
+ // the markers will have been destroyed by clearFeatures().
+ OpenLayers.Layer.Markers.prototype.destroy.apply(this, arguments);
+ this.clearFeatures();
+ this.features = null;
+ },
+
+ /**
+ * Method: loadRSS
+ * Start the load of the RSS data. Don't do this when we first add the layer,
+ * since we may not be visible at any point, and it would therefore be a waste.
+ */
+ loadRSS: function() {
+ if (!this.loaded) {
+ this.events.triggerEvent("loadstart");
+ OpenLayers.Request.GET({
+ url: this.location,
+ success: this.parseData,
+ scope: this
+ });
+ this.loaded = true;
+ }
+ },
+
+ /**
+ * Method: moveTo
+ * If layer is visible and RSS has not been loaded, load RSS.
+ *
+ * Parameters:
+ * bounds - {Object}
+ * zoomChanged - {Object}
+ * minor - {Object}
+ */
+ moveTo:function(bounds, zoomChanged, minor) {
+ OpenLayers.Layer.Markers.prototype.moveTo.apply(this, arguments);
+ if(this.visibility && !this.loaded){
+ this.loadRSS();
+ }
+ },
+
+ /**
+ * Method: parseData
+ * Parse the data returned from the Events call.
+ *
+ * Parameters:
+ * ajaxRequest - {<OpenLayers.Request.XMLHttpRequest>}
+ */
+ parseData: function(ajaxRequest) {
+ var doc = ajaxRequest.responseXML;
+ if (!doc || !doc.documentElement) {
+ doc = OpenLayers.Format.XML.prototype.read(ajaxRequest.responseText);
+ }
+
+ if (this.useFeedTitle) {
+ var name = null;
+ try {
+ name = doc.getElementsByTagNameNS('*', 'title')[0].firstChild.nodeValue;
+ }
+ catch (e) {
+ name = doc.getElementsByTagName('title')[0].firstChild.nodeValue;
+ }
+ if (name) {
+ this.setName(name);
+ }
+ }
+
+ var options = {};
+
+ OpenLayers.Util.extend(options, this.formatOptions);
+
+ if (this.map && !this.projection.equals(this.map.getProjectionObject())) {
+ options.externalProjection = this.projection;
+ options.internalProjection = this.map.getProjectionObject();
+ }
+
+ var format = new OpenLayers.Format.GeoRSS(options);
+ var features = format.read(doc);
+
+ for (var i=0, len=features.length; i<len; i++) {
+ var data = {};
+ var feature = features[i];
+
+ // we don't support features with no geometry in the GeoRSS
+ // layer at this time.
+ if (!feature.geometry) {
+ continue;
+ }
+
+ var title = feature.attributes.title ?
+ feature.attributes.title : "Untitled";
+
+ var description = feature.attributes.description ?
+ feature.attributes.description : "No description.";
+
+ var link = feature.attributes.link ? feature.attributes.link : "";
+
+ var location = feature.geometry.getBounds().getCenterLonLat();
+
+
+ data.icon = this.icon == null ?
+ OpenLayers.Marker.defaultIcon() :
+ this.icon.clone();
+
+ data.popupSize = this.popupSize ?
+ this.popupSize.clone() :
+ new OpenLayers.Size(250, 120);
+
+ if (title || description) {
+ // we have supplemental data, store them.
+ data.title = title;
+ data.description = description;
+
+ var contentHTML = '<div class="olLayerGeoRSSClose">[x]</div>';
+ contentHTML += '<div class="olLayerGeoRSSTitle">';
+ if (link) {
+ contentHTML += '<a class="link" href="'+link+'" target="_blank">';
+ }
+ contentHTML += title;
+ if (link) {
+ contentHTML += '</a>';
+ }
+ contentHTML += '</div>';
+ contentHTML += '<div style="" class="olLayerGeoRSSDescription">';
+ contentHTML += description;
+ contentHTML += '</div>';
+ data['popupContentHTML'] = contentHTML;
+ }
+ var feature = new OpenLayers.Feature(this, location, data);
+ this.features.push(feature);
+ var marker = feature.createMarker();
+ marker.events.register('click', feature, this.markerClick);
+ this.addMarker(marker);
+ }
+ this.events.triggerEvent("loadend");
+ },
+
+ /**
+ * Method: markerClick
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ markerClick: function(evt) {
+ var sameMarkerClicked = (this == this.layer.selectedFeature);
+ this.layer.selectedFeature = (!sameMarkerClicked) ? this : null;
+ for(var i=0, len=this.layer.map.popups.length; i<len; i++) {
+ this.layer.map.removePopup(this.layer.map.popups[i]);
+ }
+ if (!sameMarkerClicked) {
+ var popup = this.createPopup();
+ OpenLayers.Event.observe(popup.div, "click",
+ OpenLayers.Function.bind(function() {
+ for(var i=0, len=this.layer.map.popups.length; i<len; i++) {
+ this.layer.map.removePopup(this.layer.map.popups[i]);
+ }
+ }, this)
+ );
+ this.layer.map.addPopup(popup);
+ }
+ OpenLayers.Event.stop(evt);
+ },
+
+ /**
+ * Method: clearFeatures
+ * Destroy all features in this layer.
+ */
+ clearFeatures: function() {
+ if (this.features != null) {
+ while(this.features.length > 0) {
+ var feature = this.features[0];
+ OpenLayers.Util.removeItem(this.features, feature);
+ feature.destroy();
+ }
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Layer.GeoRSS"
+});
+/* ======================================================================
+ OpenLayers/Layer/Google.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/Layer/SphericalMercator.js
+ * @requires OpenLayers/Layer/EventPane.js
+ * @requires OpenLayers/Layer/FixedZoomLevels.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.Google
+ *
+ * Inherits from:
+ * - <OpenLayers.Layer.SphericalMercator>
+ * - <OpenLayers.Layer.EventPane>
+ * - <OpenLayers.Layer.FixedZoomLevels>
+ */
+OpenLayers.Layer.Google = OpenLayers.Class(
+ OpenLayers.Layer.EventPane,
+ OpenLayers.Layer.FixedZoomLevels, {
+
+ /**
+ * Constant: MIN_ZOOM_LEVEL
+ * {Integer} 0
+ */
+ MIN_ZOOM_LEVEL: 0,
+
+ /**
+ * Constant: MAX_ZOOM_LEVEL
+ * {Integer} 19
+ */
+ MAX_ZOOM_LEVEL: 19,
+
+ /**
+ * Constant: RESOLUTIONS
+ * {Array(Float)} Hardcode these resolutions so that they are more closely
+ * tied with the standard wms projection
+ */
+ RESOLUTIONS: [
+ 1.40625,
+ 0.703125,
+ 0.3515625,
+ 0.17578125,
+ 0.087890625,
+ 0.0439453125,
+ 0.02197265625,
+ 0.010986328125,
+ 0.0054931640625,
+ 0.00274658203125,
+ 0.001373291015625,
+ 0.0006866455078125,
+ 0.00034332275390625,
+ 0.000171661376953125,
+ 0.0000858306884765625,
+ 0.00004291534423828125,
+ 0.00002145767211914062,
+ 0.00001072883605957031,
+ 0.00000536441802978515,
+ 0.00000268220901489257
+ ],
+
+ /**
+ * APIProperty: type
+ * {GMapType}
+ */
+ type: null,
+
+ /**
+ * APIProperty: sphericalMercator
+ * {Boolean} Should the map act as a mercator-projected map? This will
+ * cause all interactions with the map to be in the actual map
+ * projection, which allows support for vector drawing, overlaying
+ * other maps, etc.
+ */
+ sphericalMercator: false,
+
+ /**
+ * Property: dragObject
+ * {GDraggableObject} Since 2.93, Google has exposed the ability to get
+ * the maps GDraggableObject. We can now use this for smooth panning
+ */
+ dragObject: null,
+
+ /**
+ * Constructor: OpenLayers.Layer.Google
+ *
+ * Parameters:
+ * name - {String} A name for the layer.
+ * options - {Object} An optional object whose properties will be set
+ * on the layer.
+ */
+ initialize: function(name, options) {
+ OpenLayers.Layer.EventPane.prototype.initialize.apply(this, arguments);
+ OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this,
+ arguments);
+ this.addContainerPxFunction();
+ if (this.sphericalMercator) {
+ OpenLayers.Util.extend(this, OpenLayers.Layer.SphericalMercator);
+ this.initMercatorParameters();
+ }
+ },
+
+ /**
+ * Method: loadMapObject
+ * Load the GMap and register appropriate event listeners. If we can't
+ * load GMap2, then display a warning message.
+ */
+ loadMapObject:function() {
+
+ //has gmaps library has been loaded?
+ try {
+ // create GMap, hide nav controls
+ this.mapObject = new GMap2( this.div );
+
+ //since v 2.93 getDragObject is now available.
+ if(typeof this.mapObject.getDragObject == "function") {
+ this.dragObject = this.mapObject.getDragObject();
+ } else {
+ this.dragPanMapObject = null;
+ }
+
+
+ // move the ToS and branding stuff up to the pane
+ // thanks a *mil* Erik for thinking of this
+ var poweredBy = this.div.lastChild;
+ this.div.removeChild(poweredBy);
+ this.pane.appendChild(poweredBy);
+ poweredBy.className = "olLayerGooglePoweredBy gmnoprint";
+ poweredBy.style.left = "";
+ poweredBy.style.bottom = "";
+
+ var termsOfUse = this.div.lastChild;
+ this.div.removeChild(termsOfUse);
+ this.pane.appendChild(termsOfUse);
+ termsOfUse.className = "olLayerGoogleCopyright";
+ termsOfUse.style.right = "";
+ termsOfUse.style.bottom = "";
+
+ } catch (e) {
+ OpenLayers.Console.error(e);
+ }
+
+ },
+
+ /**
+ * APIMethod: setMap
+ * Overridden from EventPane because if a map type has been specified,
+ * we need to attach a listener for the first moveend -- this is how
+ * we will know that the map has been centered. Only once the map has
+ * been centered is it safe to change the gmap object's map type.
+ *
+ * Parameters:
+ * map - {<OpenLayers.Map>}
+ */
+ setMap: function(map) {
+ OpenLayers.Layer.EventPane.prototype.setMap.apply(this, arguments);
+
+ if (this.type != null) {
+ this.map.events.register("moveend", this, this.setMapType);
+ }
+ },
+
+ /**
+ * Method: setMapType
+ * The map has been centered, and a map type was specified, so we
+ * set the map type on the gmap object, then unregister the listener
+ * so that we dont keep doing this every time the map moves.
+ */
+ setMapType: function() {
+ if (this.mapObject.getCenter() != null) {
+
+ // Support for custom map types.
+ if (OpenLayers.Util.indexOf(this.mapObject.getMapTypes(),
+ this.type) == -1) {
+ this.mapObject.addMapType(this.type);
+ }
+
+ this.mapObject.setMapType(this.type);
+ this.map.events.unregister("moveend", this, this.setMapType);
+ }
+ },
+
+ /**
+ * APIMethod: onMapResize
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ onMapResize: function() {
+ if(this.visibility) {
+ this.mapObject.checkResize();
+ } else {
+ this.windowResized = true;
+ }
+ },
+
+ /**
+ * Method: display
+ * Hide or show the layer
+ *
+ * Parameters:
+ * display - {Boolean}
+ */
+ display: function(display) {
+ OpenLayers.Layer.EventPane.prototype.display.apply(this, arguments);
+ if(this.div.style.display == "block" && this.windowResized) {
+ this.mapObject.checkResize();
+ this.windowResized = false;
+ }
+ },
+
+ /**
+ * APIMethod: getZoomForExtent
+ *
+ * Parameters:
+ * bounds - {<OpenLayers.Bounds>}
+ *
+ * Returns:
+ * {Integer} Corresponding zoom level for a specified Bounds.
+ * If mapObject is not loaded or not centered, returns null
+ *
+ getZoomForExtent: function (bounds) {
+ var zoom = null;
+ if (this.mapObject != null) {
+ var moBounds = this.getMapObjectBoundsFromOLBounds(bounds);
+ var moZoom = this.getMapObjectZoomFromMapObjectBounds(moBounds);
+
+ //make sure zoom is within bounds
+ var moZoom = Math.min(Math.max(moZoom, this.minZoomLevel),
+ this.maxZoomLevel);
+
+ zoom = this.getOLZoomFromMapObjectZoom(moZoom);
+ }
+ return zoom;
+ },
+
+ */
+
+ //
+ // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds
+ //
+
+ /**
+ * APIMethod: getOLBoundsFromMapObjectBounds
+ *
+ * Parameters:
+ * moBounds - {Object}
+ *
+ * Returns:
+ * {<OpenLayers.Bounds>} An <OpenLayers.Bounds>, translated from the
+ * passed-in MapObject Bounds.
+ * Returns null if null value is passed in.
+ */
+ getOLBoundsFromMapObjectBounds: function(moBounds) {
+ var olBounds = null;
+ if (moBounds != null) {
+ var sw = moBounds.getSouthWest();
+ var ne = moBounds.getNorthEast();
+ if (this.sphericalMercator) {
+ sw = this.forwardMercator(sw.lng(), sw.lat());
+ ne = this.forwardMercator(ne.lng(), ne.lat());
+ } else {
+ sw = new OpenLayers.LonLat(sw.lng(), sw.lat());
+ ne = new OpenLayers.LonLat(ne.lng(), ne.lat());
+ }
+ olBounds = new OpenLayers.Bounds(sw.lon,
+ sw.lat,
+ ne.lon,
+ ne.lat );
+ }
+ return olBounds;
+ },
+
+ /**
+ * APIMethod: getMapObjectBoundsFromOLBounds
+ *
+ * Parameters:
+ * olBounds - {<OpenLayers.Bounds>}
+ *
+ * Returns:
+ * {Object} A MapObject Bounds, translated from olBounds
+ * Returns null if null value is passed in
+ */
+ getMapObjectBoundsFromOLBounds: function(olBounds) {
+ var moBounds = null;
+ if (olBounds != null) {
+ var sw = this.sphericalMercator ?
+ this.inverseMercator(olBounds.bottom, olBounds.left) :
+ new OpenLayers.LonLat(olBounds.bottom, olBounds.left);
+ var ne = this.sphericalMercator ?
+ this.inverseMercator(olBounds.top, olBounds.right) :
+ new OpenLayers.LonLat(olBounds.top, olBounds.right);
+ moBounds = new GLatLngBounds(new GLatLng(sw.lat, sw.lon),
+ new GLatLng(ne.lat, ne.lon));
+ }
+ return moBounds;
+ },
+
+ /**
+ * Method: addContainerPxFunction
+ * Hack-on function because GMAPS does not give it to us
+ *
+ * Parameters:
+ * gLatLng - {GLatLng}
+ *
+ * Returns:
+ * {GPoint} A GPoint specifying gLatLng translated into "Container" coords
+ */
+ addContainerPxFunction: function() {
+ if ( (typeof GMap2 != "undefined") &&
+ !GMap2.prototype.fromLatLngToContainerPixel) {
+
+ GMap2.prototype.fromLatLngToContainerPixel = function(gLatLng) {
+
+ // first we translate into "DivPixel"
+ var gPoint = this.fromLatLngToDivPixel(gLatLng);
+
+ // locate the sliding "Div" div
+ var div = this.getContainer().firstChild.firstChild;
+
+ // adjust by the offset of "Div" and voila!
+ gPoint.x += div.offsetLeft;
+ gPoint.y += div.offsetTop;
+
+ return gPoint;
+ };
+ }
+ },
+
+ /**
+ * APIMethod: getWarningHTML
+ *
+ * Returns:
+ * {String} String with information on why layer is broken, how to get
+ * it working.
+ */
+ getWarningHTML:function() {
+ return OpenLayers.i18n("googleWarning");
+ },
+
+
+ /************************************
+ * *
+ * MapObject Interface Controls *
+ * *
+ ************************************/
+
+
+ // Get&Set Center, Zoom
+
+ /**
+ * APIMethod: setMapObjectCenter
+ * Set the mapObject to the specified center and zoom
+ *
+ * Parameters:
+ * center - {Object} MapObject LonLat format
+ * zoom - {int} MapObject zoom format
+ */
+ setMapObjectCenter: function(center, zoom) {
+ this.mapObject.setCenter(center, zoom);
+ },
+
+ /**
+ * APIMethod: dragPanMapObject
+ *
+ * Parameters:
+ * dX - {Integer}
+ * dY - {Integer}
+ */
+ dragPanMapObject: function(dX, dY) {
+ this.dragObject.moveBy(new GSize(-dX, dY));
+ },
+
+ /**
+ * APIMethod: getMapObjectCenter
+ *
+ * Returns:
+ * {Object} The mapObject's current center in Map Object format
+ */
+ getMapObjectCenter: function() {
+ return this.mapObject.getCenter();
+ },
+
+ /**
+ * APIMethod: getMapObjectZoom
+ *
+ * Returns:
+ * {Integer} The mapObject's current zoom, in Map Object format
+ */
+ getMapObjectZoom: function() {
+ return this.mapObject.getZoom();
+ },
+
+
+ // LonLat - Pixel Translation
+
+ /**
+ * APIMethod: getMapObjectLonLatFromMapObjectPixel
+ *
+ * Parameters:
+ * moPixel - {Object} MapObject Pixel format
+ *
+ * Returns:
+ * {Object} MapObject LonLat translated from MapObject Pixel
+ */
+ getMapObjectLonLatFromMapObjectPixel: function(moPixel) {
+ return this.mapObject.fromContainerPixelToLatLng(moPixel);
+ },
+
+ /**
+ * APIMethod: getMapObjectPixelFromMapObjectLonLat
+ *
+ * Parameters:
+ * moLonLat - {Object} MapObject LonLat format
+ *
+ * Returns:
+ * {Object} MapObject Pixel transtlated from MapObject LonLat
+ */
+ getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {
+ return this.mapObject.fromLatLngToContainerPixel(moLonLat);
+ },
+
+
+ // Bounds
+
+ /**
+ * APIMethod: getMapObjectZoomFromMapObjectBounds
+ *
+ * Parameters:
+ * moBounds - {Object} MapObject Bounds format
+ *
+ * Returns:
+ * {Object} MapObject Zoom for specified MapObject Bounds
+ */
+ getMapObjectZoomFromMapObjectBounds: function(moBounds) {
+ return this.mapObject.getBoundsZoomLevel(moBounds);
+ },
+
+ /************************************
+ * *
+ * MapObject Primitives *
+ * *
+ ************************************/
+
+
+ // LonLat
+
+ /**
+ * APIMethod: getLongitudeFromMapObjectLonLat
+ *
+ * Parameters:
+ * moLonLat - {Object} MapObject LonLat format
+ *
+ * Returns:
+ * {Float} Longitude of the given MapObject LonLat
+ */
+ getLongitudeFromMapObjectLonLat: function(moLonLat) {
+ return this.sphericalMercator ?
+ this.forwardMercator(moLonLat.lng(), moLonLat.lat()).lon :
+ moLonLat.lng();
+ },
+
+ /**
+ * APIMethod: getLatitudeFromMapObjectLonLat
+ *
+ * Parameters:
+ * moLonLat - {Object} MapObject LonLat format
+ *
+ * Returns:
+ * {Float} Latitude of the given MapObject LonLat
+ */
+ getLatitudeFromMapObjectLonLat: function(moLonLat) {
+ var lat = this.sphericalMercator ?
+ this.forwardMercator(moLonLat.lng(), moLonLat.lat()).lat :
+ moLonLat.lat();
+ return lat;
+ },
+
+ /**
+ * APIMethod: getMapObjectLonLatFromLonLat
+ *
+ * Parameters:
+ * lon - {Float}
+ * lat - {Float}
+ *
+ * Returns:
+ * {Object} MapObject LonLat built from lon and lat params
+ */
+ getMapObjectLonLatFromLonLat: function(lon, lat) {
+ var gLatLng;
+ if(this.sphericalMercator) {
+ var lonlat = this.inverseMercator(lon, lat);
+ gLatLng = new GLatLng(lonlat.lat, lonlat.lon);
+ } else {
+ gLatLng = new GLatLng(lat, lon);
+ }
+ return gLatLng;
+ },
+
+ // Pixel
+
+ /**
+ * APIMethod: getXFromMapObjectPixel
+ *
+ * Parameters:
+ * moPixel - {Object} MapObject Pixel format
+ *
+ * Returns:
+ * {Integer} X value of the MapObject Pixel
+ */
+ getXFromMapObjectPixel: function(moPixel) {
+ return moPixel.x;
+ },
+
+ /**
+ * APIMethod: getYFromMapObjectPixel
+ *
+ * Parameters:
+ * moPixel - {Object} MapObject Pixel format
+ *
+ * Returns:
+ * {Integer} Y value of the MapObject Pixel
+ */
+ getYFromMapObjectPixel: function(moPixel) {
+ return moPixel.y;
+ },
+
+ /**
+ * APIMethod: getMapObjectPixelFromXY
+ *
+ * Parameters:
+ * x - {Integer}
+ * y - {Integer}
+ *
+ * Returns:
+ * {Object} MapObject Pixel from x and y parameters
+ */
+ getMapObjectPixelFromXY: function(x, y) {
+ return new GPoint(x, y);
+ },
+
+ CLASS_NAME: "OpenLayers.Layer.Google"
+});
+/* ======================================================================
OpenLayers/Layer/Grid.js
====================================================================== */
@@ -14037,9 +31891,9 @@
*/
clearGrid:function() {
if (this.grid) {
- for(var iRow=0; iRow < this.grid.length; iRow++) {
+ for(var iRow=0, len=this.grid.length; iRow<len; iRow++) {
var row = this.grid[iRow];
- for(var iCol=0; iCol < row.length; iCol++) {
+ for(var iCol=0, clen=row.length; iCol<clen; iCol++) {
var tile = row[iCol];
this.removeTileMonitoringHooks(tile);
tile.destroy();
@@ -14289,7 +32143,7 @@
var minCols = Math.ceil(viewSize.w/this.tileSize.w) +
Math.max(1, 2 * this.buffer);
- var extent = this.map.getMaxExtent();
+ var extent = this.maxExtent;
var resolution = this.map.getResolution();
var tileLayout = this.calculateGridLayout(bounds, extent, resolution);
@@ -14430,7 +32284,7 @@
}
// now we go through and draw the tiles in forward order
- for(var i=0; i < tileQueue.length; i++) {
+ for(var i=0, len=tileQueue.length; i<len; i++) {
var tile = tileQueue[i];
tile.draw();
//mark tile as unqueued for the next time (since tiles are reused)
@@ -14549,7 +32403,7 @@
var row = (prepend) ? grid.pop() : grid.shift();
- for (var i=0; i < modelRow.length; i++) {
+ for (var i=0, len=modelRow.length; i<len; i++) {
var modelTile = modelRow[i];
var bounds = modelTile.bounds.clone();
var position = modelTile.position.clone();
@@ -14579,7 +32433,7 @@
var resolution = this.map.getResolution();
var deltaLon = resolution * deltaX;
- for (var i=0; i<this.grid.length; i++) {
+ for (var i=0, len=this.grid.length; i<len; i++) {
var row = this.grid[i];
var modelTileIndex = (prepend) ? 0 : (row.length - 1);
var modelTile = row[modelTileIndex];
@@ -14655,7 +32509,7 @@
* {<OpenLayers.Bounds>} Bounds of the tile at the given pixel location.
*/
getTileBounds: function(viewPortPx) {
- var maxExtent = this.map.getMaxExtent();
+ var maxExtent = this.maxExtent;
var resolution = this.getResolution();
var tileMapWidth = resolution * this.tileSize.w;
var tileMapHeight = resolution * this.tileSize.h;
@@ -14676,6 +32530,2379 @@
CLASS_NAME: "OpenLayers.Layer.Grid"
});
/* ======================================================================
+ OpenLayers/Layer/MultiMap.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/Layer/EventPane.js
+ * @requires OpenLayers/Layer/FixedZoomLevels.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.MultiMap
+ * Note that MultiMap does not fully support the sphericalMercator
+ * option. See Ticket #953 for more details.
+ *
+ * Inherits from:
+ * - <OpenLayers.Layer.EventPane>
+ * - <OpenLayers.Layer.FixedZoomLevels>
+ */
+OpenLayers.Layer.MultiMap = OpenLayers.Class(
+ OpenLayers.Layer.EventPane, OpenLayers.Layer.FixedZoomLevels, {
+
+ /**
+ * Constant: MIN_ZOOM_LEVEL
+ * {Integer} 1
+ */
+ MIN_ZOOM_LEVEL: 1,
+
+ /**
+ * Constant: MAX_ZOOM_LEVEL
+ * {Integer} 17
+ */
+ MAX_ZOOM_LEVEL: 17,
+
+ /**
+ * Constant: RESOLUTIONS
+ * {Array(Float)} Hardcode these resolutions so that they are more closely
+ * tied with the standard wms projection
+ */
+ RESOLUTIONS: [
+ 9,
+ 1.40625,
+ 0.703125,
+ 0.3515625,
+ 0.17578125,
+ 0.087890625,
+ 0.0439453125,
+ 0.02197265625,
+ 0.010986328125,
+ 0.0054931640625,
+ 0.00274658203125,
+ 0.001373291015625,
+ 0.0006866455078125,
+ 0.00034332275390625,
+ 0.000171661376953125,
+ 0.0000858306884765625,
+ 0.00004291534423828125
+ ],
+
+ /**
+ * APIProperty: type
+ * {?}
+ */
+ type: null,
+
+ /**
+ * Constructor: OpenLayers.Layer.MultiMap
+ *
+ * Parameters:
+ * name - {String}
+ * options - {Object}
+ */
+ initialize: function(name, options) {
+ OpenLayers.Layer.EventPane.prototype.initialize.apply(this, arguments);
+ OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this,
+ arguments);
+ if (this.sphericalMercator) {
+ OpenLayers.Util.extend(this, OpenLayers.Layer.SphericalMercator);
+ this.initMercatorParameters();
+ this.RESOLUTIONS.unshift(10);
+ }
+ },
+
+ /**
+ * Method: loadMapObject
+ */
+ loadMapObject:function() {
+ try { //crash proofing
+ this.mapObject = new MultimapViewer(this.div);
+ } catch (e) { }
+ },
+
+ /**
+ * APIMethod: getWarningHTML
+ *
+ * Returns:
+ * {String} String with information on why layer is broken, how to get
+ * it working.
+ */
+ getWarningHTML:function() {
+ return OpenLayers.i18n(
+ "getLayerWarning", {'layerType':"MM", 'layerLib':"MultiMap"}
+ );
+ },
+
+
+
+ /************************************
+ * *
+ * MapObject Interface Controls *
+ * *
+ ************************************/
+
+
+ // Get&Set Center, Zoom
+
+ /**
+ * APIMethod: setMapObjectCenter
+ * Set the mapObject to the specified center and zoom
+ *
+ * Parameters:
+ * center - {Object} MapObject LonLat format
+ * zoom - {int} MapObject zoom format
+ */
+ setMapObjectCenter: function(center, zoom) {
+ this.mapObject.goToPosition(center, zoom);
+ },
+
+ /**
+ * APIMethod: getMapObjectCenter
+ *
+ * Returns:
+ * {Object} The mapObject's current center in Map Object format
+ */
+ getMapObjectCenter: function() {
+ return this.mapObject.getCurrentPosition();
+ },
+
+ /**
+ * APIMethod: getMapObjectZoom
+ *
+ * Returns:
+ * {Integer} The mapObject's current zoom, in Map Object format
+ */
+ getMapObjectZoom: function() {
+ return this.mapObject.getZoomFactor();
+ },
+
+
+ // LonLat - Pixel Translation
+
+ /**
+ * APIMethod: getMapObjectLonLatFromMapObjectPixel
+ *
+ * Parameters:
+ * moPixel - {Object} MapObject Pixel format
+ *
+ * Returns:
+ * {Object} MapObject LonLat translated from MapObject Pixel
+ */
+ getMapObjectLonLatFromMapObjectPixel: function(moPixel) {
+ moPixel.x = moPixel.x - (this.map.getSize().w/2);
+ moPixel.y = moPixel.y - (this.map.getSize().h/2);
+ return this.mapObject.getMapPositionAt(moPixel);
+ },
+
+ /**
+ * APIMethod: getMapObjectPixelFromMapObjectLonLat
+ *
+ * Parameters:
+ * moLonLat - {Object} MapObject LonLat format
+ *
+ * Returns:
+ * {Object} MapObject Pixel transtlated from MapObject LonLat
+ */
+ getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {
+ return this.mapObject.geoPosToContainerPixels(moLonLat);
+ },
+
+
+ /************************************
+ * *
+ * MapObject Primitives *
+ * *
+ ************************************/
+
+
+ // LonLat
+
+ /**
+ * APIMethod: getLongitudeFromMapObjectLonLat
+ *
+ * Parameters:
+ * moLonLat - {Object} MapObject LonLat format
+ *
+ * Returns:
+ * {Float} Longitude of the given MapObject LonLat
+ */
+ getLongitudeFromMapObjectLonLat: function(moLonLat) {
+ return this.sphericalMercator ?
+ this.forwardMercator(moLonLat.lon, moLonLat.lat).lon :
+ moLonLat.lon;
+ },
+
+ /**
+ * APIMethod: getLatitudeFromMapObjectLonLat
+ *
+ * Parameters:
+ * moLonLat - {Object} MapObject LonLat format
+ *
+ * Returns:
+ * {Float} Latitude of the given MapObject LonLat
+ */
+ getLatitudeFromMapObjectLonLat: function(moLonLat) {
+ return this.sphericalMercator ?
+ this.forwardMercator(moLonLat.lon, moLonLat.lat).lat :
+ moLonLat.lat;
+ },
+
+ /**
+ * APIMethod: getMapObjectLonLatFromLonLat
+ *
+ * Parameters:
+ * lon - {Float}
+ * lat - {Float}
+ *
+ * Returns:
+ * {Object} MapObject LonLat built from lon and lat params
+ */
+ getMapObjectLonLatFromLonLat: function(lon, lat) {
+ var mmLatLon;
+ if(this.sphericalMercator) {
+ var lonlat = this.inverseMercator(lon, lat);
+ mmLatLon = new MMLatLon(lonlat.lat, lonlat.lon);
+ } else {
+ mmLatLon = new MMLatLon(lat, lon);
+ }
+ return mmLatLon;
+ },
+
+ // Pixel
+
+ /**
+ * APIMethod: getXFromMapObjectPixel
+ *
+ * Parameters:
+ * moPixel - {Object} MapObject Pixel format
+ *
+ * Returns:
+ * {Integer} X value of the MapObject Pixel
+ */
+ getXFromMapObjectPixel: function(moPixel) {
+ return moPixel.x;
+ },
+
+ /**
+ * APIMethod: getYFromMapObjectPixel
+ *
+ * Parameters:
+ * moPixel - {Object} MapObject Pixel format
+ *
+ * Returns:
+ * {Integer} Y value of the MapObject Pixel
+ */
+ getYFromMapObjectPixel: function(moPixel) {
+ return moPixel.y;
+ },
+
+ /**
+ * APIMethod: getMapObjectPixelFromXY
+ *
+ * Parameters:
+ * x - {Integer}
+ * y - {Integer}
+ *
+ * Returns:
+ * {Object} MapObject Pixel from x and y parameters
+ */
+ getMapObjectPixelFromXY: function(x, y) {
+ return new MMPoint(x, y);
+ },
+
+ CLASS_NAME: "OpenLayers.Layer.MultiMap"
+});
+/* ======================================================================
+ OpenLayers/Layer/Text.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/Layer/Markers.js
+ * @requires OpenLayers/Request/XMLHttpRequest.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.Text
+ * This layer creates markers given data in a text file. The <location>
+ * property of the layer (specified as a property of the options argument
+ * in the <OpenLayers.Layer.Text> constructor) points to a tab delimited
+ * file with data used to create markers.
+ *
+ * The first row of the data file should be a header line with the column names
+ * of the data. Each column should be delimited by a tab space. The
+ * possible columns are:
+ * - *point* lat,lon of the point where a marker is to be placed
+ * - *lat* Latitude of the point where a marker is to be placed
+ * - *lon* Longitude of the point where a marker is to be placed
+ * - *icon* or *image* URL of marker icon to use.
+ * - *iconSize* Size of Icon to use.
+ * - *iconOffset* Where the top-left corner of the icon is to be placed
+ * relative to the latitude and longitude of the point.
+ * - *title* The text of the 'title' is placed inside an 'h2' marker
+ * inside a popup, which opens when the marker is clicked.
+ * - *description* The text of the 'description' is placed below the h2
+ * in the popup. this can be plain text or HTML.
+ *
+ * Example text file:
+ * (code)
+ * lat lon title description iconSize iconOffset icon
+ * 10 20 title description 21,25 -10,-25 http://www.openlayers.org/dev/img/marker.png
+ * (end)
+ *
+ * Inherits from:
+ * - <OpenLayers.Layer.Markers>
+ */
+OpenLayers.Layer.Text = OpenLayers.Class(OpenLayers.Layer.Markers, {
+
+ /**
+ * APIProperty: location
+ * {String} URL of text file. Must be specified in the "options" argument
+ * of the constructor. Can not be changed once passed in.
+ */
+ location:null,
+
+ /**
+ * Property: features
+ * {Array(<OpenLayers.Feature>)}
+ */
+ features: null,
+
+ /**
+ * APIProperty: formatOptions
+ * {Object} Hash of options which should be passed to the format when it is
+ * created. Must be passed in the constructor.
+ */
+ formatOptions: null,
+
+ /**
+ * Property: selectedFeature
+ * {<OpenLayers.Feature>}
+ */
+ selectedFeature: null,
+
+ /**
+ * Constructor: OpenLayers.Layer.Text
+ * Create a text layer.
+ *
+ * Parameters:
+ * name - {String}
+ * options - {Object} Object with properties to be set on the layer.
+ * Must include <location> property.
+ */
+ initialize: function(name, options) {
+ OpenLayers.Layer.Markers.prototype.initialize.apply(this, arguments);
+ this.features = new Array();
+ },
+
+ /**
+ * APIMethod: destroy
+ */
+ destroy: function() {
+ // Warning: Layer.Markers.destroy() must be called prior to calling
+ // clearFeatures() here, otherwise we leak memory. Indeed, if
+ // Layer.Markers.destroy() is called after clearFeatures(), it won't be
+ // able to remove the marker image elements from the layer's div since
+ // the markers will have been destroyed by clearFeatures().
+ OpenLayers.Layer.Markers.prototype.destroy.apply(this, arguments);
+ this.clearFeatures();
+ this.features = null;
+ },
+
+ /**
+ * Method: loadText
+ * Start the load of the Text data. Don't do this when we first add the layer,
+ * since we may not be visible at any point, and it would therefore be a waste.
+ */
+ loadText: function() {
+ if (!this.loaded) {
+ if (this.location != null) {
+
+ var onFail = function(e) {
+ this.events.triggerEvent("loadend");
+ };
+
+ this.events.triggerEvent("loadstart");
+ OpenLayers.Request.GET({
+ url: this.location,
+ success: this.parseData,
+ failure: onFail,
+ scope: this
+ });
+ this.loaded = true;
+ }
+ }
+ },
+
+ /**
+ * Method: moveTo
+ * If layer is visible and Text has not been loaded, load Text.
+ *
+ * Parameters:
+ * bounds - {Object}
+ * zoomChanged - {Object}
+ * minor - {Object}
+ */
+ moveTo:function(bounds, zoomChanged, minor) {
+ OpenLayers.Layer.Markers.prototype.moveTo.apply(this, arguments);
+ if(this.visibility && !this.loaded){
+ this.loadText();
+ }
+ },
+
+ /**
+ * Method: parseData
+ *
+ * Parameters:
+ * ajaxRequest - {<OpenLayers.Request.XMLHttpRequest>}
+ */
+ parseData: function(ajaxRequest) {
+ var text = ajaxRequest.responseText;
+
+ var options = {};
+
+ OpenLayers.Util.extend(options, this.formatOptions);
+
+ if (this.map && !this.projection.equals(this.map.getProjectionObject())) {
+ options.externalProjection = this.projection;
+ options.internalProjection = this.map.getProjectionObject();
+ }
+
+ var parser = new OpenLayers.Format.Text(options);
+ features = parser.read(text);
+ for (var i=0, len=features.length; i<len; i++) {
+ var data = {};
+ var feature = features[i];
+ var location;
+ var iconSize, iconOffset;
+
+ location = new OpenLayers.LonLat(feature.geometry.x,
+ feature.geometry.y);
+
+ if (feature.style.graphicWidth
+ && feature.style.graphicHeight) {
+ iconSize = new OpenLayers.Size(
+ feature.style.graphicWidth,
+ feature.style.graphicHeight);
+ }
+
+ // FIXME: At the moment, we only use this if we have an
+ // externalGraphic, because icon has no setOffset API Method.
+ /**
+ * FIXME FIRST!!
+ * The Text format does all sorts of parseFloating
+ * The result of a parseFloat for a bogus string is NaN. That
+ * means the three possible values here are undefined, NaN, or a
+ * number. The previous check was an identity check for null. This
+ * means it was failing for all undefined or NaN. A slightly better
+ * check is for undefined. An even better check is to see if the
+ * value is a number (see #1441).
+ */
+ if (feature.style.graphicXOffset !== undefined
+ && feature.style.graphicYOffset !== undefined) {
+ iconOffset = new OpenLayers.Pixel(
+ feature.style.graphicXOffset,
+ feature.style.graphicYOffset);
+ }
+
+ if (feature.style.externalGraphic != null) {
+ data.icon = new OpenLayers.Icon(feature.style.externalGraphic,
+ iconSize,
+ iconOffset);
+ } else {
+ data.icon = OpenLayers.Marker.defaultIcon();
+
+ //allows for the case where the image url is not
+ // specified but the size is. use a default icon
+ // but change the size
+ if (iconSize != null) {
+ data.icon.setSize(iconSize);
+ }
+ }
+
+ if ((feature.attributes.title != null)
+ && (feature.attributes.description != null)) {
+ data['popupContentHTML'] =
+ '<h2>'+feature.attributes.title+'</h2>' +
+ '<p>'+feature.attributes.description+'</p>';
+ }
+
+ data['overflow'] = feature.attributes.overflow || "auto";
+
+ var markerFeature = new OpenLayers.Feature(this, location, data);
+ this.features.push(markerFeature);
+ var marker = markerFeature.createMarker();
+ if ((feature.attributes.title != null)
+ && (feature.attributes.description != null)) {
+ marker.events.register('click', markerFeature, this.markerClick);
+ }
+ this.addMarker(marker);
+ }
+ this.events.triggerEvent("loadend");
+ },
+
+ /**
+ * Property: markerClick
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ markerClick: function(evt) {
+ var sameMarkerClicked = (this == this.layer.selectedFeature);
+ this.layer.selectedFeature = (!sameMarkerClicked) ? this : null;
+ for(var i=0, len=this.layer.map.popups.length; i<len; i++) {
+ this.layer.map.removePopup(this.layer.map.popups[i]);
+ }
+ if (!sameMarkerClicked) {
+ this.layer.map.addPopup(this.createPopup());
+ }
+ OpenLayers.Event.stop(evt);
+ },
+
+ /**
+ * Method: clearFeatures
+ */
+ clearFeatures: function() {
+ if (this.features != null) {
+ while(this.features.length > 0) {
+ var feature = this.features[0];
+ OpenLayers.Util.removeItem(this.features, feature);
+ feature.destroy();
+ }
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Layer.Text"
+});
+/* ======================================================================
+ OpenLayers/Layer/VirtualEarth.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/Layer/EventPane.js
+ * @requires OpenLayers/Layer/FixedZoomLevels.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.VirtualEarth
+ *
+ * Inherits from:
+ * - <OpenLayers.Layer.EventPane>
+ * - <OpenLayers.Layer.FixedZoomLevels>
+ */
+OpenLayers.Layer.VirtualEarth = OpenLayers.Class(
+ OpenLayers.Layer.EventPane,
+ OpenLayers.Layer.FixedZoomLevels, {
+
+ /**
+ * Constant: MIN_ZOOM_LEVEL
+ * {Integer} 1
+ */
+ MIN_ZOOM_LEVEL: 1,
+
+ /**
+ * Constant: MAX_ZOOM_LEVEL
+ * {Integer} 17
+ */
+ MAX_ZOOM_LEVEL: 17,
+
+ /**
+ * Constant: RESOLUTIONS
+ * {Array(Float)} Hardcode these resolutions so that they are more closely
+ * tied with the standard wms projection
+ */
+ RESOLUTIONS: [
+ 1.40625,
+ 0.703125,
+ 0.3515625,
+ 0.17578125,
+ 0.087890625,
+ 0.0439453125,
+ 0.02197265625,
+ 0.010986328125,
+ 0.0054931640625,
+ 0.00274658203125,
+ 0.001373291015625,
+ 0.0006866455078125,
+ 0.00034332275390625,
+ 0.000171661376953125,
+ 0.0000858306884765625,
+ 0.00004291534423828125
+ ],
+
+ /**
+ * APIProperty: type
+ * {VEMapType}
+ */
+ type: null,
+
+ /**
+ * APIProperty: sphericalMercator
+ * {Boolean} Should the map act as a mercator-projected map? This will
+ * cause all interactions with the map to be in the actual map
+ * projection, which allows support for vector drawing, overlaying
+ * other maps, etc.
+ */
+ sphericalMercator: false,
+
+ /**
+ * Constructor: OpenLayers.Layer.VirtualEarth
+ *
+ * Parameters:
+ * name - {String}
+ * options - {Object}
+ */
+ initialize: function(name, options) {
+ OpenLayers.Layer.EventPane.prototype.initialize.apply(this, arguments);
+ OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this,
+ arguments);
+ if(this.sphericalMercator) {
+ OpenLayers.Util.extend(this, OpenLayers.Layer.SphericalMercator);
+ this.initMercatorParameters();
+ }
+ },
+
+ /**
+ * Method: loadMapObject
+ */
+ loadMapObject:function() {
+
+ // create div and set to same size as map
+ var veDiv = OpenLayers.Util.createDiv(this.name);
+ var sz = this.map.getSize();
+ veDiv.style.width = sz.w + "px";
+ veDiv.style.height = sz.h + "px";
+ this.div.appendChild(veDiv);
+
+ try { // crash prevention
+ this.mapObject = new VEMap(this.name);
+ } catch (e) { }
+
+ if (this.mapObject != null) {
+ try { // this is to catch a Mozilla bug without falling apart
+
+ // The fourth argument is whether the map is 'fixed' -- not
+ // draggable. See:
+ // http://blogs.msdn.com/virtualearth/archive/2007/09/28/locking-a-virtual-earth-map.aspx
+ //
+ this.mapObject.LoadMap(null, null, this.type, true);
+ this.mapObject.AttachEvent("onmousedown", function() {return true; });
+
+ } catch (e) { }
+ this.mapObject.HideDashboard();
+ }
+
+ //can we do smooth panning? this is an unpublished method, so we need
+ // to be careful
+ if ( !this.mapObject ||
+ !this.mapObject.vemapcontrol ||
+ !this.mapObject.vemapcontrol.PanMap ||
+ (typeof this.mapObject.vemapcontrol.PanMap != "function")) {
+
+ this.dragPanMapObject = null;
+ }
+
+ },
+
+ /**
+ * APIMethod: getWarningHTML
+ *
+ * Returns:
+ * {String} String with information on why layer is broken, how to get
+ * it working.
+ */
+ getWarningHTML:function() {
+ return OpenLayers.i18n(
+ "getLayerWarning", {'layerType':'VE', 'layerLib':'VirtualEarth'}
+ );
+ },
+
+
+
+ /************************************
+ * *
+ * MapObject Interface Controls *
+ * *
+ ************************************/
+
+
+ // Get&Set Center, Zoom
+
+ /**
+ * APIMethod: setMapObjectCenter
+ * Set the mapObject to the specified center and zoom
+ *
+ * Parameters:
+ * center - {Object} MapObject LonLat format
+ * zoom - {int} MapObject zoom format
+ */
+ setMapObjectCenter: function(center, zoom) {
+ this.mapObject.SetCenterAndZoom(center, zoom);
+ },
+
+ /**
+ * APIMethod: getMapObjectCenter
+ *
+ * Returns:
+ * {Object} The mapObject's current center in Map Object format
+ */
+ getMapObjectCenter: function() {
+ return this.mapObject.GetCenter();
+ },
+
+ /**
+ * APIMethod: dragPanMapObject
+ *
+ * Parameters:
+ * dX - {Integer}
+ * dY - {Integer}
+ */
+ dragPanMapObject: function(dX, dY) {
+ this.mapObject.vemapcontrol.PanMap(dX, -dY);
+ },
+
+ /**
+ * APIMethod: getMapObjectZoom
+ *
+ * Returns:
+ * {Integer} The mapObject's current zoom, in Map Object format
+ */
+ getMapObjectZoom: function() {
+ return this.mapObject.GetZoomLevel();
+ },
+
+
+ // LonLat - Pixel Translation
+
+ /**
+ * APIMethod: getMapObjectLonLatFromMapObjectPixel
+ *
+ * Parameters:
+ * moPixel - {Object} MapObject Pixel format
+ *
+ * Returns:
+ * {Object} MapObject LonLat translated from MapObject Pixel
+ */
+ getMapObjectLonLatFromMapObjectPixel: function(moPixel) {
+ //the conditional here is to test if we are running the v6 of VE
+ return (typeof VEPixel != 'undefined')
+ ? this.mapObject.PixelToLatLong(moPixel)
+ : this.mapObject.PixelToLatLong(moPixel.x, moPixel.y);
+ },
+
+ /**
+ * APIMethod: getMapObjectPixelFromMapObjectLonLat
+ *
+ * Parameters:
+ * moLonLat - {Object} MapObject LonLat format
+ *
+ * Returns:
+ * {Object} MapObject Pixel transtlated from MapObject LonLat
+ */
+ getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {
+ return this.mapObject.LatLongToPixel(moLonLat);
+ },
+
+
+ /************************************
+ * *
+ * MapObject Primitives *
+ * *
+ ************************************/
+
+
+ // LonLat
+
+ /**
+ * APIMethod: getLongitudeFromMapObjectLonLat
+ *
+ * Parameters:
+ * moLonLat - {Object} MapObject LonLat format
+ *
+ * Returns:
+ * {Float} Longitude of the given MapObject LonLat
+ */
+ getLongitudeFromMapObjectLonLat: function(moLonLat) {
+ return this.sphericalMercator ?
+ this.forwardMercator(moLonLat.Longitude, moLonLat.Latitude).lon :
+ moLonLat.Longitude;
+ },
+
+ /**
+ * APIMethod: getLatitudeFromMapObjectLonLat
+ *
+ * Parameters:
+ * moLonLat - {Object} MapObject LonLat format
+ *
+ * Returns:
+ * {Float} Latitude of the given MapObject LonLat
+ */
+ getLatitudeFromMapObjectLonLat: function(moLonLat) {
+ return this.sphericalMercator ?
+ this.forwardMercator(moLonLat.Longitude, moLonLat.Latitude).lat :
+ moLonLat.Latitude;
+ },
+
+ /**
+ * APIMethod: getMapObjectLonLatFromLonLat
+ *
+ * Parameters:
+ * lon - {Float}
+ * lat - {Float}
+ *
+ * Returns:
+ * {Object} MapObject LonLat built from lon and lat params
+ */
+ getMapObjectLonLatFromLonLat: function(lon, lat) {
+ var veLatLong;
+ if(this.sphericalMercator) {
+ var lonlat = this.inverseMercator(lon, lat);
+ veLatLong = new VELatLong(lonlat.lat, lonlat.lon);
+ } else {
+ veLatLong = new VELatLong(lat, lon);
+ }
+ return veLatLong;
+ },
+
+ // Pixel
+
+ /**
+ * APIMethod: getXFromMapObjectPixel
+ *
+ * Parameters:
+ * moPixel - {Object} MapObject Pixel format
+ *
+ * Returns:
+ * {Integer} X value of the MapObject Pixel
+ */
+ getXFromMapObjectPixel: function(moPixel) {
+ return moPixel.x;
+ },
+
+ /**
+ * APIMethod: getYFromMapObjectPixel
+ *
+ * Parameters:
+ * moPixel - {Object} MapObject Pixel format
+ *
+ * Returns:
+ * {Integer} Y value of the MapObject Pixel
+ */
+ getYFromMapObjectPixel: function(moPixel) {
+ return moPixel.y;
+ },
+
+ /**
+ * APIMethod: getMapObjectPixelFromXY
+ *
+ * Parameters:
+ * x - {Integer}
+ * y - {Integer}
+ *
+ * Returns:
+ * {Object} MapObject Pixel from x and y parameters
+ */
+ getMapObjectPixelFromXY: function(x, y) {
+ //the conditional here is to test if we are running the v6 of VE
+ return (typeof VEPixel != 'undefined') ? new VEPixel(x, y)
+ : new Msn.VE.Pixel(x, y);
+ },
+
+ CLASS_NAME: "OpenLayers.Layer.VirtualEarth"
+});
+/* ======================================================================
+ OpenLayers/Layer/Yahoo.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/Layer/EventPane.js
+ * @requires OpenLayers/Layer/FixedZoomLevels.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.Yahoo
+ *
+ * Inherits from:
+ * - <OpenLayers.Layer.EventPane>
+ * - <OpenLayers.Layer.FixedZoomLevels>
+ */
+OpenLayers.Layer.Yahoo = OpenLayers.Class(
+ OpenLayers.Layer.EventPane, OpenLayers.Layer.FixedZoomLevels, {
+
+ /**
+ * Constant: MIN_ZOOM_LEVEL
+ * {Integer} 0
+ */
+ MIN_ZOOM_LEVEL: 0,
+
+ /**
+ * Constant: MAX_ZOOM_LEVEL
+ * {Integer} 15
+ */
+ MAX_ZOOM_LEVEL: 15,
+
+ /**
+ * Constant: RESOLUTIONS
+ * {Array(Float)} Hardcode these resolutions so that they are more closely
+ * tied with the standard wms projection
+ */
+ RESOLUTIONS: [
+ 1.40625,
+ 0.703125,
+ 0.3515625,
+ 0.17578125,
+ 0.087890625,
+ 0.0439453125,
+ 0.02197265625,
+ 0.010986328125,
+ 0.0054931640625,
+ 0.00274658203125,
+ 0.001373291015625,
+ 0.0006866455078125,
+ 0.00034332275390625,
+ 0.000171661376953125,
+ 0.0000858306884765625,
+ 0.00004291534423828125
+ ],
+
+ /**
+ * APIProperty: type
+ * {YahooMapType}
+ */
+ type: null,
+
+ /**
+ * APIProperty: sphericalMercator
+ * {Boolean} Should the map act as a mercator-projected map? This will
+ * cause all interactions with the map to be in the actual map projection,
+ * which allows support for vector drawing, overlaying other maps, etc.
+ */
+ sphericalMercator: false,
+
+ /**
+ * Constructor: OpenLayers.Layer.Yahoo
+ *
+ * Parameters:
+ * name - {String}
+ * options - {Object}
+ */
+ initialize: function(name, options) {
+ OpenLayers.Layer.EventPane.prototype.initialize.apply(this, arguments);
+ OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this,
+ arguments);
+ if(this.sphericalMercator) {
+ OpenLayers.Util.extend(this, OpenLayers.Layer.SphericalMercator);
+ this.initMercatorParameters();
+ }
+ },
+
+ /**
+ * Method: loadMapObject
+ */
+ loadMapObject:function() {
+ try { //do not crash!
+ var size = this.getMapObjectSizeFromOLSize(this.map.getSize());
+ this.mapObject = new YMap(this.div, this.type, size);
+ this.mapObject.disableKeyControls();
+ this.mapObject.disableDragMap();
+
+ //can we do smooth panning? (moveByXY is not an API function)
+ if ( !this.mapObject.moveByXY ||
+ (typeof this.mapObject.moveByXY != "function" ) ) {
+
+ this.dragPanMapObject = null;
+ }
+ } catch(e) {}
+ },
+
+ /**
+ * Method: onMapResize
+ *
+ */
+ onMapResize: function() {
+ try {
+ var size = this.getMapObjectSizeFromOLSize(this.map.getSize());
+ this.mapObject.resizeTo(size);
+ } catch(e) {}
+ },
+
+
+ /**
+ * APIMethod: setMap
+ * Overridden from EventPane because we need to remove this yahoo event
+ * pane which prohibits our drag and drop, and we can only do this
+ * once the map has been loaded and centered.
+ *
+ * Parameters:
+ * map - {<OpenLayers.Map>}
+ */
+ setMap: function(map) {
+ OpenLayers.Layer.EventPane.prototype.setMap.apply(this, arguments);
+
+ this.map.events.register("moveend", this, this.fixYahooEventPane);
+ },
+
+ /**
+ * Method: fixYahooEventPane
+ * The map has been centered, so the mysterious yahoo eventpane has been
+ * added. we remove it so that it doesnt mess with *our* event pane.
+ */
+ fixYahooEventPane: function() {
+ var yahooEventPane = OpenLayers.Util.getElement("ygddfdiv");
+ if (yahooEventPane != null) {
+ if (yahooEventPane.parentNode != null) {
+ yahooEventPane.parentNode.removeChild(yahooEventPane);
+ }
+ this.map.events.unregister("moveend", this,
+ this.fixYahooEventPane);
+ }
+ },
+
+ /**
+ * APIMethod: getWarningHTML
+ *
+ * Returns:
+ * {String} String with information on why layer is broken, how to get
+ * it working.
+ */
+ getWarningHTML:function() {
+ return OpenLayers.i18n(
+ "getLayerWarning", {'layerType':'Yahoo', 'layerLib':'Yahoo'}
+ );
+ },
+
+ /********************************************************/
+ /* */
+ /* Translation Functions */
+ /* */
+ /* The following functions translate GMaps and OL */
+ /* formats for Pixel, LonLat, Bounds, and Zoom */
+ /* */
+ /********************************************************/
+
+
+ //
+ // TRANSLATION: MapObject Zoom <-> OpenLayers Zoom
+ //
+
+ /**
+ * APIMethod: getOLZoomFromMapObjectZoom
+ *
+ * Parameters:
+ * gZoom - {Integer}
+ *
+ * Returns:
+ * {Integer} An OpenLayers Zoom level, translated from the passed in gZoom
+ * Returns null if null value is passed in.
+ */
+ getOLZoomFromMapObjectZoom: function(moZoom) {
+ var zoom = null;
+ if (moZoom != null) {
+ zoom = OpenLayers.Layer.FixedZoomLevels.prototype.getOLZoomFromMapObjectZoom.apply(this, [moZoom]);
+ zoom = 18 - zoom;
+ }
+ return zoom;
+ },
+
+ /**
+ * APIMethod: getMapObjectZoomFromOLZoom
+ *
+ * Parameters:
+ * olZoom - {Integer}
+ *
+ * Returns:
+ * {Integer} A MapObject level, translated from the passed in olZoom
+ * Returns null if null value is passed in
+ */
+ getMapObjectZoomFromOLZoom: function(olZoom) {
+ var zoom = null;
+ if (olZoom != null) {
+ zoom = OpenLayers.Layer.FixedZoomLevels.prototype.getMapObjectZoomFromOLZoom.apply(this, [olZoom]);
+ zoom = 18 - zoom;
+ }
+ return zoom;
+ },
+
+ /************************************
+ * *
+ * MapObject Interface Controls *
+ * *
+ ************************************/
+
+
+ // Get&Set Center, Zoom
+
+ /**
+ * APIMethod: setMapObjectCenter
+ * Set the mapObject to the specified center and zoom
+ *
+ * Parameters:
+ * center - {Object} MapObject LonLat format
+ * zoom - {int} MapObject zoom format
+ */
+ setMapObjectCenter: function(center, zoom) {
+ this.mapObject.drawZoomAndCenter(center, zoom);
+ },
+
+ /**
+ * APIMethod: getMapObjectCenter
+ *
+ * Returns:
+ * {Object} The mapObject's current center in Map Object format
+ */
+ getMapObjectCenter: function() {
+ return this.mapObject.getCenterLatLon();
+ },
+
+ /**
+ * APIMethod: dragPanMapObject
+ *
+ * Parameters:
+ * dX - {Integer}
+ * dY - {Integer}
+ */
+ dragPanMapObject: function(dX, dY) {
+ this.mapObject.moveByXY({
+ 'x': -dX,
+ 'y': dY
+ });
+ },
+
+ /**
+ * APIMethod: getMapObjectZoom
+ *
+ * Returns:
+ * {Integer} The mapObject's current zoom, in Map Object format
+ */
+ getMapObjectZoom: function() {
+ return this.mapObject.getZoomLevel();
+ },
+
+
+ // LonLat - Pixel Translation
+
+ /**
+ * APIMethod: getMapObjectLonLatFromMapObjectPixel
+ *
+ * Parameters:
+ * moPixel - {Object} MapObject Pixel format
+ *
+ * Returns:
+ * {Object} MapObject LonLat translated from MapObject Pixel
+ */
+ getMapObjectLonLatFromMapObjectPixel: function(moPixel) {
+ return this.mapObject.convertXYLatLon(moPixel);
+ },
+
+ /**
+ * APIMethod: getMapObjectPixelFromMapObjectLonLat
+ *
+ * Parameters:
+ * moLonLat - {Object} MapObject LonLat format
+ *
+ * Returns:
+ * {Object} MapObject Pixel transtlated from MapObject LonLat
+ */
+ getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {
+ return this.mapObject.convertLatLonXY(moLonLat);
+ },
+
+
+ /************************************
+ * *
+ * MapObject Primitives *
+ * *
+ ************************************/
+
+
+ // LonLat
+
+ /**
+ * APIMethod: getLongitudeFromMapObjectLonLat
+ *
+ * Parameters:
+ * moLonLat - {Object} MapObject LonLat format
+ *
+ * Returns:
+ * {Float} Longitude of the given MapObject LonLat
+ */
+ getLongitudeFromMapObjectLonLat: function(moLonLat) {
+ return this.sphericalMercator ?
+ this.forwardMercator(moLonLat.Lon, moLonLat.Lat).lon :
+ moLonLat.Lon;
+ },
+
+ /**
+ * APIMethod: getLatitudeFromMapObjectLonLat
+ *
+ * Parameters:
+ * moLonLat - {Object} MapObject LonLat format
+ *
+ * Returns:
+ * {Float} Latitude of the given MapObject LonLat
+ */
+ getLatitudeFromMapObjectLonLat: function(moLonLat) {
+ return this.sphericalMercator ?
+ this.forwardMercator(moLonLat.Lon, moLonLat.Lat).lat :
+ moLonLat.Lat;
+ },
+
+ /**
+ * APIMethod: getMapObjectLonLatFromLonLat
+ *
+ * Parameters:
+ * lon - {Float}
+ * lat - {Float}
+ *
+ * Returns:
+ * {Object} MapObject LonLat built from lon and lat params
+ */
+ getMapObjectLonLatFromLonLat: function(lon, lat) {
+ var yLatLong;
+ if(this.sphericalMercator) {
+ var lonlat = this.inverseMercator(lon, lat);
+ yLatLong = new YGeoPoint(lonlat.lat, lonlat.lon);
+ } else {
+ yLatLong = new YGeoPoint(lat, lon);
+ }
+ return yLatLong;
+ },
+
+ // Pixel
+
+ /**
+ * APIMethod: getXFromMapObjectPixel
+ *
+ * Parameters:
+ * moPixel - {Object} MapObject Pixel format
+ *
+ * Returns:
+ * {Integer} X value of the MapObject Pixel
+ */
+ getXFromMapObjectPixel: function(moPixel) {
+ return moPixel.x;
+ },
+
+ /**
+ * APIMethod: getYFromMapObjectPixel
+ *
+ * Parameters:
+ * moPixel - {Object} MapObject Pixel format
+ *
+ * Returns:
+ * {Integer} Y value of the MapObject Pixel
+ */
+ getYFromMapObjectPixel: function(moPixel) {
+ return moPixel.y;
+ },
+
+ /**
+ * APIMethod: getMapObjectPixelFromXY
+ *
+ * Parameters:
+ * x - {Integer}
+ * y - {Integer}
+ *
+ * Returns:
+ * {Object} MapObject Pixel from x and y parameters
+ */
+ getMapObjectPixelFromXY: function(x, y) {
+ return new YCoordPoint(x, y);
+ },
+
+ // Size
+
+ /**
+ * APIMethod: getMapObjectSizeFromOLSize
+ *
+ * Parameters:
+ * olSize - {<OpenLayers.Size>}
+ *
+ * Returns:
+ * {Object} MapObject Size from olSize parameter
+ */
+ getMapObjectSizeFromOLSize: function(olSize) {
+ return new YSize(olSize.w, olSize.h);
+ },
+
+ CLASS_NAME: "OpenLayers.Layer.Yahoo"
+});
+/* ======================================================================
+ OpenLayers/Style.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/Util.js
+ * @requires OpenLayers/Feature/Vector.js
+ */
+
+/**
+ * Class: OpenLayers.Style
+ * This class represents a UserStyle obtained
+ * from a SLD, containing styling rules.
+ */
+OpenLayers.Style = OpenLayers.Class({
+
+ /**
+ * APIProperty: name
+ * {String}
+ */
+ name: null,
+
+ /**
+ * Property: title
+ * {String} Title of this style (set if included in SLD)
+ */
+ title: null,
+
+ /**
+ * Property: description
+ * {String} Description of this style (set if abstract is included in SLD)
+ */
+ description: null,
+
+ /**
+ * APIProperty: layerName
+ * {<String>} name of the layer that this style belongs to, usually
+ * according to the NamedLayer attribute of an SLD document.
+ */
+ layerName: null,
+
+ /**
+ * APIProperty: isDefault
+ * {Boolean}
+ */
+ isDefault: false,
+
+ /**
+ * Property: rules
+ * {Array(<OpenLayers.Rule>)}
+ */
+ rules: null,
+
+ /**
+ * Property: context
+ * {Object} An optional object with properties that symbolizers' property
+ * values should be evaluated against. If no context is specified,
+ * feature.attributes will be used
+ */
+ context: null,
+
+ /**
+ * Property: defaultStyle
+ * {Object} hash of style properties to use as default for merging
+ * rule-based style symbolizers onto. If no rules are defined,
+ * createSymbolizer will return this style.
+ */
+ defaultStyle: null,
+
+ /**
+ * Property: propertyStyles
+ * {Hash of Boolean} cache of style properties that need to be parsed for
+ * propertyNames. Property names are keys, values won't be used.
+ */
+ propertyStyles: null,
+
+
+ /**
+ * Constructor: OpenLayers.Style
+ * Creates a UserStyle.
+ *
+ * Parameters:
+ * style - {Object} Optional hash of style properties that will be
+ * used as default style for this style object. This style
+ * applies if no rules are specified. Symbolizers defined in
+ * rules will extend this default style.
+ * options - {Object} An optional object with properties to set on the
+ * userStyle
+ *
+ * Return:
+ * {<OpenLayers.Style>}
+ */
+ initialize: function(style, options) {
+ this.rules = [];
+
+ // use the default style from OpenLayers.Feature.Vector if no style
+ // was given in the constructor
+ this.setDefaultStyle(style ||
+ OpenLayers.Feature.Vector.style["default"]);
+
+ OpenLayers.Util.extend(this, options);
+ },
+
+ /**
+ * APIMethod: destroy
+ * nullify references to prevent circular references and memory leaks
+ */
+ destroy: function() {
+ for (var i=0, len=this.rules.length; i<len; i++) {
+ this.rules[i].destroy();
+ this.rules[i] = null;
+ }
+ this.rules = null;
+ this.defaultStyle = null;
+ },
+
+ /**
+ * Method: createSymbolizer
+ * creates a style by applying all feature-dependent rules to the base
+ * style.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature>} feature to evaluate rules for
+ *
+ * Returns:
+ * {Object} symbolizer hash
+ */
+ createSymbolizer: function(feature) {
+ var style = this.createLiterals(
+ OpenLayers.Util.extend({}, this.defaultStyle), feature);
+
+ var rules = this.rules;
+
+ var rule, context;
+ var elseRules = [];
+ var appliedRules = false;
+ for(var i=0, len=rules.length; i<len; i++) {
+ rule = rules[i];
+ // does the rule apply?
+ var applies = rule.evaluate(feature);
+
+ if(applies) {
+ if(rule instanceof OpenLayers.Rule && rule.elseFilter) {
+ elseRules.push(rule);
+ } else {
+ appliedRules = true;
+ this.applySymbolizer(rule, style, feature);
+ }
+ }
+ }
+
+ // if no other rules apply, apply the rules with else filters
+ if(appliedRules == false && elseRules.length > 0) {
+ appliedRules = true;
+ for(var i=0, len=elseRules.length; i<len; i++) {
+ this.applySymbolizer(elseRules[i], style, feature);
+ }
+ }
+
+ // don't display if there were rules but none applied
+ if(rules.length > 0 && appliedRules == false) {
+ style.display = "none";
+ } else {
+ style.display = "";
+ }
+
+ return style;
+ },
+
+ /**
+ * Method: applySymbolizer
+ *
+ * Parameters:
+ * rule - {OpenLayers.Rule}
+ * style - {Object}
+ * feature - {<OpenLayer.Feature.Vector>}
+ *
+ * Returns:
+ * {Object} A style with new symbolizer applied.
+ */
+ applySymbolizer: function(rule, style, feature) {
+ var symbolizerPrefix = feature.geometry ?
+ this.getSymbolizerPrefix(feature.geometry) :
+ OpenLayers.Style.SYMBOLIZER_PREFIXES[0];
+
+ var symbolizer = rule.symbolizer[symbolizerPrefix] || rule.symbolizer;
+
+ // merge the style with the current style
+ return this.createLiterals(
+ OpenLayers.Util.extend(style, symbolizer), feature);
+ },
+
+ /**
+ * Method: createLiterals
+ * creates literals for all style properties that have an entry in
+ * <this.propertyStyles>.
+ *
+ * Parameters:
+ * style - {Object} style to create literals for. Will be modified
+ * inline.
+ * feature - {Object}
+ *
+ * Returns:
+ * {Object} the modified style
+ */
+ createLiterals: function(style, feature) {
+ var context = this.context || feature.attributes || feature.data;
+
+ for (var i in this.propertyStyles) {
+ style[i] = OpenLayers.Style.createLiteral(style[i], context, feature);
+ }
+ return style;
+ },
+
+ /**
+ * Method: findPropertyStyles
+ * Looks into all rules for this style and the defaultStyle to collect
+ * all the style hash property names containing ${...} strings that have
+ * to be replaced using the createLiteral method before returning them.
+ *
+ * Returns:
+ * {Object} hash of property names that need createLiteral parsing. The
+ * name of the property is the key, and the value is true;
+ */
+ findPropertyStyles: function() {
+ var propertyStyles = {};
+
+ // check the default style
+ var style = this.defaultStyle;
+ this.addPropertyStyles(propertyStyles, style);
+
+ // walk through all rules to check for properties in their symbolizer
+ var rules = this.rules;
+ var symbolizer, value;
+ for (var i=0, len=rules.length; i<len; i++) {
+ var symbolizer = rules[i].symbolizer;
+ for (var key in symbolizer) {
+ value = symbolizer[key];
+ if (typeof value == "object") {
+ // symbolizer key is "Point", "Line" or "Polygon"
+ this.addPropertyStyles(propertyStyles, value);
+ } else {
+ // symbolizer is a hash of style properties
+ this.addPropertyStyles(propertyStyles, symbolizer);
+ break;
+ }
+ }
+ }
+ return propertyStyles;
+ },
+
+ /**
+ * Method: addPropertyStyles
+ *
+ * Parameters:
+ * propertyStyles - {Object} hash to add new property styles to. Will be
+ * modified inline
+ * symbolizer - {Object} search this symbolizer for property styles
+ *
+ * Returns:
+ * {Object} propertyStyles hash
+ */
+ addPropertyStyles: function(propertyStyles, symbolizer) {
+ var property;
+ for (var key in symbolizer) {
+ property = symbolizer[key];
+ if (typeof property == "string" &&
+ property.match(/\$\{\w+\}/)) {
+ propertyStyles[key] = true;
+ }
+ }
+ return propertyStyles;
+ },
+
+ /**
+ * APIMethod: addRules
+ * Adds rules to this style.
+ *
+ * Parameters:
+ * rules - {Array(<OpenLayers.Rule>)}
+ */
+ addRules: function(rules) {
+ this.rules = this.rules.concat(rules);
+ this.propertyStyles = this.findPropertyStyles();
+ },
+
+ /**
+ * APIMethod: setDefaultStyle
+ * Sets the default style for this style object.
+ *
+ * Parameters:
+ * style - {Object} Hash of style properties
+ */
+ setDefaultStyle: function(style) {
+ this.defaultStyle = style;
+ this.propertyStyles = this.findPropertyStyles();
+ },
+
+ /**
+ * Method: getSymbolizerPrefix
+ * Returns the correct symbolizer prefix according to the
+ * geometry type of the passed geometry
+ *
+ * Parameters:
+ * geometry {<OpenLayers.Geometry>}
+ *
+ * Returns:
+ * {String} key of the according symbolizer
+ */
+ getSymbolizerPrefix: function(geometry) {
+ var prefixes = OpenLayers.Style.SYMBOLIZER_PREFIXES;
+ for (var i=0, len=prefixes.length; i<len; i++) {
+ if (geometry.CLASS_NAME.indexOf(prefixes[i]) != -1) {
+ return prefixes[i];
+ }
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Style"
+});
+
+
+/**
+ * Function: createLiteral
+ * converts a style value holding a combination of PropertyName and Literal
+ * into a Literal, taking the property values from the passed features.
+ *
+ * Parameters:
+ * value - {String} value to parse. If this string contains a construct like
+ * "foo ${bar}", then "foo " will be taken as literal, and "${bar}"
+ * will be replaced by the value of the "bar" attribute of the passed
+ * feature.
+ * context - {Object} context to take attribute values from
+ * feature - {OpenLayers.Feature.Vector} The feature that will be passed
+ * to <OpenLayers.String.format> for evaluating functions in the context.
+ *
+ * Returns:
+ * {String} the parsed value. In the example of the value parameter above, the
+ * result would be "foo valueOfBar", assuming that the passed feature has an
+ * attribute named "bar" with the value "valueOfBar".
+ */
+OpenLayers.Style.createLiteral = function(value, context, feature) {
+ if (typeof value == "string" && value.indexOf("${") != -1) {
+ value = OpenLayers.String.format(value, context, [feature]);
+ value = (isNaN(value) || !value) ? value : parseFloat(value);
+ }
+ return value;
+};
+
+/**
+ * Constant: OpenLayers.Style.SYMBOLIZER_PREFIXES
+ * {Array} prefixes of the sld symbolizers. These are the
+ * same as the main geometry types
+ */
+OpenLayers.Style.SYMBOLIZER_PREFIXES = ['Point', 'Line', 'Polygon', 'Text'];
+/* ======================================================================
+ OpenLayers/Control/ModifyFeature.js
+ ====================================================================== */
+
+/* Copyright (c) 2006 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/DragFeature.js
+ * @requires OpenLayers/Control/SelectFeature.js
+ * @requires OpenLayers/Handler/Keyboard.js
+ */
+
+/**
+ * Class: OpenLayers.Control.ModifyFeature
+ * Control to modify features. When activated, a click renders the vertices
+ * of a feature - these vertices can then be dragged. By default, the
+ * delete key will delete the vertex under the mouse. New features are
+ * added by dragging "virtual vertices" between vertices. Create a new
+ * control with the <OpenLayers.Control.ModifyFeature> constructor.
+ *
+ * Inherits From:
+ * - <OpenLayers.Control>
+ */
+OpenLayers.Control.ModifyFeature = OpenLayers.Class(OpenLayers.Control, {
+
+ /**
+ * APIProperty: geometryTypes
+ * {Array(String)} To restrict modification to a limited set of geometry
+ * types, send a list of strings corresponding to the geometry class
+ * names.
+ */
+ geometryTypes: null,
+
+ /**
+ * APIProperty: clickout
+ * {Boolean} Unselect features when clicking outside any feature.
+ * Default is true.
+ */
+ clickout: true,
+
+ /**
+ * APIProperty: toggle
+ * {Boolean} Unselect a selected feature on click.
+ * Default is true.
+ */
+ toggle: true,
+
+ /**
+ * Property: layer
+ * {<OpenLayers.Layer.Vector>}
+ */
+ layer: null,
+
+ /**
+ * Property: feature
+ * {<OpenLayers.Feature.Vector>} Feature currently available for modification.
+ */
+ feature: null,
+
+ /**
+ * Property: vertices
+ * {Array(<OpenLayers.Feature.Vector>)} Verticies currently available
+ * for dragging.
+ */
+ vertices: null,
+
+ /**
+ * Property: virtualVertices
+ * {Array(<OpenLayers.Feature.Vector>)} Virtual vertices in the middle
+ * of each edge.
+ */
+ virtualVertices: null,
+
+ /**
+ * Property: selectControl
+ * {<OpenLayers.Control.SelectFeature>}
+ */
+ selectControl: null,
+
+ /**
+ * Property: dragControl
+ * {<OpenLayers.Control.DragFeature>}
+ */
+ dragControl: null,
+
+ /**
+ * Property: handlers
+ * {Object}
+ */
+ handlers: null,
+
+ /**
+ * APIProperty: deleteCodes
+ * {Array(Integer)} Keycodes for deleting verticies. Set to null to disable
+ * vertex deltion by keypress. If non-null, keypresses with codes
+ * in this array will delete vertices under the mouse. Default
+ * is 46 and 68, the 'delete' and lowercase 'd' keys.
+ */
+ deleteCodes: null,
+
+ /**
+ * APIProperty: virtualStyle
+ * {Object} A symbolizer to be used for virtual vertices.
+ */
+ virtualStyle: null,
+
+ /**
+ * APIProperty: mode
+ * {Integer} Bitfields specifying the modification mode. Defaults to
+ * OpenLayers.Control.ModifyFeature.RESHAPE. To set the mode to a
+ * combination of options, use the | operator. or example, to allow
+ * the control to both resize and rotate features, use the following
+ * syntax
+ * (code)
+ * control.mode = OpenLayers.Control.ModifyFeature.RESIZE |
+ * OpenLayers.Control.ModifyFeature.ROTATE;
+ * (end)
+ */
+ mode: null,
+
+ /**
+ * Property: radiusHandle
+ * {<OpenLayers.Feature.Vector>} A handle for rotating/resizing a feature.
+ */
+ radiusHandle: null,
+
+ /**
+ * Property: dragHandle
+ * {<OpenLayers.Feature.Vector>} A handle for dragging a feature.
+ */
+ dragHandle: null,
+
+ /**
+ * APIProperty: onModificationStart
+ * {Function} *Deprecated*. Register for "beforefeaturemodified" instead.
+ * The "beforefeaturemodified" event is triggered on the layer before
+ * any modification begins.
+ *
+ * Optional function to be called when a feature is selected
+ * to be modified. The function should expect to be called with a
+ * feature. This could be used for example to allow to lock the
+ * feature on server-side.
+ */
+ onModificationStart: function() {},
+
+ /**
+ * APIProperty: onModification
+ * {Function} *Deprecated*. Register for "featuremodified" instead.
+ * The "featuremodified" event is triggered on the layer with each
+ * feature modification.
+ *
+ * Optional function to be called when a feature has been
+ * modified. The function should expect to be called with a feature.
+ */
+ onModification: function() {},
+
+ /**
+ * APIProperty: onModificationEnd
+ * {Function} *Deprecated*. Register for "afterfeaturemodified" instead.
+ * The "afterfeaturemodified" event is triggered on the layer after
+ * a feature has been modified.
+ *
+ * Optional function to be called when a feature is finished
+ * being modified. The function should expect to be called with a
+ * feature.
+ */
+ onModificationEnd: function() {},
+
+ /**
+ * Constructor: OpenLayers.Control.ModifyFeature
+ * Create a new modify feature control.
+ *
+ * Parameters:
+ * layer - {<OpenLayers.Layer.Vector>} Layer that contains features that
+ * will be modified.
+ * options - {Object} Optional object whose properties will be set on the
+ * control.
+ */
+ initialize: function(layer, options) {
+ this.layer = layer;
+ this.vertices = [];
+ this.virtualVertices = [];
+ this.virtualStyle = OpenLayers.Util.extend({},
+ this.layer.style || this.layer.styleMap.createSymbolizer());
+ this.virtualStyle.fillOpacity = 0.3;
+ this.virtualStyle.strokeOpacity = 0.3;
+ this.deleteCodes = [46, 68];
+ this.mode = OpenLayers.Control.ModifyFeature.RESHAPE;
+ OpenLayers.Control.prototype.initialize.apply(this, [options]);
+ if(!(this.deleteCodes instanceof Array)) {
+ this.deleteCodes = [this.deleteCodes];
+ }
+ var control = this;
+
+ // configure the select control
+ var selectOptions = {
+ geometryTypes: this.geometryTypes,
+ clickout: this.clickout,
+ toggle: this.toggle
+ };
+ this.selectControl = new OpenLayers.Control.SelectFeature(
+ layer, selectOptions
+ );
+ this.layer.events.on({
+ "beforefeatureselected": this.beforeSelectFeature,
+ "featureselected": this.selectFeature,
+ "featureunselected": this.unselectFeature,
+ scope: this
+ });
+
+ // configure the drag control
+ var dragOptions = {
+ geometryTypes: ["OpenLayers.Geometry.Point"],
+ snappingOptions: this.snappingOptions,
+ onStart: function(feature, pixel) {
+ control.dragStart.apply(control, [feature, pixel]);
+ },
+ onDrag: function(feature) {
+ control.dragVertex.apply(control, [feature]);
+ },
+ onComplete: function(feature) {
+ control.dragComplete.apply(control, [feature]);
+ }
+ };
+ this.dragControl = new OpenLayers.Control.DragFeature(
+ layer, dragOptions
+ );
+
+ // configure the keyboard handler
+ var keyboardOptions = {
+ keydown: this.handleKeypress
+ };
+ this.handlers = {
+ keyboard: new OpenLayers.Handler.Keyboard(this, keyboardOptions)
+ };
+ },
+
+ /**
+ * APIMethod: destroy
+ * Take care of things that are not handled in superclass.
+ */
+ destroy: function() {
+ this.layer.events.un({
+ "beforefeatureselected": this.beforeSelectFeature,
+ "featureselected": this.selectFeature,
+ "featureunselected": this.unselectFeature,
+ scope: this
+ });
+ this.layer = null;
+ this.selectControl.destroy();
+ this.dragControl.destroy();
+ OpenLayers.Control.prototype.destroy.apply(this, []);
+ },
+
+ /**
+ * APIMethod: activate
+ * Activate the control.
+ *
+ * Returns:
+ * {Boolean} Successfully activated the control.
+ */
+ activate: function() {
+ return (this.selectControl.activate() &&
+ this.handlers.keyboard.activate() &&
+ OpenLayers.Control.prototype.activate.apply(this, arguments));
+ },
+
+ /**
+ * APIMethod: deactivate
+ * Deactivate the control.
+ *
+ * Returns:
+ * {Boolean} Successfully deactivated the control.
+ */
+ deactivate: function() {
+ var deactivated = false;
+ // the return from the controls is unimportant in this case
+ if(OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {
+ this.layer.removeFeatures(this.vertices, {silent: true});
+ this.layer.removeFeatures(this.virtualVertices, {silent: true});
+ this.vertices = [];
+ this.dragControl.deactivate();
+ if(this.feature && this.feature.geometry) {
+ this.selectControl.unselect.apply(this.selectControl,
+ [this.feature]);
+ }
+ this.selectControl.deactivate();
+ this.handlers.keyboard.deactivate();
+ deactivated = true;
+ }
+ return deactivated;
+ },
+
+ /**
+ * Method: beforeSelectFeature
+ * Called before a feature is selected.
+ *
+ * Parameters:
+ * object - {Object} Object with a feature property referencing the
+ * selected feature.
+ */
+ beforeSelectFeature: function(object) {
+ return this.layer.events.triggerEvent(
+ "beforefeaturemodified", {feature: object.feature}
+ );
+ },
+
+ /**
+ * Method: selectFeature
+ * Called when the select feature control selects a feature.
+ *
+ * Parameters:
+ * object - {Object} Object with a feature property referencing the
+ * selected feature.
+ */
+ selectFeature: function(object) {
+ this.feature = object.feature;
+ this.resetVertices();
+ this.dragControl.activate();
+ this.onModificationStart(this.feature);
+ },
+
+ /**
+ * Method: unselectFeature
+ * Called when the select feature control unselects a feature.
+ *
+ * Parameters:
+ * object - {Object} Object with a feature property referencing the
+ * unselected feature.
+ */
+ unselectFeature: function(object) {
+ this.layer.removeFeatures(this.vertices, {silent: true});
+ this.vertices = [];
+ this.layer.destroyFeatures(this.virtualVertices, {silent: true});
+ this.virtualVertices = [];
+ if(this.dragHandle) {
+ this.layer.destroyFeatures([this.dragHandle], {silent: true});
+ delete this.dragHandle;
+ }
+ if(this.radiusHandle) {
+ this.layer.destroyFeatures([this.radiusHandle], {silent: true});
+ delete this.radiusHandle;
+ }
+ this.feature = null;
+ this.dragControl.deactivate();
+ this.onModificationEnd(object.feature);
+ this.layer.events.triggerEvent("afterfeaturemodified",
+ {feature: object.feature});
+ },
+
+ /**
+ * Method: dragStart
+ * Called by the drag feature control with before a feature is dragged.
+ * This method is used to differentiate between points and vertices
+ * of higher order geometries. This respects the <geometryTypes>
+ * property and forces a select of points when the drag control is
+ * already active (and stops events from propagating to the select
+ * control).
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>} The point or vertex about to be
+ * dragged.
+ * pixel - {<OpenLayers.Pixel>} Pixel location of the mouse event.
+ */
+ dragStart: function(feature, pixel) {
+ // only change behavior if the feature is not in the vertices array
+ if(feature != this.feature && !feature.geometry.parent &&
+ feature != this.dragHandle && feature != this.radiusHandle) {
+ if(this.feature) {
+ // unselect the currently selected feature
+ this.selectControl.clickFeature.apply(this.selectControl,
+ [this.feature]);
+ }
+ // check any constraints on the geometry type
+ if(this.geometryTypes == null ||
+ OpenLayers.Util.indexOf(this.geometryTypes,
+ feature.geometry.CLASS_NAME) != -1) {
+ // select the point
+ this.selectControl.clickFeature.apply(this.selectControl,
+ [feature]);
+ /**
+ * TBD: These lines improve workflow by letting the user
+ * immediately start dragging after the mouse down.
+ * However, it is very ugly to be messing with controls
+ * and their handlers in this way. I'd like a better
+ * solution if the workflow change is necessary.
+ */
+ // prepare the point for dragging
+ this.dragControl.overFeature.apply(this.dragControl,
+ [feature]);
+ this.dragControl.lastPixel = pixel;
+ this.dragControl.handlers.drag.started = true;
+ this.dragControl.handlers.drag.start = pixel;
+ this.dragControl.handlers.drag.last = pixel;
+ }
+ }
+ },
+
+ /**
+ * Method: dragVertex
+ * Called by the drag feature control with each drag move of a vertex.
+ *
+ * Parameters:
+ * vertex - {<OpenLayers.Feature.Vector>} The vertex being dragged.
+ */
+ dragVertex: function(vertex) {
+ /**
+ * Five cases:
+ * 1) dragging a simple point
+ * 2) dragging a virtual vertex
+ * 3) dragging a drag handle
+ * 4) dragging a radius handle
+ * 5) dragging a real vertex
+ */
+ if(this.feature.geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
+ // dragging a simple point
+ if(this.feature != vertex) {
+ this.feature = vertex;
+ }
+ } else {
+ if(vertex._index) {
+ // dragging a virtual vertex
+ vertex.geometry.parent.addComponent(vertex.geometry,
+ vertex._index);
+ // move from virtual to real vertex
+ delete vertex._index;
+ OpenLayers.Util.removeItem(this.virtualVertices, vertex);
+ this.vertices.push(vertex);
+ } else if(vertex == this.dragHandle) {
+ // dragging a drag handle
+ this.layer.removeFeatures(this.vertices, {silent: true});
+ this.vertices = [];
+ if(this.radiusHandle) {
+ this.layer.destroyFeatures([this.radiusHandle], {silent: true});
+ this.radiusHandle = null;
+ }
+ }
+ // dragging a radius handle - no special treatment
+ // dragging a real vertex - no special treatment
+ if(this.virtualVertices.length > 0) {
+ this.layer.destroyFeatures(this.virtualVertices, {silent: true});
+ this.virtualVertices = [];
+ }
+ this.layer.drawFeature(this.feature, this.selectControl.renderIntent);
+ }
+ // keep the vertex on top so it gets the mouseout after dragging
+ // this should be removed in favor of an option to draw under or
+ // maintain node z-index
+ this.layer.drawFeature(vertex);
+ },
+
+ /**
+ * Method: dragComplete
+ * Called by the drag feature control when the feature dragging is complete.
+ *
+ * Parameters:
+ * vertex - {<OpenLayers.Feature.Vector>} The vertex being dragged.
+ */
+ dragComplete: function(vertex) {
+ this.resetVertices();
+ this.onModification(this.feature);
+ this.layer.events.triggerEvent("featuremodified",
+ {feature: this.feature});
+ },
+
+ /**
+ * Method: resetVertices
+ */
+ resetVertices: function() {
+ // if coming from a drag complete we're about to destroy the vertex
+ // that was just dragged. For that reason, the drag feature control
+ // will never detect a mouse-out on that vertex, meaning that the drag
+ // handler won't be deactivated. This can cause errors because the drag
+ // feature control still has a feature to drag but that feature is
+ // destroyed. To prevent this, we call outFeature on the drag feature
+ // control if the control actually has a feature to drag.
+ if(this.dragControl.feature) {
+ this.dragControl.outFeature(this.dragControl.feature);
+ }
+ if(this.vertices.length > 0) {
+ this.layer.removeFeatures(this.vertices, {silent: true});
+ this.vertices = [];
+ }
+ if(this.virtualVertices.length > 0) {
+ this.layer.removeFeatures(this.virtualVertices, {silent: true});
+ this.virtualVertices = [];
+ }
+ if(this.dragHandle) {
+ this.layer.destroyFeatures([this.dragHandle], {silent: true});
+ this.dragHandle = null;
+ }
+ if(this.radiusHandle) {
+ this.layer.destroyFeatures([this.radiusHandle], {silent: true});
+ this.radiusHandle = null;
+ }
+ if(this.feature &&
+ this.feature.geometry.CLASS_NAME != "OpenLayers.Geometry.Point") {
+ if((this.mode & OpenLayers.Control.ModifyFeature.DRAG)) {
+ this.collectDragHandle();
+ }
+ if((this.mode & (OpenLayers.Control.ModifyFeature.ROTATE |
+ OpenLayers.Control.ModifyFeature.RESIZE))) {
+ this.collectRadiusHandle();
+ }
+ if((this.mode & OpenLayers.Control.ModifyFeature.RESHAPE)) {
+ this.collectVertices();
+ }
+ }
+ },
+
+ /**
+ * Method: handleKeypress
+ * Called by the feature handler on keypress. This is used to delete
+ * vertices. If the <deleteCode> property is set, vertices will
+ * be deleted when a feature is selected for modification and
+ * the mouse is over a vertex.
+ *
+ * Parameters:
+ * {Integer} Key code corresponding to the keypress event.
+ */
+ handleKeypress: function(evt) {
+ var code = evt.keyCode;
+
+ // check for delete key
+ if(this.feature &&
+ OpenLayers.Util.indexOf(this.deleteCodes, code) != -1) {
+ var vertex = this.dragControl.feature;
+ if(vertex &&
+ OpenLayers.Util.indexOf(this.vertices, vertex) != -1 &&
+ !this.dragControl.handlers.drag.dragging &&
+ vertex.geometry.parent) {
+ // remove the vertex
+ vertex.geometry.parent.removeComponent(vertex.geometry);
+ this.layer.drawFeature(this.feature,
+ this.selectControl.renderIntent);
+ this.resetVertices();
+ this.onModification(this.feature);
+ this.layer.events.triggerEvent("featuremodified",
+ {feature: this.feature});
+ }
+ }
+ },
+
+ /**
+ * Method: collectVertices
+ * Collect the vertices from the modifiable feature's geometry and push
+ * them on to the control's vertices array.
+ */
+ collectVertices: function() {
+ this.vertices = [];
+ this.virtualVertices = [];
+ var control = this;
+ function collectComponentVertices(geometry) {
+ var i, vertex, component, len;
+ if(geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
+ vertex = new OpenLayers.Feature.Vector(geometry);
+ control.vertices.push(vertex);
+ } else {
+ var numVert = geometry.components.length;
+ if(geometry.CLASS_NAME == "OpenLayers.Geometry.LinearRing") {
+ numVert -= 1;
+ }
+ for(i=0; i<numVert; ++i) {
+ component = geometry.components[i];
+ if(component.CLASS_NAME == "OpenLayers.Geometry.Point") {
+ vertex = new OpenLayers.Feature.Vector(component);
+ control.vertices.push(vertex);
+ } else {
+ collectComponentVertices(component);
+ }
+ }
+
+ // add virtual vertices in the middle of each edge
+ if(geometry.CLASS_NAME != "OpenLayers.Geometry.MultiPoint") {
+ for(i=0, len=geometry.components.length; i<len-1; ++i) {
+ var prevVertex = geometry.components[i];
+ var nextVertex = geometry.components[i + 1];
+ if(prevVertex.CLASS_NAME == "OpenLayers.Geometry.Point" &&
+ nextVertex.CLASS_NAME == "OpenLayers.Geometry.Point") {
+ var x = (prevVertex.x + nextVertex.x) / 2;
+ var y = (prevVertex.y + nextVertex.y) / 2;
+ var point = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(x, y),
+ null, control.virtualStyle
+ );
+ // set the virtual parent and intended index
+ point.geometry.parent = geometry;
+ point._index = i + 1;
+ control.virtualVertices.push(point);
+ }
+ }
+ }
+ }
+ }
+ collectComponentVertices.call(this, this.feature.geometry);
+ this.layer.addFeatures(this.virtualVertices, {silent: true});
+ this.layer.addFeatures(this.vertices, {silent: true});
+ },
+
+ /**
+ * Method: collectDragHandle
+ * Collect the drag handle for the selected geometry.
+ */
+ collectDragHandle: function() {
+ var geometry = this.feature.geometry;
+ var center = geometry.getBounds().getCenterLonLat();
+ var originGeometry = new OpenLayers.Geometry.Point(
+ center.lon, center.lat
+ );
+ var origin = new OpenLayers.Feature.Vector(originGeometry);
+ originGeometry.move = function(x, y) {
+ OpenLayers.Geometry.Point.prototype.move.call(this, x, y);
+ geometry.move(x, y);
+ };
+ this.dragHandle = origin;
+ this.layer.addFeatures([this.dragHandle], {silent: true});
+ },
+
+ /**
+ * Method: collectRadiusHandle
+ * Collect the radius handle for the selected geometry.
+ */
+ collectRadiusHandle: function() {
+ var geometry = this.feature.geometry;
+ var bounds = geometry.getBounds();
+ var center = bounds.getCenterLonLat();
+ var originGeometry = new OpenLayers.Geometry.Point(
+ center.lon, center.lat
+ );
+ var radiusGeometry = new OpenLayers.Geometry.Point(
+ bounds.right, bounds.bottom
+ );
+ var radius = new OpenLayers.Feature.Vector(radiusGeometry);
+ var resize = (this.mode & OpenLayers.Control.ModifyFeature.RESIZE);
+ var rotate = (this.mode & OpenLayers.Control.ModifyFeature.ROTATE);
+ radiusGeometry.move = function(x, y) {
+ OpenLayers.Geometry.Point.prototype.move.call(this, x, y);
+ var dx1 = this.x - originGeometry.x;
+ var dy1 = this.y - originGeometry.y;
+ var dx0 = dx1 - x;
+ var dy0 = dy1 - y;
+ if(rotate) {
+ var a0 = Math.atan2(dy0, dx0);
+ var a1 = Math.atan2(dy1, dx1);
+ var angle = a1 - a0;
+ angle *= 180 / Math.PI;
+ geometry.rotate(angle, originGeometry);
+ }
+ if(resize) {
+ var l0 = Math.sqrt((dx0 * dx0) + (dy0 * dy0));
+ var l1 = Math.sqrt((dx1 * dx1) + (dy1 * dy1));
+ geometry.resize(l1 / l0, originGeometry);
+ }
+ };
+ this.radiusHandle = radius;
+ this.layer.addFeatures([this.radiusHandle], {silent: true});
+ },
+
+ /**
+ * Method: setMap
+ * Set the map property for the control and all handlers.
+ *
+ * Parameters:
+ * map - {<OpenLayers.Map>} The control's map.
+ */
+ setMap: function(map) {
+ this.selectControl.setMap(map);
+ this.dragControl.setMap(map);
+ OpenLayers.Control.prototype.setMap.apply(this, arguments);
+ },
+
+ CLASS_NAME: "OpenLayers.Control.ModifyFeature"
+});
+
+/**
+ * Constant: RESHAPE
+ * {Integer} Constant used to make the control work in reshape mode
+ */
+OpenLayers.Control.ModifyFeature.RESHAPE = 1;
+/**
+ * Constant: RESIZE
+ * {Integer} Constant used to make the control work in resize mode
+ */
+OpenLayers.Control.ModifyFeature.RESIZE = 2;
+/**
+ * Constant: ROTATE
+ * {Integer} Constant used to make the control work in rotate mode
+ */
+OpenLayers.Control.ModifyFeature.ROTATE = 4;
+/**
+ * Constant: DRAG
+ * {Integer} Constant used to make the control work in drag mode
+ */
+OpenLayers.Control.ModifyFeature.DRAG = 8;
+/* ======================================================================
OpenLayers/Control/Navigation.js
====================================================================== */
@@ -14711,6 +34938,12 @@
*/
dragPan: null,
+ /**
+ * APIProprety: dragPanOptions
+ * {Object} Options passed to the DragPan control.
+ */
+ dragPanOptions: null,
+
/**
* Property: zoomBox
* {<OpenLayers.Control.ZoomBox>}
@@ -14724,6 +34957,12 @@
zoomWheelEnabled: true,
/**
+ * APIProperty: handleRightClicks
+ * {Boolean} Whether or not to handle right clicks. Default is false.
+ */
+ handleRightClicks: true,
+
+ /**
* Constructor: OpenLayers.Control.Navigation
* Create a new navigation control
*
@@ -14785,13 +35024,25 @@
* Method: draw
*/
draw: function() {
- this.handlers.click = new OpenLayers.Handler.Click(this,
- { 'dblclick': this.defaultDblClick },
- {
- 'double': true,
- 'stopDouble': true
- });
- this.dragPan = new OpenLayers.Control.DragPan({map: this.map});
+ // disable right mouse context menu for support of right click events
+ if (this.handleRightClicks) {
+ this.map.div.oncontextmenu = function () { return false;};
+ }
+
+ var clickCallbacks = {
+ 'dblclick': this.defaultDblClick,
+ 'dblrightclick': this.defaultDblRightClick
+ };
+ var clickOptions = {
+ 'double': true,
+ 'stopDouble': true
+ };
+ this.handlers.click = new OpenLayers.Handler.Click(
+ this, clickCallbacks, clickOptions
+ );
+ this.dragPan = new OpenLayers.Control.DragPan(
+ OpenLayers.Util.extend({map: this.map}, this.dragPanOptions)
+ );
this.zoomBox = new OpenLayers.Control.ZoomBox(
{map: this.map, keyMask: OpenLayers.Handler.MOD_SHIFT});
this.dragPan.draw();
@@ -14814,6 +35065,17 @@
},
/**
+ * Method: defaultRightDblClick
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ defaultDblRightClick: function (evt) {
+ var newCenter = this.map.getLonLatFromViewPortPx( evt.xy );
+ this.map.setCenter(newCenter, this.map.zoom - 1);
+ },
+
+ /**
* Method: wheelChange
*
* Parameters:
@@ -14881,6 +35143,546 @@
CLASS_NAME: "OpenLayers.Control.Navigation"
});
/* ======================================================================
+ OpenLayers/Filter.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.Filter
+ * This class represents an OGC Filter.
+ */
+OpenLayers.Filter = OpenLayers.Class({
+
+ /**
+ * Constructor: OpenLayers.Filter
+ * This is an abstract class. Create an instance of a filter subclass.
+ *
+ * Parameters:
+ * options - {Object} Optional object whose properties will be set on the
+ * instance.
+ *
+ * Returns:
+ * {<OpenLayers.Filter>}
+ */
+ initialize: function(options) {
+ OpenLayers.Util.extend(this, options);
+ },
+
+ /**
+ * APIMethod: destroy
+ * Remove reference to anything added.
+ */
+ destroy: function() {
+ },
+
+ /**
+ * APIMethod: evaluate
+ * Evaluates this filter in a specific context. Should be implemented by
+ * subclasses.
+ *
+ * Parameters:
+ * context - {Object} Context to use in evaluating the filter.
+ *
+ * Returns:
+ * {Boolean} The filter applies.
+ */
+ evaluate: function(context) {
+ return true;
+ },
+
+ CLASS_NAME: "OpenLayers.Filter"
+});
+/* ======================================================================
+ OpenLayers/Geometry.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/Format/WKT.js
+ * @requires OpenLayers/Feature/Vector.js
+ */
+
+/**
+ * Class: OpenLayers.Geometry
+ * A Geometry is a description of a geographic object. Create an instance of
+ * this class with the <OpenLayers.Geometry> constructor. This is a base class,
+ * typical geometry types are described by subclasses of this class.
+ */
+OpenLayers.Geometry = OpenLayers.Class({
+
+ /**
+ * Property: id
+ * {String} A unique identifier for this geometry.
+ */
+ id: null,
+
+ /**
+ * Property: parent
+ * {<OpenLayers.Geometry>}This is set when a Geometry is added as component
+ * of another geometry
+ */
+ parent: null,
+
+ /**
+ * Property: bounds
+ * {<OpenLayers.Bounds>} The bounds of this geometry
+ */
+ bounds: null,
+
+ /**
+ * Constructor: OpenLayers.Geometry
+ * Creates a geometry object.
+ */
+ initialize: function() {
+ this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME+ "_");
+ },
+
+ /**
+ * Method: destroy
+ * Destroy this geometry.
+ */
+ destroy: function() {
+ this.id = null;
+ this.bounds = null;
+ },
+
+ /**
+ * APIMethod: clone
+ * Create a clone of this geometry. Does not set any non-standard
+ * properties of the cloned geometry.
+ *
+ * Returns:
+ * {<OpenLayers.Geometry>} An exact clone of this geometry.
+ */
+ clone: function() {
+ return new OpenLayers.Geometry();
+ },
+
+ /**
+ * Set the bounds for this Geometry.
+ *
+ * Parameters:
+ * object - {<OpenLayers.Bounds>}
+ */
+ setBounds: function(bounds) {
+ if (bounds) {
+ this.bounds = bounds.clone();
+ }
+ },
+
+ /**
+ * Method: clearBounds
+ * Nullify this components bounds and that of its parent as well.
+ */
+ clearBounds: function() {
+ this.bounds = null;
+ if (this.parent) {
+ this.parent.clearBounds();
+ }
+ },
+
+ /**
+ * Method: extendBounds
+ * Extend the existing bounds to include the new bounds.
+ * If geometry's bounds is not yet set, then set a new Bounds.
+ *
+ * Parameters:
+ * newBounds - {<OpenLayers.Bounds>}
+ */
+ extendBounds: function(newBounds){
+ var bounds = this.getBounds();
+ if (!bounds) {
+ this.setBounds(newBounds);
+ } else {
+ this.bounds.extend(newBounds);
+ }
+ },
+
+ /**
+ * APIMethod: getBounds
+ * Get the bounds for this Geometry. If bounds is not set, it
+ * is calculated again, this makes queries faster.
+ *
+ * Returns:
+ * {<OpenLayers.Bounds>}
+ */
+ getBounds: function() {
+ if (this.bounds == null) {
+ this.calculateBounds();
+ }
+ return this.bounds;
+ },
+
+ /**
+ * APIMethod: calculateBounds
+ * Recalculate the bounds for the geometry.
+ */
+ calculateBounds: function() {
+ //
+ // This should be overridden by subclasses.
+ //
+ },
+
+ /**
+ * Method: atPoint
+ * Note - This is only an approximation based on the bounds of the
+ * geometry.
+ *
+ * Parameters:
+ * lonlat - {<OpenLayers.LonLat>}
+ * toleranceLon - {float} Optional tolerance in Geometric Coords
+ * toleranceLat - {float} Optional tolerance in Geographic Coords
+ *
+ * Returns:
+ * {Boolean} Whether or not the geometry is at the specified location
+ */
+ atPoint: function(lonlat, toleranceLon, toleranceLat) {
+ var atPoint = false;
+ var bounds = this.getBounds();
+ if ((bounds != null) && (lonlat != null)) {
+
+ var dX = (toleranceLon != null) ? toleranceLon : 0;
+ var dY = (toleranceLat != null) ? toleranceLat : 0;
+
+ var toleranceBounds =
+ new OpenLayers.Bounds(this.bounds.left - dX,
+ this.bounds.bottom - dY,
+ this.bounds.right + dX,
+ this.bounds.top + dY);
+
+ atPoint = toleranceBounds.containsLonLat(lonlat);
+ }
+ return atPoint;
+ },
+
+ /**
+ * Method: getLength
+ * Calculate the length of this geometry. This method is defined in
+ * subclasses.
+ *
+ * Returns:
+ * {Float} The length of the collection by summing its parts
+ */
+ getLength: function() {
+ //to be overridden by geometries that actually have a length
+ //
+ return 0.0;
+ },
+
+ /**
+ * Method: getArea
+ * Calculate the area of this geometry. This method is defined in subclasses.
+ *
+ * Returns:
+ * {Float} The area of the collection by summing its parts
+ */
+ getArea: function() {
+ //to be overridden by geometries that actually have an area
+ //
+ return 0.0;
+ },
+
+ /**
+ * Method: toString
+ * Returns the Well-Known Text representation of a geometry
+ *
+ * Returns:
+ * {String} Well-Known Text
+ */
+ toString: function() {
+ return OpenLayers.Format.WKT.prototype.write(
+ new OpenLayers.Feature.Vector(this)
+ );
+ },
+
+ CLASS_NAME: "OpenLayers.Geometry"
+});
+
+
+/**
+ * Method: OpenLayers.Geometry.segmentsIntersect
+ * Determine whether two line segments intersect. Optionally calculates
+ * and returns the intersection point. This function is optimized for
+ * cases where seg1.x2 >= seg2.x1 || seg2.x2 >= seg1.x1. In those
+ * obvious cases where there is no intersection, the function should
+ * not be called.
+ *
+ * Parameters:
+ * seg1 - {Object} Object representing a segment with properties x1, y1, x2,
+ * and y2. The start point is represented by x1 and y1. The end point
+ * is represented by x2 and y2. Start and end are ordered so that x1 < x2.
+ * seg2 - {Object} Object representing a segment with properties x1, y1, x2,
+ * and y2. The start point is represented by x1 and y1. The end point
+ * is represented by x2 and y2. Start and end are ordered so that x1 < x2.
+ * point - {Boolean} Return the intersection point. If false, the actual
+ * intersection point will not be calculated. If true and the segments
+ * intersect, the intersection point will be returned. If true and
+ * the segments do not intersect, false will be returned. If true and
+ * the segments are coincident, true will be returned.
+ *
+ * Returns:
+ * {Boolean | <OpenLayers.Geometry.Point>} The two segments intersect.
+ * If the point argument is true, the return will be the intersection
+ * point or false if none exists. If point is true and the segments
+ * are coincident, return will be true (and the instersection is equal
+ * to the shorter segment).
+ */
+OpenLayers.Geometry.segmentsIntersect = function(seg1, seg2, point) {
+ var intersection = false;
+ var x11_21 = seg1.x1 - seg2.x1;
+ var y11_21 = seg1.y1 - seg2.y1;
+ var x12_11 = seg1.x2 - seg1.x1;
+ var y12_11 = seg1.y2 - seg1.y1;
+ var y22_21 = seg2.y2 - seg2.y1;
+ var x22_21 = seg2.x2 - seg2.x1;
+ var d = (y22_21 * x12_11) - (x22_21 * y12_11);
+ var n1 = (x22_21 * y11_21) - (y22_21 * x11_21);
+ var n2 = (x12_11 * y11_21) - (y12_11 * x11_21);
+ if(d == 0) {
+ // parallel
+ if(n1 == 0 && n2 == 0) {
+ // coincident
+ intersection = true;
+ }
+ } else {
+ var along1 = n1 / d;
+ var along2 = n2 / d;
+ if(along1 >= 0 && along1 <= 1 && along2 >=0 && along2 <= 1) {
+ // intersect
+ if(!point) {
+ intersection = true;
+ } else {
+ // calculate the intersection point
+ var x = seg1.x1 + (along1 * x12_11);
+ var y = seg1.y1 + (along1 * y12_11);
+ intersection = new OpenLayers.Geometry.Point(x, y);
+ }
+ }
+ }
+ return intersection;
+};
+/* ======================================================================
+ OpenLayers/Layer/KaMap.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/Layer/Grid.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.KaMap
+ *
+ * Inherits from:
+ * - <OpenLayers.Layer.Grid>
+ */
+OpenLayers.Layer.KaMap = OpenLayers.Class(OpenLayers.Layer.Grid, {
+
+ /**
+ * APIProperty: isBaseLayer
+ * {Boolean} KaMap Layer is always a base layer
+ */
+ isBaseLayer: true,
+
+ /**
+ * APIProperty: units
+ * {?}
+ */
+ units: null,
+
+ /**
+ * APIProperty: resolution
+ * {Float}
+ */
+ resolution: OpenLayers.DOTS_PER_INCH,
+
+ /**
+ * Constant: DEFAULT_PARAMS
+ * {Object} parameters set by default. The default parameters set
+ * the format via the 'i' parameter to 'jpeg'.
+ */
+ DEFAULT_PARAMS: {
+ i: 'jpeg',
+ map: ''
+ },
+
+ /**
+ * Constructor: OpenLayers.Layer.KaMap
+ *
+ * Parameters:
+ * name - {String}
+ * url - {String}
+ * params - {Object} Parameters to be sent to the HTTP server in the
+ * query string for the tile. The format can be set via the 'i'
+ * parameter (defaults to jpg) , and the map should be set via
+ * the 'map' parameter. It has been reported that ka-Map may behave
+ * inconsistently if your format parameter does not match the format
+ * parameter configured in your config.php. (See ticket #327 for more
+ * information.)
+ * options - {Object} Additional options for the layer. Any of the
+ * APIProperties listed on this layer, and any layer types it
+ * extends, can be overridden through the options parameter.
+ */
+ initialize: function(name, url, params, options) {
+ var newArguments = [];
+ newArguments.push(name, url, params, options);
+ OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);
+ this.params = OpenLayers.Util.applyDefaults(
+ this.params, this.DEFAULT_PARAMS
+ );
+ },
+
+ /**
+ * Method: getURL
+ *
+ * Parameters:
+ * bounds - {<OpenLayers.Bounds>}
+ *
+ * Returns:
+ * {String} A string with the layer's url and parameters and also the
+ * passed-in bounds and appropriate tile size specified as
+ * parameters
+ */
+ getURL: function (bounds) {
+ bounds = this.adjustBounds(bounds);
+ var mapRes = this.map.getResolution();
+ var scale = Math.round((this.map.getScale() * 10000)) / 10000;
+ var pX = Math.round(bounds.left / mapRes);
+ var pY = -Math.round(bounds.top / mapRes);
+ return this.getFullRequestString(
+ { t: pY,
+ l: pX,
+ s: scale
+ });
+ },
+
+ /**
+ * Method: addTile
+ *
+ * Parameters:
+ * bounds - {<OpenLayers.Bounds>}
+ * position - {<OpenLayers.Pixel>}
+ *
+ * Returns:
+ * {<OpenLayers.Tile.Image>}
+ */
+ addTile:function(bounds,position) {
+ var url = this.getURL(bounds);
+ return new OpenLayers.Tile.Image(this, position, bounds,
+ url, this.tileSize);
+ },
+
+ /**
+ * Method: calculateGridLayout
+ * ka-Map uses the center point of the map as an origin for
+ * its tiles. Override calculateGridLayout to center tiles
+ * correctly for this case.
+ *
+ * Parameters:
+ * bounds - {<OpenLayers.Bound>}
+ * extent - {<OpenLayers.Bounds>}
+ * resolution - {Number}
+ *
+ * Returns:
+ * Object containing properties tilelon, tilelat, tileoffsetlat,
+ * tileoffsetlat, tileoffsetx, tileoffsety
+ */
+ calculateGridLayout: function(bounds, extent, resolution) {
+ var tilelon = resolution*this.tileSize.w;
+ var tilelat = resolution*this.tileSize.h;
+
+ var offsetlon = bounds.left;
+ var tilecol = Math.floor(offsetlon/tilelon) - this.buffer;
+ var tilecolremain = offsetlon/tilelon - tilecol;
+ var tileoffsetx = -tilecolremain * this.tileSize.w;
+ var tileoffsetlon = tilecol * tilelon;
+
+ var offsetlat = bounds.top;
+ var tilerow = Math.ceil(offsetlat/tilelat) + this.buffer;
+ var tilerowremain = tilerow - offsetlat/tilelat;
+ var tileoffsety = -(tilerowremain+1) * this.tileSize.h;
+ var tileoffsetlat = tilerow * tilelat;
+
+ return {
+ tilelon: tilelon, tilelat: tilelat,
+ tileoffsetlon: tileoffsetlon, tileoffsetlat: tileoffsetlat,
+ tileoffsetx: tileoffsetx, tileoffsety: tileoffsety
+ };
+ },
+
+ /**
+ * APIMethod: clone
+ *
+ * Parameters:
+ * obj - {Object}
+ *
+ * Returns:
+ * {<OpenLayers.Layer.Kamap>} An exact clone of this OpenLayers.Layer.KaMap
+ */
+ clone: function (obj) {
+
+ if (obj == null) {
+ obj = new OpenLayers.Layer.KaMap(this.name,
+ this.url,
+ this.params,
+ this.options);
+ }
+
+ //get all additions from superclasses
+ obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);
+
+ // copy/set any non-init, non-simple values here
+ if (this.tileSize != null) {
+ obj.tileSize = this.tileSize.clone();
+ }
+
+ // we do not want to copy reference to grid, so we make a new array
+ obj.grid = [];
+
+ return obj;
+ },
+
+ /**
+ * APIMethod: getTileBounds
+ * Returns The tile bounds for a layer given a pixel location.
+ *
+ * Parameters:
+ * viewPortPx - {<OpenLayers.Pixel>} The location in the viewport.
+ *
+ * Returns:
+ * {<OpenLayers.Bounds>} Bounds of the tile at the given pixel location.
+ */
+ getTileBounds: function(viewPortPx) {
+ var resolution = this.getResolution();
+ var tileMapWidth = resolution * this.tileSize.w;
+ var tileMapHeight = resolution * this.tileSize.h;
+ var mapPoint = this.getLonLatFromViewPortPx(viewPortPx);
+ var tileLeft = tileMapWidth * Math.floor(mapPoint.lon / tileMapWidth);
+ var tileBottom = tileMapHeight * Math.floor(mapPoint.lat / tileMapHeight);
+ return new OpenLayers.Bounds(tileLeft, tileBottom,
+ tileLeft + tileMapWidth,
+ tileBottom + tileMapHeight);
+ },
+
+ CLASS_NAME: "OpenLayers.Layer.KaMap"
+});
+/* ======================================================================
OpenLayers/Layer/MapGuide.js
====================================================================== */
@@ -14889,7 +35691,7 @@
* full text of the license. */
/**
- * @requires OpenLayers/Ajax.js
+ * @requires OpenLayers/Request/XMLHttpRequest.js
* @requires OpenLayers/Layer/Grid.js
*/
@@ -14914,7 +35716,7 @@
* {Boolean} use tile server or request single tile image. Note that using
* singleTile *and* isBaseLayer false is *not recommend*: it uses synchronous
* XMLHttpRequests to load tiles, and this will *lock up users browsers*
- * during requests.
+ * during requests if the server fails to respond.
**/
singleTile: false,
@@ -14949,18 +35751,29 @@
* Constructor: OpenLayers.Layer.MapGuide
* Create a new Mapguide layer, either tiled or untiled.
*
- * For tiled layers, the 'groupName' and 'mapDefnition' options
- * must be specified as options.
+ * For tiled layers, the 'groupName' and 'mapDefinition' values
+ * must be specified as parameters in the constructor.
*
- * For untiled layers, specify either combination of 'mapName' and
- * 'session', or 'mapDefinition' and 'locale'.
+ * For untiled base layers, specify either combination of 'mapName' and
+ * 'session', or 'mapDefinition' and 'locale'.
*
+ * For untiled overlay layers (singleTile=true and isBaseLayer=false),
+ * mapName and session are required parameters for the Layer constructor.
+ * Also NOTE: untiled overlay layers issues a synchronous AJAX request
+ * before the image request can be issued so the users browser may lock
+ * up if the MG Web tier does not respond in a timely fashion.
+ *
+ * NOTE: MapGuide OS uses a DPI value and degrees to meters conversion
+ * factor that are different than the defaults used in OpenLayers,
+ * so these must be adjusted accordingly in your application.
+ * See the MapGuide example for how to set these values for MGOS.
+ *
* Parameters:
* name - {String} Name of the layer displayed in the interface
* url - {String} Location of the MapGuide mapagent executable
* (e.g. http://localhost:8008/mapguide/mapagent/mapagent.fcgi)
* params - {Object} hashtable of additional parameters to use. Some
- * parameters may require additional code on the serer. The ones that
+ * parameters may require additional code on the server. The ones that
* you may want to use are:
* - mapDefinition - {String} The MapGuide resource definition
* (e.g. Library://Samples/Gmap/Maps/gmapTiled.MapDefinition)
@@ -15002,6 +35815,7 @@
this.params,
this.SINGLE_TILE_PARAMS
);
+
} else {
//initialize for tiled layers
OpenLayers.Util.applyDefaults(
@@ -15082,18 +35896,15 @@
//but we first need to call GETVISIBLEMAPEXTENT to set the extent
var getVisParams = {};
+ getVisParams = OpenLayers.Util.extend(getVisParams, params);
getVisParams.operation = "GETVISIBLEMAPEXTENT";
getVisParams.version = "1.0.0";
getVisParams.session = this.params.session;
getVisParams.mapName = this.params.mapName;
getVisParams.format = 'text/xml';
- getVisParams = OpenLayers.Util.extend(getVisParams, params);
-
- new OpenLayers.Ajax.Request(this.url,
- { parameters: getVisParams,
- method: 'get',
- asynchronous: false //must be synchronous call to return control here
- });
+ url = this.getFullRequestString( getVisParams );
+
+ OpenLayers.Request.GET({url: url, async: false});
}
//construct the full URL
@@ -15113,7 +35924,7 @@
tilerow: rowidx,
scaleindex: this.resolutions.length - this.map.zoom - 1
});
- }
+ }
return url;
},
@@ -15268,12 +36079,9 @@
newArguments.push(name, url, params, options);
OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);
- if (arguments.length > 0) {
- OpenLayers.Util.applyDefaults(
- this.params,
- this.DEFAULT_PARAMS
- );
- }
+ this.params = OpenLayers.Util.applyDefaults(
+ this.params, this.DEFAULT_PARAMS
+ );
// unless explicitly set in options, if the layer is transparent,
// it will be an overlay
@@ -15426,6 +36234,334 @@
CLASS_NAME: "OpenLayers.Layer.MapServer"
});
/* ======================================================================
+ OpenLayers/Layer/TMS.js
+ ====================================================================== */
+
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * licence. See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+
+/**
+ * @requires OpenLayers/Layer/Grid.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.TMS
+ *
+ * Inherits from:
+ * - <OpenLayers.Layer.Grid>
+ */
+OpenLayers.Layer.TMS = OpenLayers.Class(OpenLayers.Layer.Grid, {
+
+ /**
+ * APIProperty: serviceVersion
+ * {String}
+ */
+ serviceVersion: "1.0.0",
+
+ /**
+ * APIProperty: isBaseLayer
+ * {Boolean}
+ */
+ isBaseLayer: true,
+
+ /**
+ * APIProperty: tileOrigin
+ * {<OpenLayers.Pixel>}
+ */
+ tileOrigin: null,
+
+ /**
+ * Constructor: OpenLayers.Layer.TMS
+ *
+ * Parameters:
+ * name - {String}
+ * url - {String}
+ * options - {Object} Hashtable of extra options to tag onto the layer
+ */
+ initialize: function(name, url, options) {
+ var newArguments = [];
+ newArguments.push(name, url, {}, options);
+ OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);
+ },
+
+ /**
+ * APIMethod:destroy
+ */
+ destroy: function() {
+ // for now, nothing special to do here.
+ OpenLayers.Layer.Grid.prototype.destroy.apply(this, arguments);
+ },
+
+
+ /**
+ * APIMethod: clone
+ *
+ * Parameters:
+ * obj - {Object}
+ *
+ * Returns:
+ * {<OpenLayers.Layer.TMS>} An exact clone of this <OpenLayers.Layer.TMS>
+ */
+ clone: function (obj) {
+
+ if (obj == null) {
+ obj = new OpenLayers.Layer.TMS(this.name,
+ this.url,
+ this.options);
+ }
+
+ //get all additions from superclasses
+ obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);
+
+ // copy/set any non-init, non-simple values here
+
+ return obj;
+ },
+
+ /**
+ * Method: getURL
+ *
+ * Parameters:
+ * bounds - {<OpenLayers.Bounds>}
+ *
+ * Returns:
+ * {String} A string with the layer's url and parameters and also the
+ * passed-in bounds and appropriate tile size specified as
+ * parameters
+ */
+ getURL: function (bounds) {
+ bounds = this.adjustBounds(bounds);
+ var res = this.map.getResolution();
+ var x = Math.round((bounds.left - this.tileOrigin.lon) / (res * this.tileSize.w));
+ var y = Math.round((bounds.bottom - this.tileOrigin.lat) / (res * this.tileSize.h));
+ var z = this.map.getZoom();
+ var path = this.serviceVersion + "/" + this.layername + "/" + z + "/" + x + "/" + y + "." + this.type;
+ var url = this.url;
+ if (url instanceof Array) {
+ url = this.selectUrl(path, url);
+ }
+ return url + path;
+ },
+
+ /**
+ * Method: addTile
+ * addTile creates a tile, initializes it, and adds it to the layer div.
+ *
+ * Parameters:
+ * bounds - {<OpenLayers.Bounds>}
+ * position - {<OpenLayers.Pixel>}
+ *
+ * Returns:
+ * {<OpenLayers.Tile.Image>} The added OpenLayers.Tile.Image
+ */
+ addTile:function(bounds,position) {
+ return new OpenLayers.Tile.Image(this, position, bounds,
+ null, this.tileSize);
+ },
+
+ /**
+ * APIMethod: setMap
+ * When the layer is added to a map, then we can fetch our origin
+ * (if we don't have one.)
+ *
+ * Parameters:
+ * map - {<OpenLayers.Map>}
+ */
+ setMap: function(map) {
+ OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);
+ if (!this.tileOrigin) {
+ this.tileOrigin = new OpenLayers.LonLat(this.map.maxExtent.left,
+ this.map.maxExtent.bottom);
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Layer.TMS"
+});
+/* ======================================================================
+ OpenLayers/Layer/TileCache.js
+ ====================================================================== */
+
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * licence. See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+
+/**
+ * @requires OpenLayers/Layer/Grid.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.TileCache
+ * A read only TileCache layer. Used to requests tiles cached by TileCache in
+ * a web accessible cache. This means that you have to pre-populate your
+ * cache before this layer can be used. It is meant only to read tiles
+ * created by TileCache, and not to make calls to TileCache for tile
+ * creation. Create a new instance with the
+ * <OpenLayers.Layer.TileCache> constructor.
+ *
+ * Inherits from:
+ * - <OpenLayers.Layer.Grid>
+ */
+OpenLayers.Layer.TileCache = OpenLayers.Class(OpenLayers.Layer.Grid, {
+
+ /**
+ * APIProperty: isBaseLayer
+ * {Boolean} Treat this layer as a base layer. Default is true.
+ */
+ isBaseLayer: true,
+
+ /**
+ * APIProperty: tileOrigin
+ * {<OpenLayers.LonLat>} Location of the tile lattice origin. Default is
+ * bottom left of the maxExtent.
+ */
+ tileOrigin: null,
+
+ /**
+ * APIProperty: format
+ * {String} Mime type of the images returned. Default is image/png.
+ */
+ format: 'image/png',
+
+ /**
+ * Constructor: OpenLayers.Layer.TileCache
+ * Create a new read only TileCache layer.
+ *
+ * Parameters:
+ * name - {String} Name of the layer displayed in the interface
+ * url - {String} Location of the web accessible cache (not the location of
+ * your tilecache script!)
+ * layername - {String} Layer name as defined in the TileCache
+ * configuration
+ * options - {Object} Optional object with properties to be set on the
+ * layer. Note that you should speficy your resolutions to match
+ * your TileCache configuration. This can be done by setting
+ * the resolutions array directly (here or on the map), by setting
+ * maxResolution and numZoomLevels, or by using scale based properties.
+ */
+ initialize: function(name, url, layername, options) {
+ this.layername = layername;
+ OpenLayers.Layer.Grid.prototype.initialize.apply(this,
+ [name, url, {}, options]);
+ this.extension = this.format.split('/')[1].toLowerCase();
+ this.extension = (this.extension == 'jpg') ? 'jpeg' : this.extension;
+ },
+
+ /**
+ * APIMethod: clone
+ * obj - {Object}
+ *
+ * Returns:
+ * {<OpenLayers.Layer.TileCache>} An exact clone of this
+ * <OpenLayers.Layer.TileCache>
+ */
+ clone: function (obj) {
+
+ if (obj == null) {
+ obj = new OpenLayers.Layer.TileCache(this.name,
+ this.url,
+ this.layername,
+ this.options);
+ }
+
+ //get all additions from superclasses
+ obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);
+
+ // copy/set any non-init, non-simple values here
+
+ return obj;
+ },
+
+ /**
+ * Method: getURL
+ *
+ * Parameters:
+ * bounds - {<OpenLayers.Bounds>}
+ *
+ * Returns:
+ * {String} A string with the layer's url and parameters and also the
+ * passed-in bounds and appropriate tile size specified as parameters.
+ */
+ getURL: function(bounds) {
+ var res = this.map.getResolution();
+ var bbox = this.maxExtent;
+ var size = this.tileSize;
+ var tileX = Math.round((bounds.left - bbox.left) / (res * size.w));
+ var tileY = Math.round((bounds.bottom - bbox.bottom) / (res * size.h));
+ var tileZ = this.map.zoom;
+ /**
+ * Zero-pad a positive integer.
+ * number - {Int}
+ * length - {Int}
+ *
+ * Returns:
+ * {String} A zero-padded string
+ */
+ function zeroPad(number, length) {
+ number = String(number);
+ var zeros = [];
+ for(var i=0; i<length; ++i) {
+ zeros.push('0');
+ }
+ return zeros.join('').substring(0, length - number.length) + number;
+ }
+ var components = [
+ this.layername,
+ zeroPad(tileZ, 2),
+ zeroPad(parseInt(tileX / 1000000), 3),
+ zeroPad((parseInt(tileX / 1000) % 1000), 3),
+ zeroPad((parseInt(tileX) % 1000), 3),
+ zeroPad(parseInt(tileY / 1000000), 3),
+ zeroPad((parseInt(tileY / 1000) % 1000), 3),
+ zeroPad((parseInt(tileY) % 1000), 3) + '.' + this.extension
+ ];
+ var path = components.join('/');
+ var url = this.url;
+ if (url instanceof Array) {
+ url = this.selectUrl(path, url);
+ }
+ url = (url.charAt(url.length - 1) == '/') ? url : url + '/';
+ return url + path;
+ },
+
+ /**
+ * Method: addTile
+ * Create a tile, initialize it, and add it to the layer div.
+ *
+ * Parameters:
+ * bounds - {<OpenLayers.Bounds>}
+ * position - {<OpenLayers.Pixel>}
+ *
+ * Returns:
+ * {<OpenLayers.Tile.Image>} The added <OpenLayers.Tile.Image>
+ */
+ addTile:function(bounds, position) {
+ var url = this.getURL(bounds);
+ return new OpenLayers.Tile.Image(this, position, bounds,
+ url, this.tileSize);
+ },
+
+ /**
+ * Method: setMap
+ * When the layer is added to a map, then we can fetch our origin
+ * (if we don't have one.)
+ *
+ * Parameters:
+ * map - {<OpenLayers.Map>}
+ */
+ setMap: function(map) {
+ OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);
+ if (!this.tileOrigin) {
+ this.tileOrigin = new OpenLayers.LonLat(this.map.maxExtent.left,
+ this.map.maxExtent.bottom);
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Layer.TileCache"
+});
+/* ======================================================================
OpenLayers/Layer/WMS.js
====================================================================== */
@@ -15617,7 +36753,8 @@
* Catch changeParams and uppercase the new params to be merged in
* before calling changeParams on the super class.
*
- * Once params have been changed, we will need to re-init our tiles.
+ * Once params have been changed, the tiles will be reloaded with
+ * the new parameters.
*
* Parameters:
* newParams - {Object} Hashtable of new params to use
@@ -15630,7 +36767,7 @@
},
/**
- * Method: getFullRequestString
+ * APIMethod: getFullRequestString
* Combine the layer's url with its params and these newParams.
*
* Add the SRS parameter from projection -- this is probably
@@ -15654,3 +36791,10969 @@
CLASS_NAME: "OpenLayers.Layer.WMS"
});
+/* ======================================================================
+ OpenLayers/Layer/WorldWind.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/Layer/Grid.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.WorldWind
+ *
+ * Inherits from:
+ * - <OpenLayers.Layer.Grid>
+ */
+OpenLayers.Layer.WorldWind = OpenLayers.Class(OpenLayers.Layer.Grid, {
+
+ DEFAULT_PARAMS: {
+ },
+
+ /**
+ * APIProperty: isBaseLayer
+ * WorldWind layer is a base layer by default.
+ */
+ isBaseLayer: true,
+
+
+ /**
+ * APIProperty: lzd
+ * LevelZeroTileSizeDegrees
+ */
+ lzd: null,
+
+ /**
+ * APIProperty: zoomLevels
+ * Number of zoom levels.
+ */
+ zoomLevels: null,
+
+ /**
+ * Constructor: OpenLayers.Layer.WorldWind
+ *
+ * Parameters:
+ * name - {String} Name of Layer
+ * url - {String} Base URL
+ * lzd - {Float} Level zero tile size degrees
+ * zoomLevels - {Int} number of zoom levels
+ * params - {Object} additional parameters
+ * options - {Object} additional options
+ */
+ initialize: function(name, url, lzd, zoomLevels, params, options) {
+ this.lzd = lzd;
+ this.zoomLevels = zoomLevels;
+ var newArguments = [];
+ newArguments.push(name, url, params, options);
+ OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);
+ this.params = OpenLayers.Util.applyDefaults(
+ this.params, this.DEFAULT_PARAMS
+ );
+ },
+ /**
+ * Method: addTile
+ *
+ * Parameters:
+ * bounds - {<OpenLayers.Bounds>}
+ * position - {<OpenLayers.Pixel>}
+ *
+ * Returns:
+ * {<OpenLayers.Tile.Image>} The added OpenLayers.Tile.Image
+ */
+ addTile:function(bounds,position) {
+ return new OpenLayers.Tile.Image(this, position, bounds,
+ null, this.tileSize);
+ },
+
+ /**
+ * Method: getZoom
+ * Convert map zoom to WW zoom.
+ */
+ getZoom: function () {
+ var zoom = this.map.getZoom();
+ var extent = this.map.getMaxExtent();
+ zoom = zoom - Math.log(this.maxResolution / (this.lzd/512))/Math.log(2);
+ return zoom;
+ },
+
+ /**
+ * Method: getURL
+ *
+ * Parameters:
+ * bounds - {<OpenLayers.Bounds>}
+ *
+ * Returns:
+ * {String} A string with the layer's url and parameters and also the
+ * passed-in bounds and appropriate tile size specified as
+ * parameters
+ */
+ getURL: function (bounds) {
+ bounds = this.adjustBounds(bounds);
+ var zoom = this.getZoom();
+ var extent = this.map.getMaxExtent();
+ var deg = this.lzd/Math.pow(2,this.getZoom());
+ var x = Math.floor((bounds.left - extent.left)/deg);
+ var y = Math.floor((bounds.bottom - extent.bottom)/deg);
+ if (this.map.getResolution() <= (this.lzd/512)
+ && this.getZoom() <= this.zoomLevels) {
+ return this.getFullRequestString(
+ { L: zoom,
+ X: x,
+ Y: y
+ });
+ } else {
+ return OpenLayers.Util.getImagesLocation() + "blank.gif";
+ }
+
+ },
+
+ CLASS_NAME: "OpenLayers.Layer.WorldWind"
+});
+/* ======================================================================
+ 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
+ ====================================================================== */
+
+/* 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/Style.js
+ * @requires OpenLayers/Feature/Vector.js
+ */
+
+/**
+ * Class: OpenLayers.StyleMap
+ */
+OpenLayers.StyleMap = OpenLayers.Class({
+
+ /**
+ * Property: styles
+ * Hash of {<OpenLayers.Style>}, keyed by names of well known
+ * rendering intents (e.g. "default", "temporary", "select").
+ */
+ styles: null,
+
+ /**
+ * Property: extendDefault
+ * {Boolean} if true, every render intent will extend the symbolizers
+ * specified for the "default" intent at rendering time. Otherwise, every
+ * rendering intent will be treated as a completely independent style.
+ */
+ extendDefault: true,
+
+ /**
+ * Constructor: OpenLayers.StyleMap
+ *
+ * Parameters:
+ * style - {Object} Optional. Either a style hash, or a style object, or
+ * a hash of style objects (style hashes) keyed by rendering
+ * intent. If just one style hash or style object is passed,
+ * this will be used for all known render intents (default,
+ * select, temporary)
+ * options - {Object} optional hash of additional options for this
+ * instance
+ */
+ initialize: function (style, options) {
+ this.styles = {
+ "default": new OpenLayers.Style(
+ OpenLayers.Feature.Vector.style["default"]),
+ "select": new OpenLayers.Style(
+ OpenLayers.Feature.Vector.style["select"]),
+ "temporary": new OpenLayers.Style(
+ OpenLayers.Feature.Vector.style["temporary"])
+ };
+
+ // take whatever the user passed as style parameter and convert it
+ // into parts of stylemap.
+ if(style instanceof OpenLayers.Style) {
+ // user passed a style object
+ this.styles["default"] = style;
+ this.styles["select"] = style;
+ this.styles["temporary"] = style;
+ } else if(typeof style == "object") {
+ for(var key in style) {
+ if(style[key] instanceof OpenLayers.Style) {
+ // user passed a hash of style objects
+ this.styles[key] = style[key];
+ } else if(typeof style[key] == "object") {
+ // user passsed a hash of style hashes
+ this.styles[key] = new OpenLayers.Style(style[key]);
+ } else {
+ // user passed a style hash (i.e. symbolizer)
+ this.styles["default"] = new OpenLayers.Style(style);
+ this.styles["select"] = new OpenLayers.Style(style);
+ this.styles["temporary"] = new OpenLayers.Style(style);
+ break;
+ }
+ }
+ }
+ OpenLayers.Util.extend(this, options);
+ },
+
+ /**
+ * Method: destroy
+ */
+ destroy: function() {
+ for(var key in this.styles) {
+ this.styles[key].destroy();
+ }
+ this.styles = null;
+ },
+
+ /**
+ * Method: createSymbolizer
+ * Creates the symbolizer for a feature for a render intent.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature>} The feature to evaluate the rules
+ * of the intended style against.
+ * intent - {String} The intent determines the symbolizer that will be
+ * used to draw the feature. Well known intents are "default"
+ * (for just drawing the features), "select" (for selected
+ * features) and "temporary" (for drawing features).
+ *
+ * Returns:
+ * {Object} symbolizer hash
+ */
+ createSymbolizer: function(feature, intent) {
+ if(!feature) {
+ feature = new OpenLayers.Feature.Vector();
+ }
+ if(!this.styles[intent]) {
+ intent = "default";
+ }
+ feature.renderIntent = intent;
+ var defaultSymbolizer = {};
+ if(this.extendDefault && intent != "default") {
+ defaultSymbolizer = this.styles["default"].createSymbolizer(feature);
+ }
+ return OpenLayers.Util.extend(defaultSymbolizer,
+ this.styles[intent].createSymbolizer(feature));
+ },
+
+ /**
+ * Method: addUniqueValueRules
+ * Convenience method to create comparison rules for unique values of a
+ * property. The rules will be added to the style object for a specified
+ * rendering intent. This method is a shortcut for creating something like
+ * the "unique value legends" familiar from well known desktop GIS systems
+ *
+ * Parameters:
+ * renderIntent - {String} rendering intent to add the rules to
+ * property - {String} values of feature attributes to create the
+ * rules for
+ * symbolizers - {Object} Hash of symbolizers, keyed by the desired
+ * property values
+ * context - {Object} An optional object with properties that
+ * symbolizers' property values should be evaluated
+ * against. If no context is specified, feature.attributes
+ * will be used
+ */
+ addUniqueValueRules: function(renderIntent, property, symbolizers, context) {
+ var rules = [];
+ for (var value in symbolizers) {
+ rules.push(new OpenLayers.Rule({
+ symbolizer: symbolizers[value],
+ context: context,
+ filter: new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.EQUAL_TO,
+ property: property,
+ value: value
+ })
+ }));
+ }
+ this.styles[renderIntent].addRules(rules);
+ },
+
+ CLASS_NAME: "OpenLayers.StyleMap"
+});
+/* ======================================================================
+ OpenLayers/Control/NavToolbar.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/Panel.js
+ * @requires OpenLayers/Control/Navigation.js
+ * @requires OpenLayers/Control/ZoomBox.js
+ */
+
+/**
+ * Class: OpenLayers.Control.NavToolbar
+ *
+ * Inherits from:
+ * - <OpenLayers.Control.Panel>
+ */
+OpenLayers.Control.NavToolbar = OpenLayers.Class(OpenLayers.Control.Panel, {
+
+ /**
+ * Constructor: OpenLayers.Control.NavToolbar
+ * Add our two mousedefaults controls.
+ *
+ * Parameters:
+ * options - {Object} An optional object whose properties will be used
+ * to extend the control.
+ */
+ initialize: function(options) {
+ OpenLayers.Control.Panel.prototype.initialize.apply(this, [options]);
+ this.addControls([
+ new OpenLayers.Control.Navigation(),
+ new OpenLayers.Control.ZoomBox()
+ ]);
+ },
+
+ /**
+ * Method: draw
+ * calls the default draw, and then activates mouse defaults.
+ */
+ draw: function() {
+ var div = OpenLayers.Control.Panel.prototype.draw.apply(this, arguments);
+ this.activateControl(this.controls[0]);
+ return div;
+ },
+
+ CLASS_NAME: "OpenLayers.Control.NavToolbar"
+});
+/* ======================================================================
+ OpenLayers/Filter/Comparison.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/Filter.js
+ */
+
+/**
+ * Class: OpenLayers.Filter.Comparison
+ * This class represents a comparison filter.
+ *
+ * Inherits from
+ * - <OpenLayers.Filter>
+ */
+OpenLayers.Filter.Comparison = OpenLayers.Class(OpenLayers.Filter, {
+
+ /**
+ * APIProperty: type
+ * {String} type: type of the comparison. This is one of
+ * - OpenLayers.Filter.Comparison.EQUAL_TO = "==";
+ * - OpenLayers.Filter.Comparison.NOT_EQUAL_TO = "!=";
+ * - OpenLayers.Filter.Comparison.LESS_THAN = "<";
+ * - OpenLayers.Filter.Comparison.GREATER_THAN = ">";
+ * - OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO = "<=";
+ * - OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO = ">=";
+ * - OpenLayers.Filter.Comparison.BETWEEN = "..";
+ * - OpenLayers.Filter.Comparison.LIKE = "~";
+ */
+ type: null,
+
+ /**
+ * APIProperty: property
+ * {String}
+ * name of the context property to compare
+ */
+ property: null,
+
+ /**
+ * APIProperty: value
+ * {Number} or {String}
+ * comparison value for binary comparisons. In the case of a String, this
+ * can be a combination of text and propertyNames in the form
+ * "literal ${propertyName}"
+ */
+ value: null,
+
+ /**
+ * APIProperty: lowerBoundary
+ * {Number} or {String}
+ * lower boundary for between comparisons. In the case of a String, this
+ * can be a combination of text and propertyNames in the form
+ * "literal ${propertyName}"
+ */
+ lowerBoundary: null,
+
+ /**
+ * APIProperty: upperBoundary
+ * {Number} or {String}
+ * upper boundary for between comparisons. In the case of a String, this
+ * can be a combination of text and propertyNames in the form
+ * "literal ${propertyName}"
+ */
+ upperBoundary: null,
+
+ /**
+ * Constructor: OpenLayers.Filter.Comparison
+ * Creates a comparison rule.
+ *
+ * Parameters:
+ * options - {Object} An optional object with properties to set on the
+ * rule
+ *
+ * Returns:
+ * {<OpenLayers.Filter.Comparison>}
+ */
+ initialize: function(options) {
+ OpenLayers.Filter.prototype.initialize.apply(this, [options]);
+ },
+
+ /**
+ * APIMethod: evaluate
+ * Evaluates this filter in a specific context. Should be implemented by
+ * subclasses.
+ *
+ * Parameters:
+ * context - {Object} Context to use in evaluating the filter.
+ *
+ * Returns:
+ * {Boolean} The filter applies.
+ */
+ evaluate: function(context) {
+ switch(this.type) {
+ case OpenLayers.Filter.Comparison.EQUAL_TO:
+ case OpenLayers.Filter.Comparison.LESS_THAN:
+ case OpenLayers.Filter.Comparison.GREATER_THAN:
+ case OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO:
+ case OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO:
+ return this.binaryCompare(context, this.property, this.value);
+
+ case OpenLayers.Filter.Comparison.BETWEEN:
+ var result =
+ context[this.property] >= this.lowerBoundary;
+ result = result &&
+ context[this.property] <= this.upperBoundary;
+ return result;
+ case OpenLayers.Filter.Comparison.LIKE:
+ var regexp = new RegExp(this.value,
+ "gi");
+ return regexp.test(context[this.property]);
+ }
+ },
+
+ /**
+ * APIMethod: value2regex
+ * Converts the value of this rule into a regular expression string,
+ * according to the wildcard characters specified. This method has to
+ * be called after instantiation of this class, if the value is not a
+ * regular expression already.
+ *
+ * Parameters:
+ * wildCard - {<Char>} wildcard character in the above value, default
+ * is "*"
+ * singleChar - {<Char>) single-character wildcard in the above value
+ * default is "."
+ * escape - {<Char>) escape character in the above value, default is
+ * "!"
+ *
+ * Returns:
+ * {String} regular expression string
+ */
+ value2regex: function(wildCard, singleChar, escapeChar) {
+ if (wildCard == ".") {
+ var msg = "'.' is an unsupported wildCard character for "+
+ "OpenLayers.Filter.Comparison";
+ OpenLayers.Console.error(msg);
+ return null;
+ }
+
+
+ // set UMN MapServer defaults for unspecified parameters
+ wildCard = wildCard ? wildCard : "*";
+ singleChar = singleChar ? singleChar : ".";
+ escapeChar = escapeChar ? escapeChar : "!";
+
+ this.value = this.value.replace(
+ new RegExp("\\"+escapeChar+"(.|$)", "g"), "\\$1")
+ this.value = this.value.replace(
+ new RegExp("\\"+singleChar, "g"), ".");
+ this.value = this.value.replace(
+ new RegExp("\\"+wildCard, "g"), ".*");
+ this.value = this.value.replace(
+ new RegExp("\\\\.\\*", "g"), "\\"+wildCard);
+ this.value = this.value.replace(
+ new RegExp("\\\\\\.", "g"), "\\"+singleChar);
+
+ return this.value;
+ },
+
+ /**
+ * Method: regex2value
+ * Convert the value of this rule from a regular expression string into an
+ * ogc literal string using a wildCard of *, a singleChar of ., and an
+ * escape of !. Leaves the <value> property unmodified.
+ *
+ * Returns:
+ * {String} A string value.
+ */
+ regex2value: function() {
+
+ var value = this.value;
+
+ // replace ! with !!
+ value = value.replace(/!/g, "!!");
+
+ // replace \. with !. (watching out for \\.)
+ value = value.replace(/(\\)?\\\./g, function($0, $1) {
+ return $1 ? $0 : "!.";
+ });
+
+ // replace \* with #* (watching out for \\*)
+ value = value.replace(/(\\)?\\\*/g, function($0, $1) {
+ return $1 ? $0 : "!*";
+ });
+
+ // replace \\ with \
+ value = value.replace(/\\\\/g, "\\");
+
+ // convert .* to * (the sequence #.* is not allowed)
+ value = value.replace(/\.\*/g, "*");
+
+ return value;
+ },
+
+ /**
+ * Function: binaryCompare
+ * Compares a feature property to a rule value
+ *
+ * Parameters:
+ * context - {Object}
+ * property - {String} or {Number}
+ * value - {String} or {Number}, same as property
+ *
+ * Returns:
+ * {Boolean}
+ */
+ binaryCompare: function(context, property, value) {
+ switch (this.type) {
+ case OpenLayers.Filter.Comparison.EQUAL_TO:
+ return context[property] == value;
+ case OpenLayers.Filter.Comparison.NOT_EQUAL_TO:
+ return context[property] != value;
+ case OpenLayers.Filter.Comparison.LESS_THAN:
+ return context[property] < value;
+ case OpenLayers.Filter.Comparison.GREATER_THAN:
+ return context[property] > value;
+ case OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO:
+ return context[property] <= value;
+ case OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO:
+ return context[property] >= value;
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Filter.Comparison"
+});
+
+
+OpenLayers.Filter.Comparison.EQUAL_TO = "==";
+OpenLayers.Filter.Comparison.NOT_EQUAL_TO = "!=";
+OpenLayers.Filter.Comparison.LESS_THAN = "<";
+OpenLayers.Filter.Comparison.GREATER_THAN = ">";
+OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO = "<=";
+OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO = ">=";
+OpenLayers.Filter.Comparison.BETWEEN = "..";
+OpenLayers.Filter.Comparison.LIKE = "~";
+/* ======================================================================
+ OpenLayers/Filter/FeatureId.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/Filter.js
+ */
+
+/**
+ * Class: OpenLayers.Filter.FeatureId
+ * This class represents a ogc:FeatureId Filter, as being used for rule-based SLD
+ * styling
+ *
+ * Inherits from
+ * - <OpenLayers.Filter>
+ */
+OpenLayers.Filter.FeatureId = OpenLayers.Class(OpenLayers.Filter, {
+
+ /**
+ * APIProperty: fids
+ * {Array(String)} Feature Ids to evaluate this rule against. To be passed
+ * To be passed inside the params object.
+ */
+ fids: null,
+
+ /**
+ * Constructor: OpenLayers.Filter.FeatureId
+ * Creates an ogc:FeatureId rule.
+ *
+ * Parameters:
+ * options - {Object} An optional object with properties to set on the
+ * rule
+ *
+ * Returns:
+ * {<OpenLayers.Filter.FeatureId>}
+ */
+ initialize: function(options) {
+ this.fids = [];
+ OpenLayers.Filter.prototype.initialize.apply(this, [options]);
+ },
+
+ /**
+ * APIMethod: evaluate
+ * evaluates this rule for a specific feature
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature>} feature to apply the rule to.
+ * For vector features, the check is run against the fid,
+ * for plain features against the id.
+ *
+ * Returns:
+ * {Boolean} true if the rule applies, false if it does not
+ */
+ evaluate: function(feature) {
+ for (var i=0, len=this.fids.length; i<len; i++) {
+ var fid = feature.fid || feature.id;
+ if (fid == this.fids[i]) {
+ return true;
+ }
+ }
+ return false;
+ },
+
+ CLASS_NAME: "OpenLayers.Filter.FeatureId"
+});
+/* ======================================================================
+ OpenLayers/Filter/Logical.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/Filter.js
+ */
+
+/**
+ * Class: OpenLayers.Filter.Logical
+ * This class represents ogc:And, ogc:Or and ogc:Not rules.
+ *
+ * Inherits from
+ * - <OpenLayers.Filter>
+ */
+OpenLayers.Filter.Logical = OpenLayers.Class(OpenLayers.Filter, {
+
+ /**
+ * APIProperty: filters
+ * {Array(<OpenLayers.Filter>)} Child filters for this filter.
+ */
+ filters: null,
+
+ /**
+ * APIProperty: type
+ * {String} type of logical operator. Available types are:
+ * - OpenLayers.Filter.Locical.AND = "&&";
+ * - OpenLayers.Filter.Logical.OR = "||";
+ * - OpenLayers.Filter.Logical.NOT = "!";
+ */
+ type: null,
+
+ /**
+ * Constructor: OpenLayers.Filter.Logical
+ * Creates a logical filter (And, Or, Not).
+ *
+ * Parameters:
+ * options - {Object} An optional object with properties to set on the
+ * filter.
+ *
+ * Returns:
+ * {<OpenLayers.Filter.Logical>}
+ */
+ initialize: function(options) {
+ this.filters = [];
+ OpenLayers.Filter.prototype.initialize.apply(this, [options]);
+ },
+
+ /**
+ * APIMethod: destroy
+ * Remove reference to child filters.
+ */
+ destroy: function() {
+ this.filters = null;
+ OpenLayers.Filter.prototype.destroy.apply(this);
+ },
+
+ /**
+ * APIMethod: evaluate
+ * Evaluates this filter in a specific context. Should be implemented by
+ * subclasses.
+ *
+ * Parameters:
+ * context - {Object} Context to use in evaluating the filter.
+ *
+ * Returns:
+ * {Boolean} The filter applies.
+ */
+ evaluate: function(context) {
+ switch(this.type) {
+ case OpenLayers.Filter.Logical.AND:
+ for (var i=0, len=this.filters.length; i<len; i++) {
+ if (this.filters[i].evaluate(context) == false) {
+ return false;
+ }
+ }
+ return true;
+
+ case OpenLayers.Filter.Logical.OR:
+ for (var i=0, len=this.filters.length; i<len; i++) {
+ if (this.filters[i].evaluate(context) == true) {
+ return true;
+ }
+ }
+ return false;
+
+ case OpenLayers.Filter.Logical.NOT:
+ return (!this.filters[0].evaluate(context));
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Filter.Logical"
+});
+
+
+OpenLayers.Filter.Logical.AND = "&&";
+OpenLayers.Filter.Logical.OR = "||";
+OpenLayers.Filter.Logical.NOT = "!";
+/* ======================================================================
+ OpenLayers/Geometry/Collection.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.js
+ */
+
+/**
+ * Class: OpenLayers.Geometry.Collection
+ * A Collection is exactly what it sounds like: A collection of different
+ * Geometries. These are stored in the local parameter <components> (which
+ * can be passed as a parameter to the constructor).
+ *
+ * As new geometries are added to the collection, they are NOT cloned.
+ * When removing geometries, they need to be specified by reference (ie you
+ * have to pass in the *exact* geometry to be removed).
+ *
+ * The <getArea> and <getLength> functions here merely iterate through
+ * the components, summing their respective areas and lengths.
+ *
+ * Create a new instance with the <OpenLayers.Geometry.Collection> constructor.
+ *
+ * Inerhits from:
+ * - <OpenLayers.Geometry>
+ */
+OpenLayers.Geometry.Collection = OpenLayers.Class(OpenLayers.Geometry, {
+
+ /**
+ * APIProperty: components
+ * {Array(<OpenLayers.Geometry>)} The component parts of this geometry
+ */
+ components: null,
+
+ /**
+ * 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: null,
+
+ /**
+ * Constructor: OpenLayers.Geometry.Collection
+ * Creates a Geometry Collection -- a list of geoms.
+ *
+ * Parameters:
+ * components - {Array(<OpenLayers.Geometry>)} Optional array of geometries
+ *
+ */
+ initialize: function (components) {
+ OpenLayers.Geometry.prototype.initialize.apply(this, arguments);
+ this.components = [];
+ if (components != null) {
+ this.addComponents(components);
+ }
+ },
+
+ /**
+ * APIMethod: destroy
+ * Destroy this geometry.
+ */
+ destroy: function () {
+ this.components.length = 0;
+ this.components = null;
+ },
+
+ /**
+ * APIMethod: clone
+ * Clone this geometry.
+ *
+ * Returns:
+ * {<OpenLayers.Geometry.Collection>} An exact clone of this collection
+ */
+ clone: function() {
+ var geometry = eval("new " + this.CLASS_NAME + "()");
+ for(var i=0, len=this.components.length; i<len; i++) {
+ geometry.addComponent(this.components[i].clone());
+ }
+
+ // catch any randomly tagged-on properties
+ OpenLayers.Util.applyDefaults(geometry, this);
+
+ return geometry;
+ },
+
+ /**
+ * Method: getComponentsString
+ * Get a string representing the components for this collection
+ *
+ * Returns:
+ * {String} A string representation of the components of this geometry
+ */
+ getComponentsString: function(){
+ var strings = [];
+ for(var i=0, len=this.components.length; i<len; i++) {
+ strings.push(this.components[i].toShortString());
+ }
+ return strings.join(",");
+ },
+
+ /**
+ * APIMethod: calculateBounds
+ * Recalculate the bounds by iterating through the components and
+ * calling calling extendBounds() on each item.
+ */
+ calculateBounds: function() {
+ this.bounds = null;
+ if ( this.components && this.components.length > 0) {
+ this.setBounds(this.components[0].getBounds());
+ for (var i=1, len=this.components.length; i<len; i++) {
+ this.extendBounds(this.components[i].getBounds());
+ }
+ }
+ },
+
+ /**
+ * APIMethod: addComponents
+ * Add components to this geometry.
+ *
+ * Parameters:
+ * components - {Array(<OpenLayers.Geometry>)} An array of geometries to add
+ */
+ addComponents: function(components){
+ if(!(components instanceof Array)) {
+ components = [components];
+ }
+ for(var i=0, len=components.length; i<len; i++) {
+ this.addComponent(components[i]);
+ }
+ },
+
+ /**
+ * Method: addComponent
+ * Add a new component (geometry) to the collection. If this.componentTypes
+ * is set, then the component class name must be in the componentTypes array.
+ *
+ * The bounds cache is reset.
+ *
+ * Parameters:
+ * component - {<OpenLayers.Geometry>} A geometry to add
+ * index - {int} Optional index into the array to insert the component
+ *
+ * Returns:
+ * {Boolean} The component geometry was successfully added
+ */
+ addComponent: function(component, index) {
+ var added = false;
+ if(component) {
+ if(this.componentTypes == null ||
+ (OpenLayers.Util.indexOf(this.componentTypes,
+ component.CLASS_NAME) > -1)) {
+
+ if(index != null && (index < this.components.length)) {
+ var components1 = this.components.slice(0, index);
+ var components2 = this.components.slice(index,
+ this.components.length);
+ components1.push(component);
+ this.components = components1.concat(components2);
+ } else {
+ this.components.push(component);
+ }
+ component.parent = this;
+ this.clearBounds();
+ added = true;
+ }
+ }
+ return added;
+ },
+
+ /**
+ * APIMethod: removeComponents
+ * Remove components from this geometry.
+ *
+ * Parameters:
+ * components - {Array(<OpenLayers.Geometry>)} The components to be removed
+ */
+ removeComponents: function(components) {
+ if(!(components instanceof Array)) {
+ components = [components];
+ }
+ for(var i=components.length-1; i>=0; --i) {
+ this.removeComponent(components[i]);
+ }
+ },
+
+ /**
+ * Method: removeComponent
+ * Remove a component from this geometry.
+ *
+ * Parameters:
+ * component - {<OpenLayers.Geometry>}
+ */
+ removeComponent: function(component) {
+
+ OpenLayers.Util.removeItem(this.components, component);
+
+ // clearBounds() so that it gets recalculated on the next call
+ // to this.getBounds();
+ this.clearBounds();
+ },
+
+ /**
+ * APIMethod: getLength
+ * Calculate the length of this geometry
+ *
+ * Returns:
+ * {Float} The length of the geometry
+ */
+ getLength: function() {
+ var length = 0.0;
+ for (var i=0, len=this.components.length; i<len; i++) {
+ length += this.components[i].getLength();
+ }
+ return length;
+ },
+
+ /**
+ * APIMethod: getArea
+ * Calculate the area of this geometry. Note how this function is overridden
+ * in <OpenLayers.Geometry.Polygon>.
+ *
+ * Returns:
+ * {Float} The area of the collection by summing its parts
+ */
+ getArea: function() {
+ var area = 0.0;
+ for (var i=0, len=this.components.length; i<len; i++) {
+ area += this.components[i].getArea();
+ }
+ return area;
+ },
+
+ /**
+ * 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; 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; ++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; i<this.components.length; ++i) {
+ this.components[i].resize(scale, origin, ratio);
+ }
+ },
+
+ /**
+ * APIMethod: equals
+ * Tests for equivalent geometries
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry>}
+ *
+ * Returns:
+ * {Boolean} The coordinates are equivalent
+ */
+ equals: function(geometry) {
+ var equivalent = true;
+ if(!geometry || !geometry.CLASS_NAME ||
+ (this.CLASS_NAME != geometry.CLASS_NAME)) {
+ equivalent = false;
+ } else if(!(geometry.components instanceof Array) ||
+ (geometry.components.length != this.components.length)) {
+ equivalent = false;
+ } else {
+ for(var i=0, len=this.components.length; i<len; ++i) {
+ if(!this.components[i].equals(geometry.components[i])) {
+ equivalent = false;
+ break;
+ }
+ }
+ }
+ return equivalent;
+ },
+
+ /**
+ * 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; i++) {
+ var component = this.components[i];
+ component.transform(source, dest);
+ }
+ this.bounds = null;
+ }
+ return this;
+ },
+
+ /**
+ * 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;
+ for(var i=0, len=this.components.length; i<len; ++ i) {
+ intersect = geometry.intersects(this.components[i]);
+ if(intersect) {
+ break;
+ }
+ }
+ return intersect;
+ },
+
+ CLASS_NAME: "OpenLayers.Geometry.Collection"
+});
+/* ======================================================================
+ OpenLayers/Geometry/Point.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.js
+ */
+
+/**
+ * Class: OpenLayers.Geometry.Point
+ * Point geometry class.
+ *
+ * Inherits from:
+ * - <OpenLayers.Geometry>
+ */
+OpenLayers.Geometry.Point = OpenLayers.Class(OpenLayers.Geometry, {
+
+ /**
+ * APIProperty: x
+ * {float}
+ */
+ x: null,
+
+ /**
+ * APIProperty: y
+ * {float}
+ */
+ y: null,
+
+ /**
+ * Constructor: OpenLayers.Geometry.Point
+ * Construct a point geometry.
+ *
+ * Parameters:
+ * x - {float}
+ * y - {float}
+ *
+ */
+ initialize: function(x, y) {
+ OpenLayers.Geometry.prototype.initialize.apply(this, arguments);
+
+ this.x = parseFloat(x);
+ this.y = parseFloat(y);
+ },
+
+ /**
+ * APIMethod: clone
+ *
+ * Returns:
+ * {<OpenLayers.Geometry.Point>} An exact clone of this OpenLayers.Geometry.Point
+ */
+ clone: function(obj) {
+ if (obj == null) {
+ obj = new OpenLayers.Geometry.Point(this.x, this.y);
+ }
+
+ // catch any randomly tagged-on properties
+ OpenLayers.Util.applyDefaults(obj, this);
+
+ return obj;
+ },
+
+ /**
+ * Method: calculateBounds
+ * Create a new Bounds based on the lon/lat
+ */
+ calculateBounds: function () {
+ this.bounds = new OpenLayers.Bounds(this.x, this.y,
+ this.x, this.y);
+ },
+
+ /**
+ * APIMethod: distanceTo
+ *
+ * Parameters:
+ * point - {<OpenLayers.Geometry.Point>}
+ */
+ distanceTo: function(point) {
+ var distance = 0.0;
+ if ( (this.x != null) && (this.y != null) &&
+ (point != null) && (point.x != null) && (point.y != null) ) {
+
+ var dx2 = Math.pow(this.x - point.x, 2);
+ var dy2 = Math.pow(this.y - point.y, 2);
+ distance = Math.sqrt( dx2 + dy2 );
+ }
+ return distance;
+ },
+
+ /**
+ * APIMethod: equals
+ *
+ * Parameters:
+ * xy - {<OpenLayers.Geometry>}
+ *
+ * Returns:
+ * {Boolean} Boolean value indicating whether the passed-in
+ * {<OpenLayers.Geometry>} object has the same components as this
+ * note that if ll passed in is null, returns false
+ *
+ */
+ equals:function(geom) {
+ var equals = false;
+ if (geom != null) {
+ equals = ((this.x == geom.x && this.y == geom.y) ||
+ (isNaN(this.x) && isNaN(this.y) && isNaN(geom.x) && isNaN(geom.y)));
+ }
+ return equals;
+ },
+
+ /**
+ * Method: toShortString
+ *
+ * Returns:
+ * {String} Shortened String representation of Point object.
+ * (ex. <i>"5, 42"</i>)
+ */
+ toShortString: function() {
+ return (this.x + ", " + this.y);
+ },
+
+ /**
+ * APIMethod: move
+ * Moves a point in place
+ *
+ * Parameters:
+ * x - {Float}
+ * y - {Float}
+ */
+ move: function(x, y) {
+ this.x = this.x + x;
+ this.y = this.y + y;
+ this.clearBounds();
+ },
+
+ /**
+ * APIMethod: rotate
+ * Rotate a point around another.
+ *
+ * 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) {
+ angle *= Math.PI / 180;
+ var radius = this.distanceTo(origin);
+ var theta = angle + Math.atan2(this.y - origin.y, this.x - origin.x);
+ this.x = origin.x + (radius * Math.cos(theta));
+ this.y = origin.y + (radius * Math.sin(theta));
+ this.clearBounds();
+ },
+
+ /**
+ * APIMethod: resize
+ * Resize a point relative to some origin. For points, this has the effect
+ * of scaling a vector (from the origin to the point). This method is
+ * more useful on geometry collection subclasses.
+ *
+ * Parameters:
+ * scale - {Float} Ratio of the new distance from the origin to the old
+ * distance from the origin. A scale of 2 doubles the
+ * distance between the point and origin.
+ * 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) {
+ ratio = (ratio == undefined) ? 1 : ratio;
+ this.x = origin.x + (scale * ratio * (this.x - origin.x));
+ this.y = origin.y + (scale * (this.y - origin.y));
+ this.clearBounds();
+ },
+
+ /**
+ * 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.equals(geometry);
+ } else {
+ intersect = geometry.intersects(this);
+ }
+ return intersect;
+ },
+
+ /**
+ * APIMethod: transform
+ * Translate the x,y properties of the point from source to dest.
+ *
+ * Parameters:
+ * source - {<OpenLayers.Projection>}
+ * dest - {<OpenLayers.Projection>}
+ *
+ * Returns:
+ * {<OpenLayers.Geometry>}
+ */
+ transform: function(source, dest) {
+ if ((source && dest)) {
+ OpenLayers.Projection.transform(
+ this, source, dest);
+ this.bounds = null;
+ }
+ return this;
+ },
+
+ CLASS_NAME: "OpenLayers.Geometry.Point"
+});
+/* ======================================================================
+ OpenLayers/Geometry/Rectangle.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.js
+ */
+
+/**
+ * Class: OpenLayers.Geometry.Rectangle
+ * This class is *not supported*, and probably isn't what you're looking for.
+ * Instead, most users probably want something like:
+ * (code)
+ * var poly = new OpenLayers.Bounds(0,0,10,10).toGeometry();
+ * (end)
+ * This will create a rectangular Polygon geometry.
+ *
+ * Inherits:
+ * - <OpenLayers.Geometry>
+ */
+
+OpenLayers.Geometry.Rectangle = OpenLayers.Class(OpenLayers.Geometry, {
+
+ /**
+ * Property: x
+ * {Float}
+ */
+ x: null,
+
+ /**
+ * Property: y
+ * {Float}
+ */
+ y: null,
+
+ /**
+ * Property: width
+ * {Float}
+ */
+ width: null,
+
+ /**
+ * Property: height
+ * {Float}
+ */
+ height: null,
+
+ /**
+ * Constructor: OpenLayers.Geometry.Rectangle
+ *
+ * Parameters:
+ * points - {Array(<OpenLayers.Geometry.Point>}
+ */
+ initialize: function(x, y, width, height) {
+ OpenLayers.Geometry.prototype.initialize.apply(this, arguments);
+
+ this.x = x;
+ this.y = y;
+
+ this.width = width;
+ this.height = height;
+ },
+
+ /**
+ * Method: calculateBounds
+ * Recalculate the bounds for the geometry.
+ */
+ calculateBounds: function() {
+ this.bounds = new OpenLayers.Bounds(this.x, this.y,
+ this.x + this.width,
+ this.y + this.height);
+ },
+
+
+ /**
+ * APIMethod: getLength
+ *
+ * Returns:
+ * {Float} The length of the geometry
+ */
+ getLength: function() {
+ var length = (2 * this.width) + (2 * this.height);
+ return length;
+ },
+
+ /**
+ * APIMethod: getArea
+ *
+ * Returns:
+ * {Float} The area of the geometry
+ */
+ getArea: function() {
+ var area = this.width * this.height;
+ return area;
+ },
+
+ CLASS_NAME: "OpenLayers.Geometry.Rectangle"
+});
+/* ======================================================================
+ OpenLayers/Geometry/Surface.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.js
+ */
+
+OpenLayers.Geometry.Surface = OpenLayers.Class(OpenLayers.Geometry, {
+
+ initialize: function() {
+ OpenLayers.Geometry.prototype.initialize.apply(this, arguments);
+ },
+
+ CLASS_NAME: "OpenLayers.Geometry.Surface"
+});
+/* ======================================================================
+ OpenLayers/Layer/MapServer/Untiled.js
+ ====================================================================== */
+
+/* Copyright 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/Layer/MapServer.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.MapServer.Untiled
+ * *Deprecated*. To be removed in 3.0. Instead use OpenLayers.Layer.MapServer
+ * and pass the option 'singleTile' as true.
+ *
+ * Inherits from:
+ * - <OpenLayers.Layer.MapServer>
+ */
+OpenLayers.Layer.MapServer.Untiled = OpenLayers.Class(OpenLayers.Layer.MapServer, {
+
+ /**
+ * APIProperty: singleTile
+ * {singleTile} Always true for untiled.
+ */
+ singleTile: true,
+
+ /**
+ * Constructor: OpenLayers.Layer.MapServer.Untiled
+ *
+ * Parameters:
+ * name - {String}
+ * url - {String}
+ * params - {Object}
+ * options - {Object}
+ */
+ initialize: function(name, url, params, options) {
+ OpenLayers.Layer.MapServer.prototype.initialize.apply(this, arguments);
+
+ var msg = "The OpenLayers.Layer.MapServer.Untiled class is deprecated and " +
+ "will be removed in 3.0. Instead, you should use the " +
+ "normal OpenLayers.Layer.MapServer class, passing it the option " +
+ "'singleTile' as true.";
+ OpenLayers.Console.warn(msg);
+ },
+
+ /**
+ * Method: clone
+ * Create a clone of this layer
+ *
+ * Returns:
+ * {<OpenLayers.Layer.MapServer.Untiled>} An exact clone of this layer
+ */
+ clone: function (obj) {
+
+ if (obj == null) {
+ obj = new OpenLayers.Layer.MapServer.Untiled(this.name,
+ this.url,
+ this.params,
+ this.options);
+ }
+
+ //get all additions from superclasses
+ obj = OpenLayers.Layer.MapServer.prototype.clone.apply(this, [obj]);
+
+ // copy/set any non-init, non-simple values here
+
+ return obj;
+ },
+
+ CLASS_NAME: "OpenLayers.Layer.MapServer.Untiled"
+});
+/* ======================================================================
+ OpenLayers/Layer/Vector.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/Layer.js
+ * @requires OpenLayers/Renderer.js
+ * @requires OpenLayers/StyleMap.js
+ * @requires OpenLayers/Feature/Vector.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.Vector
+ * Instances of OpenLayers.Layer.Vector are used to render vector data from
+ * a variety of sources. Create a new vector layer with the
+ * <OpenLayers.Layer.Vector> constructor.
+ *
+ * Inherits from:
+ * - <OpenLayers.Layer>
+ */
+OpenLayers.Layer.Vector = OpenLayers.Class(OpenLayers.Layer, {
+
+ /**
+ * Constant: EVENT_TYPES
+ * {Array(String)} Supported application event types. Register a listener
+ * for a particular event with the following syntax:
+ * (code)
+ * layer.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.
+ *
+ * All event objects have at least the following properties:
+ * - *object* {Object} A reference to layer.events.object.
+ * - *element* {DOMElement} A reference to layer.events.element.
+ *
+ * Supported map event types (in addition to those from <OpenLayers.Layer>):
+ * - *beforefeatureadded* Triggered before a feature is added. Listeners
+ * will receive an object with a *feature* property referencing the
+ * feature to be added.
+ * - *featureadded* Triggered after a feature is added. The event
+ * object passed to listeners will have a *feature* property with a
+ * reference to the added feature.
+ * - *featuresadded* Triggered after features are added. The event
+ * object passed to listeners will have a *features* property with a
+ * reference to an array of added features.
+ * - *beforefeatureremoved* Triggered before a feature is removed. Listeners
+ * will receive an object with a *feature* property referencing the
+ * feature to be removed.
+ * - *featureremoved* Triggerd after a feature is removed. The event
+ * object passed to listeners will have a *feature* property with a
+ * reference to the removed feature.
+ * - *featuresremoved* Triggered after features are removed. The event
+ * object passed to listeners will have a *features* property with a
+ * reference to an array of removed features.
+ * - *featureselected* Triggered after a feature is selected. Listeners
+ * will receive an object with a *feature* property referencing the
+ * selected feature.
+ * - *featureunselected* Triggered after a feature is unselected.
+ * Listeners will receive an object with a *feature* property
+ * referencing the unselected feature.
+ * - *beforefeaturemodified* Triggered when a feature is selected to
+ * be modified. Listeners will receive an object with a *feature*
+ * property referencing the selected feature.
+ * - *featuremodified* Triggered when a feature has been modified.
+ * Listeners will receive an object with a *feature* property referencing
+ * the modified feature.
+ * - *afterfeaturemodified* Triggered when a feature is finished being modified.
+ * Listeners will receive an object with a *feature* property referencing
+ * the modified feature.
+ */
+ EVENT_TYPES: ["beforefeatureadded", "featureadded", "featuresadded",
+ "beforefeatureremoved", "featureremoved", "featuresremoved",
+ "beforefeatureselected", "featureselected", "featureunselected",
+ "beforefeaturemodified", "featuremodified", "afterfeaturemodified"],
+
+ /**
+ * APIProperty: isBaseLayer
+ * {Boolean} The layer is a base layer. Default is true. Set this property
+ * in the layer options
+ */
+ isBaseLayer: false,
+
+ /**
+ * APIProperty: isFixed
+ * {Boolean} Whether the layer remains in one place while dragging the
+ * map.
+ */
+ isFixed: false,
+
+ /**
+ * APIProperty: isVector
+ * {Boolean} Whether the layer is a vector layer.
+ */
+ isVector: true,
+
+ /**
+ * APIProperty: features
+ * {Array(<OpenLayers.Feature.Vector>)}
+ */
+ features: null,
+
+ /**
+ * Property: selectedFeatures
+ * {Array(<OpenLayers.Feature.Vector>)}
+ */
+ selectedFeatures: null,
+
+ /**
+ * Property: unrenderedFeatures
+ * {Object} hash of features, keyed by feature.id, that the renderer
+ * failed to draw
+ */
+ unrenderedFeatures: null,
+
+ /**
+ * APIProperty: reportError
+ * {Boolean} report friendly error message when loading of renderer
+ * fails.
+ */
+ reportError: true,
+
+ /**
+ * APIProperty: style
+ * {Object} Default style for the layer
+ */
+ style: null,
+
+ /**
+ * Property: styleMap
+ * {<OpenLayers.StyleMap>}
+ */
+ styleMap: null,
+
+ /**
+ * Property: strategies
+ * {Array(<OpenLayers.Strategy>})} Optional list of strategies for the layer.
+ */
+ strategies: null,
+
+ /**
+ * Property: protocol
+ * {<OpenLayers.Protocol>} Optional protocol for the layer.
+ */
+ protocol: null,
+
+ /**
+ * Property: renderers
+ * {Array(String)} List of supported Renderer classes. Add to this list to
+ * add support for additional renderers. This list is ordered:
+ * the first renderer which returns true for the 'supported()'
+ * method will be used, if not defined in the 'renderer' option.
+ */
+ renderers: ['SVG', 'VML', 'Canvas'],
+
+ /**
+ * Property: renderer
+ * {<OpenLayers.Renderer>}
+ */
+ renderer: null,
+
+ /**
+ * APIProperty: rendererOptions
+ * {Object} Options for the renderer. See {<OpenLayers.Renderer>} for
+ * supported options.
+ */
+ rendererOptions: null,
+
+ /**
+ * APIProperty: geometryType
+ * {String} geometryType allows you to limit the types of geometries this
+ * layer supports. This should be set to something like
+ * "OpenLayers.Geometry.Point" to limit types.
+ */
+ geometryType: null,
+
+ /**
+ * Property: drawn
+ * {Boolean} Whether the Vector Layer features have been drawn yet.
+ */
+ drawn: false,
+
+ /**
+ * Constructor: OpenLayers.Layer.Vector
+ * Create a new vector layer
+ *
+ * Parameters:
+ * name - {String} A name for the layer
+ * options - {Object} Optional object with non-default properties to set on
+ * the layer.
+ *
+ * Returns:
+ * {<OpenLayers.Layer.Vector>} A new vector layer
+ */
+ initialize: function(name, options) {
+
+ // concatenate events specific to vector with those from the base
+ this.EVENT_TYPES =
+ OpenLayers.Layer.Vector.prototype.EVENT_TYPES.concat(
+ OpenLayers.Layer.prototype.EVENT_TYPES
+ );
+
+ OpenLayers.Layer.prototype.initialize.apply(this, arguments);
+
+ // allow user-set renderer, otherwise assign one
+ if (!this.renderer || !this.renderer.supported()) {
+ this.assignRenderer();
+ }
+
+ // if no valid renderer found, display error
+ if (!this.renderer || !this.renderer.supported()) {
+ this.renderer = null;
+ this.displayError();
+ }
+
+ if (!this.styleMap) {
+ this.styleMap = new OpenLayers.StyleMap();
+ }
+
+ this.features = [];
+ this.selectedFeatures = [];
+ this.unrenderedFeatures = {};
+
+ // Allow for custom layer behavior
+ if(this.strategies){
+ for(var i=0, len=this.strategies.length; i<len; i++) {
+ this.strategies[i].setLayer(this);
+ }
+ }
+
+ },
+
+ /**
+ * APIMethod: destroy
+ * Destroy this layer
+ */
+ destroy: function() {
+ if (this.strategies) {
+ var strategy, i, len;
+ for(i=0, len=this.strategies.length; i<len; i++) {
+ strategy = this.strategies[i];
+ if(strategy.autoDestroy) {
+ strategy.destroy();
+ }
+ }
+ this.strategies = null;
+ }
+ if (this.protocol) {
+ if(this.protocol.autoDestroy) {
+ this.protocol.destroy();
+ }
+ this.protocol = null;
+ }
+ this.destroyFeatures();
+ this.features = null;
+ this.selectedFeatures = null;
+ this.unrenderedFeatures = null;
+ if (this.renderer) {
+ this.renderer.destroy();
+ }
+ this.renderer = null;
+ this.geometryType = null;
+ this.drawn = null;
+ OpenLayers.Layer.prototype.destroy.apply(this, arguments);
+ },
+
+ /**
+ * Method: assignRenderer
+ * Iterates through the available renderer implementations and selects
+ * and assigns the first one whose "supported()" function returns true.
+ */
+ assignRenderer: function() {
+ for (var i=0, len=this.renderers.length; i<this.renderers.length; i++) {
+ var rendererClass = OpenLayers.Renderer[this.renderers[i]];
+ if (rendererClass && rendererClass.prototype.supported()) {
+ this.renderer = new rendererClass(this.div,
+ this.rendererOptions);
+ break;
+ }
+ }
+ },
+
+ /**
+ * Method: displayError
+ * Let the user know their browser isn't supported.
+ */
+ displayError: function() {
+ if (this.reportError) {
+ OpenLayers.Console.userError(OpenLayers.i18n("browserNotSupported",
+ {'renderers':this.renderers.join("\n")}));
+ }
+ },
+
+ /**
+ * Method: setMap
+ * The layer has been added to the map.
+ *
+ * If there is no renderer set, the layer can't be used. Remove it.
+ * Otherwise, give the renderer a reference to the map and set its size.
+ *
+ * Parameters:
+ * map - {<OpenLayers.Map>}
+ */
+ setMap: function(map) {
+ OpenLayers.Layer.prototype.setMap.apply(this, arguments);
+
+ if (!this.renderer) {
+ this.map.removeLayer(this);
+ } else {
+ this.renderer.map = this.map;
+ this.renderer.setSize(this.map.getSize());
+ }
+ if(this.strategies) {
+ var strategy, i, len;
+ for(i=0, len=this.strategies.length; i<len; i++) {
+ strategy = this.strategies[i];
+ if(strategy.autoActivate) {
+ strategy.activate();
+ }
+ }
+ }
+ },
+
+ /**
+ * Method: removeMap
+ * The layer has been removed from the map.
+ *
+ * Parameters:
+ * map - {<OpenLayers.Map>}
+ */
+ removeMap: function(map) {
+ if(this.strategies) {
+ var strategy, i, len;
+ for(i=0, len=this.strategies.length; i<len; i++) {
+ strategy = this.strategies[i];
+ if(strategy.autoActivate) {
+ strategy.deactivate();
+ }
+ }
+ }
+ },
+
+ /**
+ * Method: onMapResize
+ * Notify the renderer of the change in size.
+ *
+ */
+ onMapResize: function() {
+ OpenLayers.Layer.prototype.onMapResize.apply(this, arguments);
+ this.renderer.setSize(this.map.getSize());
+ },
+
+ /**
+ * Method: moveTo
+ * Reset the vector layer's div so that it once again is lined up with
+ * the map. Notify the renderer of the change of extent, and in the
+ * case of a change of zoom level (resolution), have the
+ * renderer redraw features.
+ *
+ * If the layer has not yet been drawn, cycle through the layer's
+ * features and draw each one.
+ *
+ * Parameters:
+ * bounds - {<OpenLayers.Bounds>}
+ * zoomChanged - {Boolean}
+ * dragging - {Boolean}
+ */
+ moveTo: function(bounds, zoomChanged, dragging) {
+ OpenLayers.Layer.prototype.moveTo.apply(this, arguments);
+
+ var coordSysUnchanged = true;
+
+ if (!dragging) {
+ this.renderer.root.style.visibility = "hidden";
+
+ this.div.style.left = -parseInt(this.map.layerContainerDiv.style.left) + "px";
+ this.div.style.top = -parseInt(this.map.layerContainerDiv.style.top) + "px";
+ var extent = this.map.getExtent();
+ coordSysUnchanged = this.renderer.setExtent(extent, zoomChanged);
+
+ this.renderer.root.style.visibility = "visible";
+
+ // Force a reflow on gecko based browsers to prevent jump/flicker.
+ // This seems to happen on only certain configurations; it was originally
+ // noticed in FF 2.0 and Linux.
+ if (navigator.userAgent.toLowerCase().indexOf("gecko") != -1) {
+ this.div.scrollLeft = this.div.scrollLeft;
+ }
+
+ if(!zoomChanged && coordSysUnchanged) {
+ var unrenderedFeatures = {};
+ for(var i in this.unrenderedFeatures) {
+ var feature = this.unrenderedFeatures[i];
+ if(!this.drawFeature(feature)) {
+ unrenderedFeatures[i] = feature;
+ }
+ }
+ this.unrenderedFeatures = unrenderedFeatures;
+ }
+ }
+
+ if (!this.drawn || zoomChanged || !coordSysUnchanged) {
+ this.unrenderedFeatures = {};
+ 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;
+ }
+ feature = this.features[i];
+ if (!this.drawFeature(feature)) {
+ this.unrenderedFeatures[feature.id] = feature;
+ };
+ }
+ }
+ },
+
+ /**
+ * APIMethod: addFeatures
+ * Add Features to the layer.
+ *
+ * Parameters:
+ * features - {Array(<OpenLayers.Feature.Vector>)}
+ * options - {Object}
+ */
+ addFeatures: function(features, options) {
+ if (!(features instanceof Array)) {
+ features = [features];
+ }
+
+ var notify = !options || !options.silent;
+
+ for (var i=0, len=features.length; i<len; i++) {
+ if (i != (features.length - 1)) {
+ this.renderer.locked = true;
+ } else {
+ this.renderer.locked = false;
+ }
+ var feature = features[i];
+
+ if (this.geometryType &&
+ !(feature.geometry instanceof this.geometryType)) {
+ var throwStr = OpenLayers.i18n('componentShouldBe',
+ {'geomType':this.geometryType.prototype.CLASS_NAME});
+ throw throwStr;
+ }
+
+ this.features.push(feature);
+
+ //give feature reference to its layer
+ feature.layer = this;
+
+ if (!feature.style && this.style) {
+ feature.style = OpenLayers.Util.extend({}, this.style);
+ }
+
+ if (notify) {
+ this.events.triggerEvent("beforefeatureadded", {
+ feature: feature
+ });
+ this.preFeatureInsert(feature);
+ }
+
+ if (this.drawn) {
+ if(!this.drawFeature(feature)) {
+ this.unrenderedFeatures[feature.id] = feature;
+ }
+ }
+
+ if (notify) {
+ this.events.triggerEvent("featureadded", {
+ feature: feature
+ });
+ this.onFeatureInsert(feature);
+ }
+ }
+
+ if(notify) {
+ this.events.triggerEvent("featuresadded", {features: features});
+ }
+ },
+
+
+ /**
+ * APIMethod: removeFeatures
+ *
+ * Parameters:
+ * features - {Array(<OpenLayers.Feature.Vector>)}
+ * options - {Object}
+ */
+ removeFeatures: function(features, options) {
+ if (!(features instanceof Array)) {
+ features = [features];
+ }
+ if (features.length <= 0) {
+ return;
+ }
+
+ var notify = !options || !options.silent;
+
+ for (var i = features.length - 1; i >= 0; i--) {
+ // We remain locked so long as we're not at 0
+ // and the 'next' feature has a geometry. We do the geometry check
+ // because if all the features after the current one are 'null', we
+ // won't call eraseGeometry, so we break the 'renderer functions
+ // will always be called with locked=false *last*' rule. The end result
+ // is a possible gratiutious unlocking to save a loop through the rest
+ // of the list checking the remaining features every time. So long as
+ // null geoms are rare, this is probably okay.
+ if (i != 0 && features[i-1].geometry) {
+ this.renderer.locked = true;
+ } else {
+ this.renderer.locked = false;
+ }
+
+ var feature = features[i];
+ delete this.unrenderedFeatures[feature.id];
+
+ if (notify) {
+ this.events.triggerEvent("beforefeatureremoved", {
+ feature: feature
+ });
+ }
+
+ this.features = OpenLayers.Util.removeItem(this.features, feature);
+ // feature has no layer at this point
+ feature.layer = null;
+
+ if (feature.geometry) {
+ this.renderer.eraseGeometry(feature.geometry);
+ }
+
+ //in the case that this feature is one of the selected features,
+ // remove it from that array as well.
+ if (OpenLayers.Util.indexOf(this.selectedFeatures, feature) != -1){
+ OpenLayers.Util.removeItem(this.selectedFeatures, feature);
+ }
+
+ if (notify) {
+ this.events.triggerEvent("featureremoved", {
+ feature: feature
+ });
+ }
+ }
+
+ if (notify) {
+ this.events.triggerEvent("featuresremoved", {features: features});
+ }
+ },
+
+ /**
+ * APIMethod: destroyFeatures
+ * Erase and destroy features on the layer.
+ *
+ * Parameters:
+ * features - {Array(<OpenLayers.Feature.Vector>)} An optional array of
+ * features to destroy. If not supplied, all features on the layer
+ * will be destroyed.
+ * options - {Object}
+ */
+ destroyFeatures: function(features, options) {
+ var all = (features == undefined); // evaluates to true if
+ // features is null
+ if(all) {
+ features = this.features;
+ }
+ this.removeFeatures(features, options);
+ for (var i = 0; i < features.length; i++) {
+ features[i].destroy();
+ }
+ },
+
+ /**
+ * APIMethod: drawFeature
+ * Draw (or redraw) a feature on the layer. If the optional style argument
+ * is included, this style will be used. If no style is included, the
+ * feature's style will be used. If the feature doesn't have a style,
+ * the layer's style will be used.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>}
+ * style - {Object} Symbolizer hash or {String} renderIntent
+ *
+ * Returns:
+ * {Boolean} true if the renderer was able to draw the feature, false
+ * otherwise
+ */
+ drawFeature: function(feature, style) {
+ if (typeof style != "object") {
+ var renderIntent = typeof style == "string" ?
+ style : feature.renderIntent;
+ style = feature.style || this.style;
+ if (!style) {
+ style = this.styleMap.createSymbolizer(feature, renderIntent);
+ }
+ }
+
+ return this.renderer.drawFeature(feature, style);
+ },
+
+ /**
+ * Method: eraseFeatures
+ * Erase features from the layer.
+ *
+ * Parameters:
+ * features - {Array(<OpenLayers.Feature.Vector>)}
+ */
+ eraseFeatures: function(features) {
+ this.renderer.eraseFeatures(features);
+ },
+
+ /**
+ * Method: getFeatureFromEvent
+ * Given an event, return a feature if the event occurred over one.
+ * Otherwise, return null.
+ *
+ * Parameters:
+ * evt - {Event}
+ *
+ * Returns:
+ * {<OpenLayers.Feature.Vector>} A feature if one was under the event.
+ */
+ getFeatureFromEvent: function(evt) {
+ if (!this.renderer) {
+ OpenLayers.Console.error(OpenLayers.i18n("getFeatureError"));
+ return null;
+ }
+ var featureId = this.renderer.getFeatureIdFromEvent(evt);
+ return this.getFeatureById(featureId);
+ },
+
+ /**
+ * APIMethod: getFeatureById
+ * Given a feature id, return the feature if it exists in the features array
+ *
+ * Parameters:
+ * featureId - {String}
+ *
+ * Returns:
+ * {<OpenLayers.Feature.Vector>} A feature corresponding to the given
+ * featureId
+ */
+ getFeatureById: function(featureId) {
+ //TBD - would it be more efficient to use a hash for this.features?
+ var feature = null;
+ for(var i=0, len=this.features.length; i<len; ++i) {
+ if(this.features[i].id == featureId) {
+ feature = this.features[i];
+ break;
+ }
+ }
+ return feature;
+ },
+
+ /**
+ * Unselect the selected features
+ * i.e. clears the featureSelection array
+ * change the style back
+ clearSelection: function() {
+
+ var vectorLayer = this.map.vectorLayer;
+ for (var i = 0; i < this.map.featureSelection.length; i++) {
+ var featureSelection = this.map.featureSelection[i];
+ vectorLayer.drawFeature(featureSelection, vectorLayer.style);
+ }
+ this.map.featureSelection = [];
+ },
+ */
+
+
+ /**
+ * APIMethod: onFeatureInsert
+ * method called after a feature is inserted.
+ * Does nothing by default. Override this if you
+ * need to do something on feature updates.
+ *
+ * Paarameters:
+ * feature - {<OpenLayers.Feature.Vector>}
+ */
+ onFeatureInsert: function(feature) {
+ },
+
+ /**
+ * APIMethod: preFeatureInsert
+ * method called before a feature is inserted.
+ * Does nothing by default. Override this if you
+ * need to do something when features are first added to the
+ * layer, but before they are drawn, such as adjust the style.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>}
+ */
+ preFeatureInsert: function(feature) {
+ },
+
+ /**
+ * APIMethod: getDataExtent
+ * Calculates the max extent which includes all of the features.
+ *
+ * Returns:
+ * {<OpenLayers.Bounds>}
+ */
+ 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++){
+ maxExtent.extend(this.features[i].geometry.getBounds());
+ }
+ }
+
+ return maxExtent;
+ },
+
+ CLASS_NAME: "OpenLayers.Layer.Vector"
+});
+/* ======================================================================
+ OpenLayers/Layer/WMS/Untiled.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/Layer/WMS.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.WMS.Untiled
+ * *Deprecated*. To be removed in 3.0. Instead use OpenLayers.Layer.WMS and
+ * pass the option 'singleTile' as true.
+ *
+ * Inherits from:
+ * - <OpenLayers.Layer.WMS>
+ */
+OpenLayers.Layer.WMS.Untiled = OpenLayers.Class(OpenLayers.Layer.WMS, {
+
+ /**
+ * APIProperty: singleTile
+ * {singleTile} Always true for untiled.
+ */
+ singleTile: true,
+
+ /**
+ * Constructor: OpenLayers.Layer.WMS.Untiled
+ *
+ * Parameters:
+ * name - {String}
+ * url - {String}
+ * params - {Object}
+ * options - {Object}
+ */
+ initialize: function(name, url, params, options) {
+ OpenLayers.Layer.WMS.prototype.initialize.apply(this, arguments);
+
+ var msg = "The OpenLayers.Layer.WMS.Untiled class is deprecated and " +
+ "will be removed in 3.0. Instead, you should use the " +
+ "normal OpenLayers.Layer.WMS class, passing it the option " +
+ "'singleTile' as true.";
+ OpenLayers.Console.warn(msg);
+ },
+
+ /**
+ * Method: clone
+ * Create a clone of this layer
+ *
+ * Returns:
+ * {<OpenLayers.Layer.WMS.Untiled>} An exact clone of this layer
+ */
+ clone: function (obj) {
+
+ if (obj == null) {
+ obj = new OpenLayers.Layer.WMS.Untiled(this.name,
+ this.url,
+ this.params,
+ this.options);
+ }
+
+ //get all additions from superclasses
+ obj = OpenLayers.Layer.WMS.prototype.clone.apply(this, [obj]);
+
+ // copy/set any non-init, non-simple values here
+
+ return obj;
+ },
+
+ CLASS_NAME: "OpenLayers.Layer.WMS.Untiled"
+});
+/* ======================================================================
+ OpenLayers/Format/Filter.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/Format/XML.js
+ * @requires OpenLayers/Filter/FeatureId.js
+ * @requires OpenLayers/Filter/Logical.js
+ * @requires OpenLayers/Filter/Comparison.js
+ */
+
+/**
+ * Class: OpenLayers.Format.Filter
+ * Read/Wite ogc:Filter. Create a new instance with the <OpenLayers.Format.Filter>
+ * constructor.
+ *
+ * Inherits from:
+ * - <OpenLayers.Format.XML>
+ */
+OpenLayers.Format.Filter = OpenLayers.Class(OpenLayers.Format.XML, {
+
+ /**
+ * APIProperty: defaultVersion
+ * {String} Version number to assume if none found. Default is "1.0.0".
+ */
+ defaultVersion: "1.0.0",
+
+ /**
+ * APIProperty: version
+ * {String} Specify a version string if one is known.
+ */
+ version: null,
+
+ /**
+ * Property: parser
+ * {Object} Instance of the versioned parser. Cached for multiple read and
+ * write calls of the same version.
+ */
+ parser: null,
+
+ /**
+ * Constructor: OpenLayers.Format.Filter
+ * Create a new parser for Filter.
+ *
+ * Parameters:
+ * options - {Object} An optional object whose properties will be set on
+ * this instance.
+ */
+ initialize: function(options) {
+ OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
+ },
+
+ /**
+ * APIMethod: write
+ * Write an ogc:Filter given a filter object.
+ *
+ * Parameters:
+ * filter - {<OpenLayers.Filter>} An filter.
+ * options - {Object} Optional configuration object.
+ *
+ * Returns:
+ * {Elment} An ogc:Filter element node.
+ */
+ write: function(filter, options) {
+ var version = (options && options.version) ||
+ this.version || this.defaultVersion;
+ if(!this.parser || this.parser.VERSION != version) {
+ var format = OpenLayers.Format.Filter[
+ "v" + version.replace(/\./g, "_")
+ ];
+ if(!format) {
+ throw "Can't find a Filter parser for version " +
+ version;
+ }
+ this.parser = new format(this.options);
+ }
+ return this.parser.write(filter);
+ //return OpenLayers.Format.XML.prototype.write.apply(this, [root]);
+ },
+
+ /**
+ * APIMethod: read
+ * Read and Filter doc and return an object representing the Filter.
+ *
+ * Parameters:
+ * data - {String | DOMElement} Data to read.
+ *
+ * Returns:
+ * {<OpenLayers.Filter>} A filter object.
+ */
+ read: function(data) {
+ if(typeof data == "string") {
+ data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
+ }
+ var root = data.nodeType == 9 ? data.documentElement : data;
+ var version = this.version;
+ if(!version) {
+ version = this.defaultVersion;
+ }
+ if(!this.parser || this.parser.VERSION != version) {
+ var format = OpenLayers.Format.Filter[
+ "v" + version.replace(/\./g, "_")
+ ];
+ if(!format) {
+ throw "Can't find a Filter parser for version " +
+ version;
+ }
+ this.parser = new format(this.options);
+ }
+ var filter = this.parser.read(data);
+ return filter;
+ },
+
+ CLASS_NAME: "OpenLayers.Format.Filter"
+});
+/* ======================================================================
+ OpenLayers/Format/SLD.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/Format/XML.js
+ * @requires OpenLayers/Style.js
+ * @requires OpenLayers/Rule.js
+ * @requires OpenLayers/Filter/FeatureId.js
+ * @requires OpenLayers/Filter/Logical.js
+ * @requires OpenLayers/Filter/Comparison.js
+ */
+
+/**
+ * Class: OpenLayers.Format.SLD
+ * Read/Wite SLD. Create a new instance with the <OpenLayers.Format.SLD>
+ * constructor.
+ *
+ * Inherits from:
+ * - <OpenLayers.Format.XML>
+ */
+OpenLayers.Format.SLD = OpenLayers.Class(OpenLayers.Format.XML, {
+
+ /**
+ * APIProperty: defaultVersion
+ * {String} Version number to assume if none found. Default is "1.0.0".
+ */
+ defaultVersion: "1.0.0",
+
+ /**
+ * APIProperty: version
+ * {String} Specify a version string if one is known.
+ */
+ version: null,
+
+ /**
+ * Property: parser
+ * {Object} Instance of the versioned parser. Cached for multiple read and
+ * write calls of the same version.
+ */
+ parser: null,
+
+ /**
+ * Constructor: OpenLayers.Format.SLD
+ * Create a new parser for SLD.
+ *
+ * Parameters:
+ * options - {Object} An optional object whose properties will be set on
+ * this instance.
+ */
+ initialize: function(options) {
+ OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
+ },
+
+ /**
+ * APIMethod: write
+ * Write a SLD document given a list of styles.
+ *
+ * Parameters:
+ * sld - {Object} An object representing the SLD.
+ * options - {Object} Optional configuration object.
+ *
+ * Returns:
+ * {String} An SLD document string.
+ */
+ write: function(sld, options) {
+ var version = (options && options.version) ||
+ this.version || this.defaultVersion;
+ if(!this.parser || this.parser.VERSION != version) {
+ var format = OpenLayers.Format.SLD[
+ "v" + version.replace(/\./g, "_")
+ ];
+ if(!format) {
+ throw "Can't find a SLD parser for version " +
+ version;
+ }
+ this.parser = new format(this.options);
+ }
+ var root = this.parser.write(sld);
+ return OpenLayers.Format.XML.prototype.write.apply(this, [root]);
+ },
+
+ /**
+ * APIMethod: read
+ * Read and SLD doc and return an object representing the SLD.
+ *
+ * Parameters:
+ * data - {String | DOMElement} Data to read.
+ *
+ * Returns:
+ * {Object} An object representing the SLD.
+ */
+ read: function(data) {
+ if(typeof data == "string") {
+ data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
+ }
+ var root = data.documentElement;
+ var version = this.version;
+ if(!version) {
+ version = root.getAttribute("version");
+ if(!version) {
+ version = this.defaultVersion;
+ }
+ }
+ if(!this.parser || this.parser.VERSION != version) {
+ var format = OpenLayers.Format.SLD[
+ "v" + version.replace(/\./g, "_")
+ ];
+ if(!format) {
+ throw "Can't find a SLD parser for version " +
+ version;
+ }
+ this.parser = new format(this.options);
+ }
+ var sld = this.parser.read(data);
+ return sld;
+ },
+
+ CLASS_NAME: "OpenLayers.Format.SLD"
+});
+/* ======================================================================
+ OpenLayers/Format/Text.js
+ ====================================================================== */
+
+/* Copyright (c) 2006-2008 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/Feature/Vector.js
+ * @requires OpenLayers/Geometry/Point.js
+ */
+
+/**
+ * Class: OpenLayers.Format.Text
+ * Read Text format. Create a new instance with the <OpenLayers.Format.Text>
+ * constructor. This reads text which is formatted like CSV text, using
+ * tabs as the seperator by default. It provides parsing of data originally
+ * used in the MapViewerService, described on the wiki. This Format is used
+ * by the <OpenLayers.Layer.Text> class.
+ *
+ * Inherits from:
+ * - <OpenLayers.Format>
+ */
+OpenLayers.Format.Text = OpenLayers.Class(OpenLayers.Format, {
+
+ /**
+ * Constructor: OpenLayers.Format.Text
+ * Create a new parser for TSV Text.
+ *
+ * Parameters:
+ * options - {Object} An optional object whose properties will be set on
+ * this instance.
+ */
+ initialize: function(options) {
+ OpenLayers.Format.prototype.initialize.apply(this, [options]);
+ },
+
+ /**
+ * APIMethod: read
+ * Return a list of features from a Tab Seperated Values text string.
+ *
+ * Parameters:
+ * data - {String}
+ *
+ * Returns:
+ * An Array of <OpenLayers.Feature.Vector>s
+ */
+ read: function(text) {
+ var lines = text.split('\n');
+ var columns;
+ var features = [];
+ // length - 1 to allow for trailing new line
+ for (var lcv = 0; lcv < (lines.length - 1); lcv++) {
+ var currLine = lines[lcv].replace(/^\s*/,'').replace(/\s*$/,'');
+
+ if (currLine.charAt(0) != '#') { /* not a comment */
+
+ if (!columns) {
+ //First line is columns
+ columns = currLine.split('\t');
+ } else {
+ var vals = currLine.split('\t');
+ var geometry = new OpenLayers.Geometry.Point(0,0);
+ var attributes = {};
+ var style = {};
+ var icon, iconSize, iconOffset, overflow;
+ var set = false;
+ for (var valIndex = 0; valIndex < vals.length; valIndex++) {
+ if (vals[valIndex]) {
+ if (columns[valIndex] == 'point') {
+ var coords = vals[valIndex].split(',');
+ geometry.y = parseFloat(coords[0]);
+ geometry.x = parseFloat(coords[1]);
+ set = true;
+ } else if (columns[valIndex] == 'lat') {
+ geometry.y = parseFloat(vals[valIndex]);
+ set = true;
+ } else if (columns[valIndex] == 'lon') {
+ geometry.x = parseFloat(vals[valIndex]);
+ set = true;
+ } else if (columns[valIndex] == 'title')
+ attributes['title'] = vals[valIndex];
+ else if (columns[valIndex] == 'image' ||
+ columns[valIndex] == 'icon')
+ style['externalGraphic'] = vals[valIndex];
+ else if (columns[valIndex] == 'iconSize') {
+ var size = vals[valIndex].split(',');
+ style['graphicWidth'] = parseFloat(size[0]);
+ style['graphicHeight'] = parseFloat(size[1]);
+ } else if (columns[valIndex] == 'iconOffset') {
+ var offset = vals[valIndex].split(',');
+ style['graphicXOffset'] = parseFloat(offset[0]);
+ style['graphicYOffset'] = parseFloat(offset[1]);
+ } else if (columns[valIndex] == 'description') {
+ attributes['description'] = vals[valIndex];
+ } else if (columns[valIndex] == 'overflow') {
+ attributes['overflow'] = vals[valIndex];
+ }
+ }
+ }
+ if (set) {
+ if (this.internalProjection && this.externalProjection) {
+ geometry.transform(this.externalProjection,
+ this.internalProjection);
+ }
+ var feature = new OpenLayers.Feature.Vector(geometry, attributes, style);
+ features.push(feature);
+ }
+ }
+ }
+ }
+ return features;
+ },
+
+ CLASS_NAME: "OpenLayers.Format.Text"
+});
+/* ======================================================================
+ OpenLayers/Geometry/MultiLineString.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.MultiLineString
+ * A MultiLineString is a geometry with multiple <OpenLayers.Geometry.LineString>
+ * components.
+ *
+ * Inherits from:
+ * - <OpenLayers.Geometry.Collection>
+ * - <OpenLayers.Geometry>
+ */
+OpenLayers.Geometry.MultiLineString = 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.LineString"],
+
+ /**
+ * Constructor: OpenLayers.Geometry.MultiLineString
+ * Constructor for a MultiLineString Geometry.
+ *
+ * Parameters:
+ * components - {Array(<OpenLayers.Geometry.LineString>)}
+ *
+ */
+ initialize: function(components) {
+ OpenLayers.Geometry.Collection.prototype.initialize.apply(this,
+ arguments);
+ },
+
+ CLASS_NAME: "OpenLayers.Geometry.MultiLineString"
+});
+/* ======================================================================
+ OpenLayers/Geometry/MultiPoint.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.MultiPoint
+ * MultiPoint is a collection of Points. Create a new instance with the
+ * <OpenLayers.Geometry.MultiPoint> constructor.
+ *
+ * Inherits from:
+ * - <OpenLayers.Geometry.Collection>
+ * - <OpenLayers.Geometry>
+ */
+OpenLayers.Geometry.MultiPoint = 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.Point"],
+
+ /**
+ * Constructor: OpenLayers.Geometry.MultiPoint
+ * Create a new MultiPoint Geometry
+ *
+ * Parameters:
+ * components - {Array(<OpenLayers.Geometry.Point>)}
+ *
+ * Returns:
+ * {<OpenLayers.Geometry.MultiPoint>}
+ */
+ initialize: function(components) {
+ OpenLayers.Geometry.Collection.prototype.initialize.apply(this,
+ arguments);
+ },
+
+ /**
+ * APIMethod: addPoint
+ * Wrapper for <OpenLayers.Geometry.Collection.addComponent>
+ *
+ * Parameters:
+ * point - {<OpenLayers.Geometry.Point>} Point to be added
+ * index - {Integer} Optional index
+ */
+ addPoint: function(point, index) {
+ this.addComponent(point, index);
+ },
+
+ /**
+ * APIMethod: removePoint
+ * Wrapper for <OpenLayers.Geometry.Collection.removeComponent>
+ *
+ * Parameters:
+ * point - {<OpenLayers.Geometry.Point>} Point to be removed
+ */
+ removePoint: function(point){
+ this.removeComponent(point);
+ },
+
+ CLASS_NAME: "OpenLayers.Geometry.MultiPoint"
+});
+/* ======================================================================
+ OpenLayers/Geometry/MultiPolygon.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.MultiPolygon
+ * MultiPolygon is a geometry with multiple <OpenLayers.Geometry.Polygon>
+ * components. Create a new instance with the <OpenLayers.Geometry.MultiPolygon>
+ * constructor.
+ *
+ * Inherits from:
+ * - <OpenLayers.Geometry.Collection>
+ */
+OpenLayers.Geometry.MultiPolygon = 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.Polygon"],
+
+ /**
+ * Constructor: OpenLayers.Geometry.MultiPolygon
+ * Create a new MultiPolygon geometry
+ *
+ * Parameters:
+ * components - {Array(<OpenLayers.Geometry.Polygon>)} An array of polygons
+ * used to generate the MultiPolygon
+ *
+ */
+ initialize: function(components) {
+ OpenLayers.Geometry.Collection.prototype.initialize.apply(this,
+ arguments);
+ },
+
+ CLASS_NAME: "OpenLayers.Geometry.MultiPolygon"
+});
+/* ======================================================================
+ 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
+ ====================================================================== */
+
+/* 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
+ * @requires OpenLayers/Geometry/Point.js
+ */
+
+/**
+ * Class: OpenLayers.Handler.Point
+ * Handler to draw a point on the map. Point is displayed on mouse down,
+ * moves 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.Point> constructor.
+ *
+ * Inherits from:
+ * - <OpenLayers.Handler>
+ */
+OpenLayers.Handler.Point = OpenLayers.Class(OpenLayers.Handler, {
+
+ /**
+ * Property: point
+ * {<OpenLayers.Feature.Vector>} The currently drawn point
+ */
+ point: null,
+
+ /**
+ * Property: layer
+ * {<OpenLayers.Layer.Vector>} The temporary drawing layer
+ */
+ layer: null,
+
+ /**
+ * Property: drawing
+ * {Boolean} A point is being drawn
+ */
+ drawing: false,
+
+ /**
+ * Property: mouseDown
+ * {Boolean} The mouse is down
+ */
+ mouseDown: false,
+
+ /**
+ * Property: lastDown
+ * {<OpenLayers.Pixel>} Location of the last mouse down
+ */
+ lastDown: null,
+
+ /**
+ * Property: lastUp
+ * {<OpenLayers.Pixel>}
+ */
+ lastUp: null,
+
+ /**
+ * Constructor: OpenLayers.Handler.Point
+ * Create a new point handler.
+ *
+ * Parameters:
+ * control - {<OpenLayers.Control>} The control that owns this handler
+ * callbacks - {Object} An object with a 'done' property whose value is a
+ * function to be called when the point drawing is finished.
+ * The callback should expect to recieve a single argument,
+ * the point 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 optional object with properties to be set on the
+ * handler
+ */
+ initialize: function(control, callbacks, options) {
+ // TBD: deal with style
+ this.style = OpenLayers.Util.extend(OpenLayers.Feature.Vector.style['default'], {});
+
+ OpenLayers.Handler.prototype.initialize.apply(this, arguments);
+ },
+
+ /**
+ * APIMethod: activate
+ * turn on the handler
+ */
+ activate: function() {
+ if(!OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
+ return false;
+ }
+ // create temporary vector layer for rendering geometry sketch
+ // TBD: this could be moved to initialize/destroy - setting visibility here
+ 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);
+ return true;
+ },
+
+ /**
+ * Method: createFeature
+ * Add temporary features
+ */
+ createFeature: function() {
+ this.point = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point());
+ },
+
+ /**
+ * APIMethod: deactivate
+ * turn off the handler
+ */
+ deactivate: function() {
+ if(!OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
+ return false;
+ }
+ // call the cancel callback if mid-drawing
+ if(this.drawing) {
+ 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);
+ }
+ this.layer = null;
+ return true;
+ },
+
+ /**
+ * Method: destroyFeature
+ * Destroy the temporary geometries
+ */
+ destroyFeature: function() {
+ if(this.point) {
+ this.point.destroy();
+ }
+ this.point = null;
+ },
+
+ /**
+ * Method: finalize
+ * Finish the geometry and call the "done" callback.
+ */
+ finalize: function() {
+ this.layer.renderer.clear();
+ this.drawing = false;
+ this.mouseDown = false;
+ this.lastDown = null;
+ this.lastUp = null;
+ this.callback("done", [this.geometryClone()]);
+ this.destroyFeature();
+ },
+
+ /**
+ * APIMethod: cancel
+ * Finish the geometry and call the "cancel" callback.
+ */
+ cancel: function() {
+ this.layer.renderer.clear();
+ this.drawing = false;
+ this.mouseDown = false;
+ this.lastDown = null;
+ this.lastUp = null;
+ this.callback("cancel", [this.geometryClone()]);
+ this.destroyFeature();
+ },
+
+ /**
+ * Method: click
+ * Handle clicks. Clicks are stopped from propagating to other listeners
+ * on map.events or other dom elements.
+ *
+ * Parameters:
+ * evt - {Event} The browser event
+ *
+ * Returns:
+ * {Boolean} Allow event propagation
+ */
+ click: function(evt) {
+ OpenLayers.Event.stop(evt);
+ return false;
+ },
+
+ /**
+ * Method: dblclick
+ * Handle double-clicks. Double-clicks are stopped from propagating to other
+ * listeners on map.events or other dom elements.
+ *
+ * Parameters:
+ * evt - {Event} The browser event
+ *
+ * Returns:
+ * {Boolean} Allow event propagation
+ */
+ dblclick: function(evt) {
+ OpenLayers.Event.stop(evt);
+ return false;
+ },
+
+ /**
+ * Method: drawFeature
+ * Render features on the temporary layer.
+ */
+ drawFeature: function() {
+ this.layer.drawFeature(this.point, this.style);
+ },
+
+ /**
+ * Method: geometryClone
+ * Return a clone of the relevant geometry.
+ *
+ * Returns:
+ * {<OpenLayers.Geometry.Point>}
+ */
+ geometryClone: function() {
+ return this.point.geometry.clone();
+ },
+
+ /**
+ * Method: mousedown
+ * Handle mouse down. Adjust the geometry and redraw.
+ * Return determines whether to propagate the event on the map.
+ *
+ * Parameters:
+ * evt - {Event} The browser event
+ *
+ * Returns:
+ * {Boolean} Allow event propagation
+ */
+ mousedown: function(evt) {
+ // check keyboard modifiers
+ if(!this.checkModifiers(evt)) {
+ return true;
+ }
+ // ignore double-clicks
+ if(this.lastDown && this.lastDown.equals(evt.xy)) {
+ return true;
+ }
+ if(this.lastDown == null) {
+ this.createFeature();
+ }
+ this.lastDown = evt.xy;
+ this.drawing = true;
+ var lonlat = this.map.getLonLatFromPixel(evt.xy);
+ this.point.geometry.x = lonlat.lon;
+ this.point.geometry.y = lonlat.lat;
+ this.drawFeature();
+ return false;
+ },
+
+ /**
+ * Method: mousemove
+ * Handle mouse move. Adjust the geometry and redraw.
+ * Return determines whether to propagate the event on the map.
+ *
+ * Parameters:
+ * evt - {Event} The browser event
+ *
+ * Returns:
+ * {Boolean} Allow event propagation
+ */
+ mousemove: function (evt) {
+ if(this.drawing) {
+ var lonlat = this.map.getLonLatFromPixel(evt.xy);
+ this.point.geometry.x = lonlat.lon;
+ this.point.geometry.y = lonlat.lat;
+ this.point.geometry.clearBounds();
+ this.drawFeature();
+ }
+ return true;
+ },
+
+ /**
+ * Method: mouseup
+ * Handle mouse up. Send the latest point in the geometry to the control.
+ * Return determines whether to propagate the event on the map.
+ *
+ * Parameters:
+ * evt - {Event} The browser event
+ *
+ * Returns:
+ * {Boolean} Allow event propagation
+ */
+ mouseup: function (evt) {
+ if(this.drawing) {
+ this.finalize();
+ return false;
+ } else {
+ return true;
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Handler.Point"
+});
+/* ======================================================================
+ OpenLayers/Layer/GML.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/Layer/Vector.js
+ * @requires OpenLayers/Request/XMLHttpRequest.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.GML
+ * Create a vector layer by parsing a GML file. The GML file is
+ * passed in as a parameter.
+ *
+ * Inherits from:
+ * - <OpenLayers.Layer.Vector>
+ */
+OpenLayers.Layer.GML = OpenLayers.Class(OpenLayers.Layer.Vector, {
+
+ /**
+ * Property: loaded
+ * {Boolean} Flag for whether the GML data has been loaded yet.
+ */
+ loaded: false,
+
+ /**
+ * APIProperty: format
+ * {<OpenLayers.Format>} The format you want the data to be parsed with.
+ */
+ format: null,
+
+ /**
+ * APIProperty: formatOptions
+ * {Object} Hash of options which should be passed to the format when it is
+ * created. Must be passed in the constructor.
+ */
+ formatOptions: null,
+
+ /**
+ * Constructor: OpenLayers.Layer.GML
+ * Load and parse a single file on the web, according to the format
+ * provided via the 'format' option, defaulting to GML.
+ *
+ * Parameters:
+ * name - {String}
+ * url - {String} URL of a GML file.
+ * options - {Object} Hashtable of extra options to tag onto the layer.
+ */
+ initialize: function(name, url, options) {
+ var newArguments = [];
+ newArguments.push(name, options);
+ OpenLayers.Layer.Vector.prototype.initialize.apply(this, newArguments);
+ this.url = url;
+ },
+
+ /**
+ * APIMethod: setVisibility
+ * Set the visibility flag for the layer and hide/show&redraw accordingly.
+ * Fire event unless otherwise specified
+ * GML will be loaded if the layer is being made visible for the first
+ * time.
+ *
+ * Parameters:
+ * visible - {Boolean} Whether or not to display the layer
+ * (if in range)
+ * noEvent - {Boolean}
+ */
+ setVisibility: function(visibility, noEvent) {
+ OpenLayers.Layer.Vector.prototype.setVisibility.apply(this, arguments);
+ if(this.visibility && !this.loaded){
+ // Load the GML
+ this.loadGML();
+ }
+ },
+
+ /**
+ * Method: moveTo
+ * If layer is visible and GML has not been loaded, load GML, then load GML
+ * and call OpenLayers.Layer.Vector.moveTo() to redraw at the new location.
+ *
+ * Parameters:
+ * bounds - {Object}
+ * zoomChanged - {Object}
+ * minor - {Object}
+ */
+ moveTo:function(bounds, zoomChanged, minor) {
+ OpenLayers.Layer.Vector.prototype.moveTo.apply(this, arguments);
+ // Wait until initialisation is complete before loading GML
+ // otherwise we can get a race condition where the root HTML DOM is
+ // loaded after the GML is paited.
+ // See http://trac.openlayers.org/ticket/404
+ if(this.visibility && !this.loaded){
+ this.events.triggerEvent("loadstart");
+ this.loadGML();
+ }
+ },
+
+ /**
+ * Method: loadGML
+ */
+ loadGML: function() {
+ if (!this.loaded) {
+ OpenLayers.Request.GET({
+ url: this.url,
+ success: this.requestSuccess,
+ failure: this.requestFailure,
+ scope: this
+ });
+ this.loaded = true;
+ }
+ },
+
+ /**
+ * Method: setUrl
+ * Change the URL and reload the GML
+ *
+ * Parameters:
+ * url - {String} URL of a GML file.
+ */
+ setUrl:function(url) {
+ this.url = url;
+ this.destroyFeatures();
+ this.loaded = false;
+ this.events.triggerEvent("loadstart");
+ this.loadGML();
+ },
+
+ /**
+ * Method: requestSuccess
+ * Process GML after it has been loaded.
+ * Called by initialise() and loadUrl() after the GML has been loaded.
+ *
+ * Parameters:
+ * request - {String}
+ */
+ requestSuccess:function(request) {
+ var doc = request.responseXML;
+
+ if (!doc || !doc.documentElement) {
+ doc = request.responseText;
+ }
+
+ var options = {};
+
+ OpenLayers.Util.extend(options, this.formatOptions);
+ if (this.map && !this.projection.equals(this.map.getProjectionObject())) {
+ options.externalProjection = this.projection;
+ options.internalProjection = this.map.getProjectionObject();
+ }
+
+ var gml = this.format ? new this.format(options) : new OpenLayers.Format.GML(options);
+ this.addFeatures(gml.read(doc));
+ this.events.triggerEvent("loadend");
+ },
+
+ /**
+ * Method: requestFailure
+ * Process a failed loading of GML.
+ * Called by initialise() and loadUrl() if there was a problem loading GML.
+ *
+ * Parameters:
+ * request - {String}
+ */
+ requestFailure: function(request) {
+ OpenLayers.Console.userError(OpenLayers.i18n("errorLoadingGML", {'url':this.url}));
+ this.events.triggerEvent("loadend");
+ },
+
+ CLASS_NAME: "OpenLayers.Layer.GML"
+});
+/* ======================================================================
+ OpenLayers/Layer/PointTrack.js
+ ====================================================================== */
+
+/* Copyright (c) 2006-2007 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/Layer/Vector.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.PointTrack
+ * Vector layer to display ordered point features as a line, creating one
+ * LineString feature for each pair of two points.
+ *
+ * Inherits from:
+ * - <OpenLayers.Layer.Vector>
+ */
+OpenLayers.Layer.PointTrack = OpenLayers.Class(OpenLayers.Layer.Vector, {
+
+ /**
+ * APIProperty:
+ * dataFrom - {<OpenLayers.Layer.PointTrack.dataFrom>} optional. If the
+ * lines should get the data/attributes from one of the two
+ * points, creating it, which one should it be?
+ */
+ dataFrom: null,
+
+ /**
+ * Constructor: OpenLayers.PointTrack
+ * Constructor for a new OpenLayers.PointTrack instance.
+ *
+ * Parameters:
+ * name - {String} name of the layer
+ * options - {Object} Optional object with properties to tag onto the
+ * instance.
+ */
+ initialize: function(name, options) {
+ OpenLayers.Layer.Vector.prototype.initialize.apply(this, arguments);
+ },
+
+ /**
+ * APIMethod: addNodes
+ * Adds point features that will be used to create lines from, using point
+ * pairs. The first point of a pair will be the source node, the second
+ * will be the target node.
+ *
+ * Parameters:
+ * pointFeatures - {Array(<OpenLayers.Feature>)}
+ *
+ */
+ addNodes: function(pointFeatures) {
+ if (pointFeatures.length < 2) {
+ OpenLayers.Console.error(
+ "At least two point features have to be added to create" +
+ "a line from");
+ return;
+ }
+
+ var lines = new Array(pointFeatures.length-1);
+
+ var pointFeature, startPoint, endPoint;
+ for(var i=0, len=pointFeatures.length; i<len; i++) {
+ pointFeature = pointFeatures[i];
+ endPoint = pointFeature.geometry;
+
+ if (!endPoint) {
+ var lonlat = pointFeature.lonlat;
+ endPoint = new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat);
+ } else if(endPoint.CLASS_NAME != "OpenLayers.Geometry.Point") {
+ OpenLayers.Console.error(
+ "Only features with point geometries are supported.");
+ return;
+ }
+
+ if(i > 0) {
+ var attributes = (this.dataFrom != null) ?
+ (pointFeatures[i+this.dataFrom].data ||
+ pointFeatures[i+this.dataFrom].attributes) :
+ null;
+ var line = new OpenLayers.Geometry.LineString([startPoint,
+ endPoint]);
+
+ lines[i-1] = new OpenLayers.Feature.Vector(line, attributes);
+ }
+
+ startPoint = endPoint;
+ }
+
+ this.addFeatures(lines);
+ },
+
+ CLASS_NAME: "OpenLayers.Layer.PointTrack"
+});
+
+/**
+ * Constant: OpenLayers.Layer.PointTrack.dataFrom
+ * {Object} with the following keys
+ * - SOURCE_NODE: take data/attributes from the source node of the line
+ * - TARGET_NODE: take data/attributes from the target node of the line
+ */
+OpenLayers.Layer.PointTrack.dataFrom = {'SOURCE_NODE': -1, 'TARGET_NODE': 0};
+
+/* ======================================================================
+ OpenLayers/Layer/WFS.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/Tile/WFS.js
+ * @requires OpenLayers/Layer/Vector.js
+ * @requires OpenLayers/Layer/Markers.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.WFS
+ *
+ * Inherits from:
+ * - <OpenLayers.Layer.Vector>
+ * - <OpenLayers.Layer.Markers>
+ */
+OpenLayers.Layer.WFS = OpenLayers.Class(
+ OpenLayers.Layer.Vector, OpenLayers.Layer.Markers, {
+
+ /**
+ * APIProperty: isBaseLayer
+ * {Boolean} WFS layer is not a base layer by default.
+ */
+ isBaseLayer: false,
+
+ /**
+ * Property: tile
+ * {<OpenLayers.Tile.WFS>}
+ */
+ tile: null,
+
+ /**
+ * APIProperty: ratio
+ * {Float} the ratio of image/tile size to map size (this is the untiled
+ * buffer)
+ */
+ ratio: 2,
+
+ /**
+ * Property: DEFAULT_PARAMS
+ * {Object} Hashtable of default key/value parameters
+ */
+ DEFAULT_PARAMS: { service: "WFS",
+ version: "1.0.0",
+ request: "GetFeature"
+ },
+
+ /**
+ * APIProperty: featureClass
+ * {<OpenLayers.Feature>} If featureClass is defined, an old-style markers
+ * based WFS layer is created instead of a new-style vector layer. If
+ * sent, this should be a subclass of OpenLayers.Feature
+ */
+ featureClass: null,
+
+ /**
+ * APIProperty: format
+ * {<OpenLayers.Format>} The format you want the data to be parsed with.
+ * Must be passed in the constructor. Should be a class, not an instance.
+ * This option can only be used if no featureClass is passed / vectorMode
+ * is false: if a featureClass is passed, then this parameter is ignored.
+ */
+ format: null,
+
+ /**
+ * Property: formatObject
+ * {<OpenLayers.Format>} Internally created/managed format object, used by
+ * the Tile to parse data.
+ */
+ formatObject: null,
+
+ /**
+ * APIProperty: formatOptions
+ * {Object} Hash of options which should be passed to the format when it is
+ * created. Must be passed in the constructor.
+ */
+ formatOptions: null,
+
+ /**
+ * Property: vectorMode
+ * {Boolean} Should be calculated automatically. Determines whether the
+ * layer is in vector mode or marker mode.
+ */
+ vectorMode: true,
+
+ /**
+ * APIProperty: encodeBBOX
+ * {Boolean} Should the BBOX commas be encoded? The WMS spec says 'no',
+ * but some services want it that way. Default false.
+ */
+ encodeBBOX: false,
+
+ /**
+ * APIProperty: extractAttributes
+ * {Boolean} Should the WFS layer parse attributes from the retrieved
+ * GML? Defaults to false. If enabled, parsing is slower, but
+ * attributes are available in the attributes property of
+ * layer features.
+ */
+ extractAttributes: false,
+
+ /**
+ * Constructor: OpenLayers.Layer.WFS
+ *
+ * Parameters:
+ * name - {String}
+ * url - {String}
+ * params - {Object}
+ * options - {Object} Hashtable of extra options to tag onto the layer
+ */
+ initialize: function(name, url, params, options) {
+ if (options == undefined) { options = {}; }
+
+ if (options.featureClass ||
+ !OpenLayers.Layer.Vector ||
+ !OpenLayers.Feature.Vector) {
+ this.vectorMode = false;
+ }
+
+ // Turn off error reporting, browsers like Safari may work
+ // depending on the setup, and we don't want an unneccesary alert.
+ OpenLayers.Util.extend(options, {'reportError': false});
+ var newArguments = [];
+ newArguments.push(name, options);
+ OpenLayers.Layer.Vector.prototype.initialize.apply(this, newArguments);
+ if (!this.renderer || !this.vectorMode) {
+ this.vectorMode = false;
+ if (!options.featureClass) {
+ options.featureClass = OpenLayers.Feature.WFS;
+ }
+ OpenLayers.Layer.Markers.prototype.initialize.apply(this,
+ newArguments);
+ }
+
+ if (this.params && this.params.typename && !this.options.typename) {
+ this.options.typename = this.params.typename;
+ }
+
+ if (!this.options.geometry_column) {
+ this.options.geometry_column = "the_geom";
+ }
+
+ this.params = OpenLayers.Util.applyDefaults(
+ params,
+ OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS)
+ );
+ this.url = url;
+ },
+
+
+ /**
+ * APIMethod: destroy
+ */
+ destroy: function() {
+ if (this.vectorMode) {
+ OpenLayers.Layer.Vector.prototype.destroy.apply(this, arguments);
+ } else {
+ OpenLayers.Layer.Markers.prototype.destroy.apply(this, arguments);
+ }
+ if (this.tile) {
+ this.tile.destroy();
+ }
+ this.tile = null;
+
+ this.ratio = null;
+ this.featureClass = null;
+ this.format = null;
+
+ if (this.formatObject && this.formatObject.destroy) {
+ this.formatObject.destroy();
+ }
+ this.formatObject = null;
+
+ this.formatOptions = null;
+ this.vectorMode = null;
+ this.encodeBBOX = null;
+ this.extractAttributes = null;
+ },
+
+ /**
+ * Method: setMap
+ *
+ * Parameters:
+ * map - {<OpenLayers.Map>}
+ */
+ setMap: function(map) {
+ if (this.vectorMode) {
+ OpenLayers.Layer.Vector.prototype.setMap.apply(this, arguments);
+
+ var options = {
+ 'extractAttributes': this.extractAttributes
+ };
+
+ OpenLayers.Util.extend(options, this.formatOptions);
+ if (this.map && !this.projection.equals(this.map.getProjectionObject())) {
+ options.externalProjection = this.projection;
+ options.internalProjection = this.map.getProjectionObject();
+ }
+
+ this.formatObject = this.format ? new this.format(options) : new OpenLayers.Format.GML(options);
+ } else {
+ OpenLayers.Layer.Markers.prototype.setMap.apply(this, arguments);
+ }
+ },
+
+ /**
+ * Method: moveTo
+ *
+ * Parameters:
+ * bounds - {<OpenLayers.Bounds>}
+ * zoomChanged - {Boolean}
+ * dragging - {Boolean}
+ */
+ moveTo:function(bounds, zoomChanged, dragging) {
+ if (this.vectorMode) {
+ OpenLayers.Layer.Vector.prototype.moveTo.apply(this, arguments);
+ } else {
+ OpenLayers.Layer.Markers.prototype.moveTo.apply(this, arguments);
+ }
+
+ // don't load wfs features while dragging, wait for drag end
+ if (dragging) {
+ // TBD try to hide the vector layer while dragging
+ // this.setVisibility(false);
+ // this will probably help for panning performances
+ return false;
+ }
+
+ if ( zoomChanged ) {
+ if (this.vectorMode) {
+ this.renderer.clear();
+ }
+ }
+
+ //DEPRECATED - REMOVE IN 3.0
+ // don't load data if current zoom level doesn't match
+ if (this.options.minZoomLevel) {
+ OpenLayers.Console.warn(OpenLayers.i18n('minZoomLevelError'));
+
+ if (this.map.getZoom() < this.options.minZoomLevel) {
+ return null;
+ }
+ }
+
+ if (bounds == null) {
+ bounds = this.map.getExtent();
+ }
+
+ var firstRendering = (this.tile == null);
+
+ //does the new bounds to which we need to move fall outside of the
+ // current tile's bounds?
+ var outOfBounds = (!firstRendering &&
+ !this.tile.bounds.containsBounds(bounds));
+
+ if (zoomChanged || firstRendering || (!dragging && outOfBounds)) {
+ //determine new tile bounds
+ var center = bounds.getCenterLonLat();
+ var tileWidth = bounds.getWidth() * this.ratio;
+ var tileHeight = bounds.getHeight() * this.ratio;
+ var tileBounds =
+ new OpenLayers.Bounds(center.lon - (tileWidth / 2),
+ center.lat - (tileHeight / 2),
+ center.lon + (tileWidth / 2),
+ center.lat + (tileHeight / 2));
+
+ //determine new tile size
+ var tileSize = this.map.getSize();
+ tileSize.w = tileSize.w * this.ratio;
+ tileSize.h = tileSize.h * this.ratio;
+
+ //determine new position (upper left corner of new bounds)
+ var ul = new OpenLayers.LonLat(tileBounds.left, tileBounds.top);
+ var pos = this.map.getLayerPxFromLonLat(ul);
+
+ //formulate request url string
+ var url = this.getFullRequestString();
+
+ var params = {BBOX: this.encodeBBOX ? tileBounds.toBBOX()
+ : tileBounds.toArray()};
+
+ if (this.map && !this.projection.equals(this.map.getProjectionObject())) {
+ var projectedBounds = tileBounds.clone();
+ projectedBounds.transform(this.map.getProjectionObject(),
+ this.projection);
+ params.BBOX = this.encodeBBOX ? projectedBounds.toBBOX()
+ : projectedBounds.toArray();
+ }
+
+ url += "&" + OpenLayers.Util.getParameterString(params);
+
+ if (!this.tile) {
+ this.tile = new OpenLayers.Tile.WFS(this, pos, tileBounds,
+ url, tileSize);
+ this.addTileMonitoringHooks(this.tile);
+ this.tile.draw();
+ } else {
+ if (this.vectorMode) {
+ this.destroyFeatures();
+ this.renderer.clear();
+ } else {
+ this.clearMarkers();
+ }
+ this.removeTileMonitoringHooks(this.tile);
+ this.tile.destroy();
+
+ this.tile = null;
+ this.tile = new OpenLayers.Tile.WFS(this, pos, tileBounds,
+ url, tileSize);
+ this.addTileMonitoringHooks(this.tile);
+ this.tile.draw();
+ }
+ }
+ },
+
+ /**
+ * Method: addTileMonitoringHooks
+ * This function takes a tile as input and adds the appropriate hooks to
+ * the tile so that the layer can keep track of the loading tile
+ * (making sure to check that the tile is always the layer's current
+ * tile before taking any action).
+ *
+ * Parameters:
+ * tile - {<OpenLayers.Tile>}
+ */
+ addTileMonitoringHooks: function(tile) {
+ tile.onLoadStart = function() {
+ //if this is the the layer's current tile, then trigger
+ // a 'loadstart'
+ if (this == this.layer.tile) {
+ this.layer.events.triggerEvent("loadstart");
+ }
+ };
+ tile.events.register("loadstart", tile, tile.onLoadStart);
+
+ tile.onLoadEnd = function() {
+ //if this is the the layer's current tile, then trigger
+ // a 'tileloaded' and 'loadend'
+ if (this == this.layer.tile) {
+ this.layer.events.triggerEvent("tileloaded");
+ this.layer.events.triggerEvent("loadend");
+ }
+ };
+ tile.events.register("loadend", tile, tile.onLoadEnd);
+ tile.events.register("unload", tile, tile.onLoadEnd);
+ },
+
+ /**
+ * Method: removeTileMonitoringHooks
+ * This function takes a tile as input and removes the tile hooks
+ * that were added in addTileMonitoringHooks()
+ *
+ * Parameters:
+ * tile - {<OpenLayers.Tile>}
+ */
+ removeTileMonitoringHooks: function(tile) {
+ tile.unload();
+ tile.events.un({
+ "loadstart": tile.onLoadStart,
+ "loadend": tile.onLoadEnd,
+ "unload": tile.onLoadEnd,
+ scope: tile
+ });
+ },
+
+ /**
+ * Method: onMapResize
+ * Call the onMapResize method of the appropriate parent class.
+ */
+ onMapResize: function() {
+ if(this.vectorMode) {
+ OpenLayers.Layer.Vector.prototype.onMapResize.apply(this,
+ arguments);
+ } else {
+ OpenLayers.Layer.Markers.prototype.onMapResize.apply(this,
+ arguments);
+ }
+ },
+
+ /**
+ * APIMethod: mergeNewParams
+ * Modify parameters for the layer and redraw.
+ *
+ * Parameters:
+ * newParams - {Object}
+ */
+ mergeNewParams:function(newParams) {
+ var upperParams = OpenLayers.Util.upperCaseObject(newParams);
+ var newArguments = [upperParams];
+ return OpenLayers.Layer.HTTPRequest.prototype.mergeNewParams.apply(this,
+ newArguments);
+ },
+
+ /**
+ * APIMethod: clone
+ *
+ * Parameters:
+ * obj - {Object}
+ *
+ * Returns:
+ * {<OpenLayers.Layer.WFS>} An exact clone of this OpenLayers.Layer.WFS
+ */
+ clone: function (obj) {
+
+ if (obj == null) {
+ obj = new OpenLayers.Layer.WFS(this.name,
+ this.url,
+ this.params,
+ this.options);
+ }
+
+ //get all additions from superclasses
+ if (this.vectorMode) {
+ obj = OpenLayers.Layer.Vector.prototype.clone.apply(this, [obj]);
+ } else {
+ obj = OpenLayers.Layer.Markers.prototype.clone.apply(this, [obj]);
+ }
+
+ // copy/set any non-init, non-simple values here
+
+ return obj;
+ },
+
+ /**
+ * APIMethod: getFullRequestString
+ * combine the layer's url with its params and these newParams.
+ *
+ * Add the SRS parameter from 'projection' -- this is probably
+ * more eloquently done via a setProjection() method, but this
+ * works for now and always.
+ *
+ * Parameters:
+ * newParams - {Object}
+ * altUrl - {String} Use this as the url instead of the layer's url
+ */
+ getFullRequestString:function(newParams, altUrl) {
+ var projectionCode = this.projection.getCode() || this.map.getProjection();
+ this.params.SRS = (projectionCode == "none") ? null : projectionCode;
+
+ return OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(
+ this, arguments);
+ },
+
+ /**
+ * APIMethod: commit
+ * Write out the data to a WFS server.
+ */
+ commit: function() {
+ if (!this.writer) {
+ var options = {};
+ if (this.map && !this.projection.equals(this.map.getProjectionObject())) {
+ options.externalProjection = this.projection;
+ options.internalProjection = this.map.getProjectionObject();
+ }
+
+ this.writer = new OpenLayers.Format.WFS(options,this);
+ }
+
+ var data = this.writer.write(this.features);
+
+ OpenLayers.Request.POST({
+ url: this.url,
+ data: data,
+ success: this.commitSuccess,
+ failure: this.commitFailure,
+ scope: this
+ });
+ },
+
+ /**
+ * Method: commitSuccess
+ * Called when the Ajax request returns a response
+ *
+ * Parameters:
+ * response - {XmlNode} from server
+ */
+ commitSuccess: function(request) {
+ var response = request.responseText;
+ if (response.indexOf('SUCCESS') != -1) {
+ this.commitReport(OpenLayers.i18n("commitSuccess", {'response':response}));
+
+ for(var i = 0; i < this.features.length; i++) {
+ this.features[i].state = null;
+ }
+ // TBD redraw the layer or reset the state of features
+ // foreach features: set state to null
+ } else if (response.indexOf('FAILED') != -1 ||
+ response.indexOf('Exception') != -1) {
+ this.commitReport(OpenLayers.i18n("commitFailed", {'response':response}));
+ }
+ },
+
+ /**
+ * Method: commitFailure
+ * Called when the Ajax request fails
+ *
+ * Parameters:
+ * response - {XmlNode} from server
+ */
+ commitFailure: function(request) {},
+
+ /**
+ * APIMethod: commitReport
+ * Called with a 'success' message if the commit succeeded, otherwise
+ * a failure message, and the full request text as a second parameter.
+ * Override this function to provide custom transaction reporting.
+ *
+ * string - {String} reporting string
+ * response - {String} full XML response
+ */
+ commitReport: function(string, response) {
+ OpenLayers.Console.userError(string);
+ },
+
+
+ /**
+ * APIMethod: refresh
+ * Refreshes all the features of the layer
+ */
+ refresh: function() {
+ if (this.tile) {
+ if (this.vectorMode) {
+ this.renderer.clear();
+ this.features.length = 0;
+ } else {
+ this.clearMarkers();
+ this.markers.length = 0;
+ }
+ this.tile.draw();
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Layer.WFS"
+});
+/* ======================================================================
+ OpenLayers/Format/Filter/v1.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/Format/Filter.js
+ */
+
+/**
+ * Class: OpenLayers.Format.Filter.v1
+ * Superclass for Filter version 1 parsers.
+ *
+ * Inherits from:
+ * - <OpenLayers.Format.XML>
+ */
+OpenLayers.Format.Filter.v1 = OpenLayers.Class(OpenLayers.Format.XML, {
+
+ /**
+ * Property: namespaces
+ * {Object} Mapping of namespace aliases to namespace URIs.
+ */
+ namespaces: {
+ ogc: "http://www.opengis.net/ogc",
+ xlink: "http://www.w3.org/1999/xlink",
+ xsi: "http://www.w3.org/2001/XMLSchema-instance"
+ },
+
+ /**
+ * Property: defaultPrefix
+ */
+ defaultPrefix: "ogc",
+
+ /**
+ * Property: schemaLocation
+ * {String} Schema location for a particular minor version.
+ */
+ schemaLocation: null,
+
+ /**
+ * Constructor: OpenLayers.Format.Filter.v1
+ * Instances of this class are not created directly. Use the
+ * <OpenLayers.Format.Filter> constructor instead.
+ *
+ * Parameters:
+ * options - {Object} An optional object whose properties will be set on
+ * this instance.
+ */
+ initialize: function(options) {
+ OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
+ },
+
+ /**
+ * Method: read
+ *
+ * Parameters:
+ * data - {DOMElement} A Filter document element.
+ *
+ * Returns:
+ * {<OpenLayers.Filter>} A filter object.
+ */
+ read: function(data) {
+ var obj = {}
+ var filter = this.readers.ogc["Filter"].apply(this, [data, obj]);
+ return obj.filter;
+ },
+
+ /**
+ * Property: readers
+ * Contains public functions, grouped by namespace prefix, that will
+ * be applied when a namespaced node is found matching the function
+ * name. The function will be applied in the scope of this parser
+ * with two arguments: the node being read and a context object passed
+ * from the parent.
+ */
+ readers: {
+ "ogc": {
+ "Filter": function(node, parent) {
+ // Filters correspond to subclasses of OpenLayers.Filter.
+ // Since they contain information we don't persist, we
+ // create a temporary object and then pass on the filter
+ // (ogc:Filter) to the parent obj.
+ var obj = {
+ fids: [],
+ filters: []
+ };
+ this.readChildNodes(node, obj);
+ if(obj.fids.length > 0) {
+ parent.filter = new OpenLayers.Filter.FeatureId({
+ fids: obj.fids
+ });
+ } else if(obj.filters.length > 0) {
+ parent.filter = obj.filters[0];
+ }
+ },
+ "FeatureId": function(node, obj) {
+ var fid = node.getAttribute("fid");
+ if(fid) {
+ obj.fids.push(fid);
+ }
+ },
+ "And": function(node, obj) {
+ var filter = new OpenLayers.Filter.Logical({
+ type: OpenLayers.Filter.Logical.AND
+ });
+ this.readChildNodes(node, filter);
+ obj.filters.push(filter);
+ },
+ "Or": function(node, obj) {
+ var filter = new OpenLayers.Filter.Logical({
+ type: OpenLayers.Filter.Logical.OR
+ });
+ this.readChildNodes(node, filter);
+ obj.filters.push(filter);
+ },
+ "Not": function(node, obj) {
+ var filter = new OpenLayers.Filter.Logical({
+ type: OpenLayers.Filter.Logical.NOT
+ });
+ this.readChildNodes(node, filter);
+ obj.filters.push(filter);
+ },
+ "PropertyIsEqualTo": function(node, obj) {
+ var filter = new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.EQUAL_TO
+ });
+ this.readChildNodes(node, filter);
+ obj.filters.push(filter);
+ },
+ "PropertyIsNotEqualTo": function(node, obj) {
+ var filter = new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.NOT_EQUAL_TO
+ });
+ this.readChildNodes(node, filter);
+ obj.filters.push(filter);
+ },
+ "PropertyIsLessThan": function(node, obj) {
+ var filter = new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.LESS_THAN
+ });
+ this.readChildNodes(node, filter);
+ obj.filters.push(filter);
+ },
+ "PropertyIsGreaterThan": function(node, obj) {
+ var filter = new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.GREATER_THAN
+ });
+ this.readChildNodes(node, filter);
+ obj.filters.push(filter);
+ },
+ "PropertyIsLessThanOrEqualTo": function(node, obj) {
+ var filter = new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO
+ });
+ this.readChildNodes(node, filter);
+ obj.filters.push(filter);
+ },
+ "PropertyIsGreaterThanOrEqualTo": function(node, obj) {
+ var filter = new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO
+ });
+ this.readChildNodes(node, filter);
+ obj.filters.push(filter);
+ },
+ "PropertyIsBetween": function(node, obj) {
+ var filter = new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.BETWEEN
+ });
+ this.readChildNodes(node, filter);
+ obj.filters.push(filter);
+ },
+ "PropertyIsLike": function(node, obj) {
+ var filter = new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.LIKE
+ });
+ this.readChildNodes(node, filter);
+ var wildCard = node.getAttribute("wildCard");
+ var singleChar = node.getAttribute("singleChar");
+ var esc = node.getAttribute("escape");
+ filter.value2regex(wildCard, singleChar, esc);
+ obj.filters.push(filter);
+ },
+ "Literal": function(node, obj) {
+ obj.value = this.getChildValue(node);
+ },
+ "PropertyName": function(node, filter) {
+ filter.property = this.getChildValue(node);
+ },
+ "LowerBoundary": function(node, filter) {
+ filter.lowerBoundary = this.readOgcExpression(node);
+ },
+ "UpperBoundary": function(node, filter) {
+ filter.upperBoundary = this.readOgcExpression(node);
+ }
+
+ }
+ },
+
+ /**
+ * Method: readOgcExpression
+ * Limited support for OGC expressions.
+ *
+ * Parameters:
+ * node - {DOMElement} A DOM element that contains an ogc:expression.
+ *
+ * Returns:
+ * {String} A value to be used in a symbolizer.
+ */
+ readOgcExpression: function(node) {
+ var obj = {};
+ this.readChildNodes(node, obj);
+ var value = obj.value;
+ if(!value) {
+ value = this.getChildValue(node);
+ }
+ return value;
+ },
+
+ /**
+ * Method: write
+ *
+ * Parameters:
+ * filter - {<OpenLayers.Filter>} A filter object.
+ *
+ * Returns:
+ * {DOMElement} An ogc:Filter element.
+ */
+ write: function(filter) {
+ return this.writers.ogc["Filter"].apply(this, [filter]);
+ },
+
+ /**
+ * Property: writers
+ * As a compliment to the readers property, this structure contains public
+ * writing functions grouped by namespace alias and named like the
+ * node names they produce.
+ */
+ writers: {
+ "ogc": {
+ "Filter": function(filter) {
+ var node = this.createElementNSPlus("ogc:Filter");
+ var sub = filter.CLASS_NAME.split(".").pop();
+ if(sub == "FeatureId") {
+ for(var i=0; i<filter.fids.length; ++i) {
+ this.writeNode(node, "FeatureId", filter.fids[i]);
+ }
+ } else {
+ this.writeNode(node, this.getFilterType(filter), filter);
+ }
+ return node;
+ },
+ "FeatureId": function(fid) {
+ return this.createElementNSPlus("ogc:FeatureId", {
+ attributes: {fid: fid}
+ });
+ },
+ "And": function(filter) {
+ var node = this.createElementNSPlus("ogc:And");
+ var childFilter;
+ for(var i=0; i<filter.filters.length; ++i) {
+ childFilter = filter.filters[i];
+ this.writeNode(
+ node, this.getFilterType(childFilter), childFilter
+ );
+ }
+ return node;
+ },
+ "Or": function(filter) {
+ var node = this.createElementNSPlus("ogc:Or");
+ var childFilter;
+ for(var i=0; i<filter.filters.length; ++i) {
+ childFilter = filter.filters[i];
+ this.writeNode(
+ node, this.getFilterType(childFilter), childFilter
+ );
+ }
+ return node;
+ },
+ "Not": function(filter) {
+ var node = this.createElementNSPlus("ogc:Not");
+ var childFilter = filter.filters[0];
+ this.writeNode(
+ node, this.getFilterType(childFilter), childFilter
+ );
+ return node;
+ },
+ "PropertyIsEqualTo": function(filter) {
+ var node = this.createElementNSPlus("ogc:PropertyIsEqualTo");
+ // no ogc:expression handling for now
+ this.writeNode(node, "PropertyName", filter);
+ this.writeNode(node, "Literal", filter.value);
+ return node;
+ },
+ "PropertyIsNotEqualTo": function(filter) {
+ var node = this.createElementNSPlus("ogc:PropertyIsNotEqualTo");
+ // no ogc:expression handling for now
+ this.writeNode(node, "PropertyName", filter);
+ this.writeNode(node, "Literal", filter.value);
+ return node;
+ },
+ "PropertyIsLessThan": function(filter) {
+ var node = this.createElementNSPlus("ogc:PropertyIsLessThan");
+ // no ogc:expression handling for now
+ this.writeNode(node, "PropertyName", filter);
+ this.writeNode(node, "Literal", filter.value);
+ return node;
+ },
+ "PropertyIsGreaterThan": function(filter) {
+ var node = this.createElementNSPlus("ogc:PropertyIsGreaterThan");
+ // no ogc:expression handling for now
+ this.writeNode(node, "PropertyName", filter);
+ this.writeNode(node, "Literal", filter.value);
+ return node;
+ },
+ "PropertyIsLessThanOrEqualTo": function(filter) {
+ var node = this.createElementNSPlus("ogc:PropertyIsLessThanOrEqualTo");
+ // no ogc:expression handling for now
+ this.writeNode(node, "PropertyName", filter);
+ this.writeNode(node, "Literal", filter.value);
+ return node;
+ },
+ "PropertyIsGreaterThanOrEqualTo": function(filter) {
+ var node = this.createElementNSPlus("ogc:PropertyIsGreaterThanOrEqualTo");
+ // no ogc:expression handling for now
+ this.writeNode(node, "PropertyName", filter);
+ this.writeNode(node, "Literal", filter.value);
+ return node;
+ },
+ "PropertyIsBetween": function(filter) {
+ var node = this.createElementNSPlus("ogc:PropertyIsBetween");
+ // no ogc:expression handling for now
+ this.writeNode(node, "PropertyName", filter);
+ this.writeNode(node, "LowerBoundary", filter);
+ this.writeNode(node, "UpperBoundary", filter);
+ return node;
+ },
+ "PropertyIsLike": function(filter) {
+ var node = this.createElementNSPlus("ogc:PropertyIsLike", {
+ attributes: {
+ wildCard: "*", singleChar: ".", escape: "!"
+ }
+ });
+ // no ogc:expression handling for now
+ this.writeNode(node, "PropertyName", filter);
+ // convert regex string to ogc string
+ this.writeNode(node, "Literal", filter.regex2value());
+ return node;
+ },
+ "PropertyName": function(filter) {
+ // no ogc:expression handling for now
+ return this.createElementNSPlus("ogc:PropertyName", {
+ value: filter.property
+ });
+ },
+ "Literal": function(value) {
+ // no ogc:expression handling for now
+ return this.createElementNSPlus("ogc:Literal", {
+ value: value
+ });
+ },
+ "LowerBoundary": function(filter) {
+ // no ogc:expression handling for now
+ var node = this.createElementNSPlus("ogc:LowerBoundary");
+ this.writeNode(node, "Literal", filter.lowerBoundary);
+ return node;
+ },
+ "UpperBoundary": function(filter) {
+ // no ogc:expression handling for now
+ var node = this.createElementNSPlus("ogc:UpperBoundary");
+ this.writeNode(node, "Literal", filter.upperBoundary);
+ return node;
+ },
+ "BBOX": function(filter) {
+ var node = this.createElementNSPlus("ogc:BBOX");
+ this.writeNode(node, "PropertyName", filter);
+ var gml = new OpenLayers.Format.GML();
+ node.appendChild(gml.buildGeometryNode(filter.value));
+ return node;
+ },
+ "DWITHIN": function(filter) {
+ var node = this.createElementNSPlus("ogc:DWithin");
+ this.writeNode(node, "PropertyName", filter);
+ var gml = new OpenLayers.Format.GML();
+ node.appendChild(gml.buildGeometryNode(filter.value));
+ this.writeNode(node, "Distance", filter);
+ return node;
+ },
+ "INTERSECTS": function(filter) {
+ var node = this.createElementNSPlus("ogc:Intersects");
+ this.writeNode(node, "PropertyName", filter);
+ var gml = new OpenLayers.Format.GML();
+ node.appendChild(gml.buildGeometryNode(filter.value));
+ return node;
+ },
+ "Distance": function(filter) {
+ return this.createElementNSPlus("ogc:Distance",
+ {attributes: {units: filter.distanceUnits},
+ value: filter.distance});
+ }
+ }
+ },
+
+ /**
+ * Method: getFilterType
+ */
+ getFilterType: function(filter) {
+ var filterType = this.filterMap[filter.type];
+ if(!filterType) {
+ throw "Filter writing not supported for rule type: " + filter.type;
+ }
+ return filterType;
+ },
+
+ /**
+ * Property: filterMap
+ * {Object} Contains a member for each filter type. Values are node names
+ * for corresponding OGC Filter child elements.
+ */
+ filterMap: {
+ "&&": "And",
+ "||": "Or",
+ "!": "Not",
+ "==": "PropertyIsEqualTo",
+ "!=": "PropertyIsNotEqualTo",
+ "<": "PropertyIsLessThan",
+ ">": "PropertyIsGreaterThan",
+ "<=": "PropertyIsLessThanOrEqualTo",
+ ">=": "PropertyIsGreaterThanOrEqualTo",
+ "..": "PropertyIsBetween",
+ "~": "PropertyIsLike",
+ "BBOX": "BBOX",
+ "DWITHIN": "DWITHIN",
+ "INTERSECTS": "INTERSECTS"
+ },
+
+
+ /**
+ * Methods below this point are of general use for versioned XML parsers.
+ * These are candidates for an abstract class.
+ */
+
+ /**
+ * Method: getNamespacePrefix
+ * Get the namespace prefix for a given uri from the <namespaces> object.
+ *
+ * Returns:
+ * {String} A namespace prefix or null if none found.
+ */
+ getNamespacePrefix: function(uri) {
+ var prefix = null;
+ if(uri == null) {
+ prefix = this.namespaces[this.defaultPrefix];
+ } else {
+ var gotPrefix = false;
+ for(prefix in this.namespaces) {
+ if(this.namespaces[prefix] == uri) {
+ gotPrefix = true;
+ break;
+ }
+ }
+ if(!gotPrefix) {
+ prefix = null;
+ }
+ }
+ return prefix;
+ },
+
+
+ /**
+ * Method: readChildNodes
+ */
+ readChildNodes: function(node, obj) {
+ var children = node.childNodes;
+ var child, group, reader, prefix, local;
+ for(var i=0; i<children.length; ++i) {
+ child = children[i];
+ if(child.nodeType == 1) {
+ prefix = this.getNamespacePrefix(child.namespaceURI);
+ local = child.nodeName.split(":").pop();
+ group = this.readers[prefix];
+ if(group) {
+ reader = group[local];
+ if(reader) {
+ reader.apply(this, [child, obj]);
+ }
+ }
+ }
+ }
+ },
+
+ /**
+ * Method: writeNode
+ * Shorthand for applying one of the named writers and appending the
+ * results to a node. If a qualified name is not provided for the
+ * second argument (and a local name is used instead), the namespace
+ * of the parent node will be assumed.
+ *
+ * Parameters:
+ * parent - {DOMElement} Result will be appended to this node.
+ * name - {String} The name of a node to generate. If a qualified name
+ * (e.g. "pre:Name") is used, the namespace prefix is assumed to be
+ * in the <writers> group. If a local name is used (e.g. "Name") then
+ * the namespace of the parent is assumed.
+ * obj - {Object} Structure containing data for the writer.
+ *
+ * Returns:
+ * {DOMElement} The child node.
+ */
+ writeNode: function(parent, name, obj) {
+ var prefix, local;
+ var split = name.indexOf(":");
+ if(split > 0) {
+ prefix = name.substring(0, split);
+ local = name.substring(split + 1);
+ } else {
+ prefix = this.getNamespacePrefix(parent.namespaceURI);
+ local = name;
+ }
+ var child = this.writers[prefix][local].apply(this, [obj]);
+ parent.appendChild(child);
+ return child;
+ },
+
+ /**
+ * Method: createElementNSPlus
+ * Shorthand for creating namespaced elements with optional attributes and
+ * child text nodes.
+ *
+ * Parameters:
+ * name - {String} The qualified node name.
+ * options - {Object} Optional object for node configuration.
+ *
+ * Returns:
+ * {Element} An element node.
+ */
+ createElementNSPlus: function(name, options) {
+ options = options || {};
+ var loc = name.indexOf(":");
+ // order of prefix preference
+ // 1. in the uri option
+ // 2. in the prefix option
+ // 3. in the qualified name
+ // 4. from the defaultPrefix
+ var uri = options.uri || this.namespaces[options.prefix];
+ if(!uri) {
+ loc = name.indexOf(":");
+ uri = this.namespaces[name.substring(0, loc)];
+ }
+ if(!uri) {
+ uri = this.namespaces[this.defaultPrefix];
+ }
+ var node = this.createElementNS(uri, name);
+ if(options.attributes) {
+ this.setAttributes(node, options.attributes);
+ }
+ if(options.value) {
+ node.appendChild(this.createTextNode(options.value));
+ }
+ return node;
+ },
+
+ /**
+ * Method: setAttributes
+ * Set multiple attributes given key value pairs from an object.
+ *
+ * Parameters:
+ * node - {Element} An element node.
+ * obj - {Object || Array} An object whose properties represent attribute
+ * names and values represent attribute values. If an attribute name
+ * is a qualified name ("prefix:local"), the prefix will be looked up
+ * in the parsers {namespaces} object. If the prefix is found,
+ * setAttributeNS will be used instead of setAttribute.
+ */
+ setAttributes: function(node, obj) {
+ var value, loc, alias, uri;
+ for(var name in obj) {
+ value = obj[name].toString();
+ // check for qualified attribute name ("prefix:local")
+ uri = this.namespaces[name.substring(0, name.indexOf(":"))] || null;
+ this.setAttributeNS(node, uri, name, value);
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Format.Filter.v1"
+
+});
+/* ======================================================================
+ OpenLayers/Format/SLD/v1.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/Rule.js
+ * @requires OpenLayers/Filter.js
+ * @requires OpenLayers/Filter/FeatureId.js
+ * @requires OpenLayers/Filter/Logical.js
+ * @requires OpenLayers/Filter/Comparison.js
+ * @requires OpenLayers/Format/SLD.js
+ */
+
+/**
+ * Class: OpenLayers.Format.SLD.v1
+ * Superclass for SLD version 1 parsers.
+ *
+ * Inherits from:
+ * - <OpenLayers.Format.XML>
+ */
+OpenLayers.Format.SLD.v1 = OpenLayers.Class(OpenLayers.Format.XML, {
+
+ /**
+ * Property: namespaces
+ * {Object} Mapping of namespace aliases to namespace URIs.
+ */
+ namespaces: {
+ sld: "http://www.opengis.net/sld",
+ ogc: "http://www.opengis.net/ogc",
+ xlink: "http://www.w3.org/1999/xlink",
+ xsi: "http://www.w3.org/2001/XMLSchema-instance"
+ },
+
+ /**
+ * Property: defaultPrefix
+ */
+ defaultPrefix: "sld",
+
+ /**
+ * Property: schemaLocation
+ * {String} Schema location for a particular minor version.
+ */
+ schemaLocation: null,
+
+ /**
+ * APIProperty: defaultSymbolizer.
+ * {Object} A symbolizer with the SLD defaults.
+ */
+ defaultSymbolizer: {
+ fillColor: "#808080",
+ fillOpacity: 1,
+ strokeColor: "#000000",
+ strokeOpacity: 1,
+ strokeWidth: 1,
+ strokeDashstyle: "solid",
+ pointRadius: 3,
+ graphicName: "square"
+ },
+
+ /**
+ * Constructor: OpenLayers.Format.SLD.v1
+ * Instances of this class are not created directly. Use the
+ * <OpenLayers.Format.SLD> constructor instead.
+ *
+ * Parameters:
+ * options - {Object} An optional object whose properties will be set on
+ * this instance.
+ */
+ initialize: function(options) {
+ // extend with ogc:Filter readers and writers
+ this.readers["ogc"] = OpenLayers.Format.Filter.v1.prototype.readers["ogc"];
+ this.writers["ogc"] = OpenLayers.Format.Filter.v1.prototype.writers["ogc"];
+ // extend with custom filter methods that may get changed
+ this.readOgcExpression = OpenLayers.Format.Filter.v1.prototype.readOgcExpression;
+ this.getFilterType = OpenLayers.Format.Filter.v1.prototype.getFilterType;
+ this.filterMap = OpenLayers.Format.Filter.v1.prototype.filterMap;
+ OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
+ },
+
+ /**
+ * Method: read
+ *
+ * Parameters:
+ * data - {DOMElement} An SLD document element.
+ *
+ * Returns:
+ * {Object} An object representing the SLD.
+ */
+ read: function(data) {
+ var sld = {
+ namedLayers: {}
+ };
+ this.readChildNodes(data, sld);
+ return sld;
+ },
+
+ /**
+ * Property: readers
+ * Contains public functions, grouped by namespace prefix, that will
+ * be applied when a namespaced node is found matching the function
+ * name. The function will be applied in the scope of this parser
+ * with two arguments: the node being read and a context object passed
+ * from the parent.
+ */
+ readers: {
+ "sld": {
+ "StyledLayerDescriptor": function(node, sld) {
+ sld.version = node.getAttribute("version");
+ this.readChildNodes(node, sld);
+ },
+ "Name": function(node, obj) {
+ obj.name = this.getChildValue(node);
+ },
+ "Title": function(node, obj) {
+ obj.title = this.getChildValue(node);
+ },
+ "Abstract": function(node, obj) {
+ obj.description = this.getChildValue(node);
+ },
+ "NamedLayer": function(node, sld) {
+ var layer = {
+ userStyles: [],
+ namedStyles: []
+ };
+ this.readChildNodes(node, layer);
+ // give each of the user styles this layer name
+ for(var i=0, len=layer.userStyles.length; i<len; ++i) {
+ layer.userStyles[i].layerName = layer.name;
+ }
+ sld.namedLayers[layer.name] = layer;
+ },
+ "NamedStyle": function(node, layer) {
+ layer.namedStyles.push(
+ this.getChildName(node.firstChild)
+ );
+ },
+ "UserStyle": function(node, layer) {
+ var style = new OpenLayers.Style(this.defaultSymbolizer);
+ this.readChildNodes(node, style);
+ layer.userStyles.push(style);
+ },
+ "IsDefault": function(node, style) {
+ if(this.getChildValue(node) == "1") {
+ style.isDefault = true;
+ }
+ },
+ "FeatureTypeStyle": function(node, style) {
+ // OpenLayers doesn't have a place for FeatureTypeStyle
+ // Name, Title, Abstract, FeatureTypeName, or
+ // SemanticTypeIdentifier so, we make a temporary object
+ // and later just use the Rule(s).
+ var obj = {
+ rules: []
+ };
+ this.readChildNodes(node, obj);
+ style.rules = obj.rules;
+ },
+ "Rule": function(node, obj) {
+ var rule = new OpenLayers.Rule();
+ this.readChildNodes(node, rule);
+ obj.rules.push(rule);
+ },
+ "ElseFilter": function(node, rule) {
+ rule.elseFilter = true;
+ },
+ "MinScaleDenominator": function(node, rule) {
+ rule.minScaleDenominator = this.getChildValue(node);
+ },
+ "MaxScaleDenominator": function(node, rule) {
+ rule.maxScaleDenominator = this.getChildValue(node);
+ },
+ "LineSymbolizer": function(node, rule) {
+ // OpenLayers doens't do painter's order, instead we extend
+ var symbolizer = rule.symbolizer["Line"] || {};
+ this.readChildNodes(node, symbolizer);
+ // in case it didn't exist before
+ rule.symbolizer["Line"] = symbolizer;
+ },
+ "PolygonSymbolizer": function(node, rule) {
+ // OpenLayers doens't do painter's order, instead we extend
+ var symbolizer = rule.symbolizer["Polygon"] || {};
+ this.readChildNodes(node, symbolizer);
+ // in case it didn't exist before
+ rule.symbolizer["Polygon"] = symbolizer;
+ },
+ "PointSymbolizer": function(node, rule) {
+ // OpenLayers doens't do painter's order, instead we extend
+ var symbolizer = rule.symbolizer["Point"] || {};
+ this.readChildNodes(node, symbolizer);
+ // in case it didn't exist before
+ rule.symbolizer["Point"] = symbolizer;
+ },
+ "Stroke": function(node, symbolizer) {
+ this.readChildNodes(node, symbolizer);
+ },
+ "Fill": function(node, symbolizer) {
+ this.readChildNodes(node, symbolizer);
+ },
+ "CssParameter": function(node, symbolizer) {
+ var cssProperty = node.getAttribute("name");
+ var symProperty = this.cssMap[cssProperty];
+ if(symProperty) {
+ // Limited support for parsing of OGC expressions
+ var value = this.readOgcExpression(node);
+ // always string, could be an empty string
+ if(value) {
+ symbolizer[symProperty] = value;
+ }
+ }
+ },
+ "Graphic": function(node, symbolizer) {
+ var graphic = {};
+ // painter's order not respected here, clobber previous with next
+ this.readChildNodes(node, graphic);
+ // directly properties with names that match symbolizer properties
+ var properties = [
+ "strokeColor", "strokeWidth", "strokeOpacity",
+ "strokeLinecap", "fillColor", "fillOpacity",
+ "graphicName", "rotation", "graphicFormat"
+ ];
+ var prop, value;
+ for(var i=0, len=properties.length; i<len; ++i) {
+ prop = properties[i];
+ value = graphic[prop];
+ if(value != undefined) {
+ symbolizer[prop] = value;
+ }
+ }
+ // set other generic properties with specific graphic property names
+ if(graphic.opacity != undefined) {
+ symbolizer.graphicOpacity = graphic.opacity;
+ }
+ if(graphic.size != undefined) {
+ symbolizer.pointRadius = graphic.size / 2;
+ }
+ if(graphic.href != undefined) {
+ symbolizer.externalGraphic = graphic.href;
+ }
+ if(graphic.rotation != undefined) {
+ symbolizer.rotation = graphic.rotation;
+ }
+ },
+ "ExternalGraphic": function(node, graphic) {
+ this.readChildNodes(node, graphic);
+ },
+ "Mark": function(node, graphic) {
+ this.readChildNodes(node, graphic);
+ },
+ "WellKnownName": function(node, graphic) {
+ graphic.graphicName = this.getChildValue(node);
+ },
+ "Opacity": function(node, obj) {
+ // No support for parsing of OGC expressions
+ var opacity = this.getChildValue(node);
+ // always string, could be empty string
+ if(opacity) {
+ obj.opacity = opacity;
+ }
+ },
+ "Size": function(node, obj) {
+ // No support for parsing of OGC expressions
+ var size = this.getChildValue(node);
+ // always string, could be empty string
+ if(size) {
+ obj.size = size;
+ }
+ },
+ "Rotation": function(node, obj) {
+ // No support for parsing of OGC expressions
+ var rotation = this.getChildValue(node);
+ // always string, could be empty string
+ if(rotation) {
+ obj.rotation = rotation;
+ }
+ },
+ "OnlineResource": function(node, obj) {
+ obj.href = this.getAttributeNS(
+ node, this.namespaces.xlink, "href"
+ );
+ },
+ "Format": function(node, graphic) {
+ graphic.graphicFormat = this.getChildValue(node);
+ }
+ }
+ },
+
+ /**
+ * Property: cssMap
+ * {Object} Object mapping supported css property names to OpenLayers
+ * symbolizer property names.
+ */
+ cssMap: {
+ "stroke": "strokeColor",
+ "stroke-opacity": "strokeOpacity",
+ "stroke-width": "strokeWidth",
+ "stroke-linecap": "strokeLinecap",
+ "stroke-dasharray": "strokeDashstyle",
+ "fill": "fillColor",
+ "fill-opacity": "fillOpacity",
+ "font-family": "fontFamily",
+ "font-size": "fontSize"
+ },
+
+ /**
+ * Method: getCssProperty
+ * Given a symbolizer property, get the corresponding CSS property
+ * from the <cssMap>.
+ *
+ * Parameters:
+ * sym - {String} A symbolizer property name.
+ *
+ * Returns:
+ * {String} A CSS property name or null if none found.
+ */
+ getCssProperty: function(sym) {
+ var css = null;
+ for(var prop in this.cssMap) {
+ if(this.cssMap[prop] == sym) {
+ css = prop;
+ break;
+ }
+ }
+ return css;
+ },
+
+ /**
+ * Method: getGraphicFormat
+ * Given a href for an external graphic, try to determine the mime-type.
+ * This method doesn't try too hard, and will fall back to
+ * <defautlGraphicFormat> if one of the known <graphicFormats> is not
+ * the file extension of the provided href.
+ *
+ * Parameters:
+ * href - {String}
+ *
+ * Returns:
+ * {String} The graphic format.
+ */
+ getGraphicFormat: function(href) {
+ var format, regex;
+ for(var key in this.graphicFormats) {
+ if(this.graphicFormats[key].test(href)) {
+ format = key;
+ break;
+ }
+ }
+ return format || this.defautlGraphicFormat;
+ },
+
+ /**
+ * Property: defaultGraphicFormat
+ * {String} If none other can be determined from <getGraphicFormat>, this
+ * default will be returned.
+ */
+ defaultGraphicFormat: "image/png",
+
+ /**
+ * Property: graphicFormats
+ * {Object} Mapping of image mime-types to regular extensions matching
+ * well-known file extensions.
+ */
+ graphicFormats: {
+ "image/jpeg": /\.jpe?g$/i,
+ "image/gif": /\.gif$/i,
+ "image/png": /\.png$/i
+ },
+
+ /**
+ * Method: write
+ *
+ * Parameters:
+ * sld - {Object} An object representing the SLD.
+ *
+ * Returns:
+ * {DOMElement} The root of an SLD document.
+ */
+ write: function(sld) {
+ return this.writers.sld.StyledLayerDescriptor.apply(this, [sld]);
+ },
+
+ /**
+ * Property: writers
+ * As a compliment to the readers property, this structure contains public
+ * writing functions grouped by namespace alias and named like the
+ * node names they produce.
+ */
+ writers: {
+ "sld": {
+ "StyledLayerDescriptor": function(sld) {
+ var root = this.createElementNSPlus(
+ "StyledLayerDescriptor",
+ {attributes: {
+ "version": this.VERSION,
+ "xsi:schemaLocation": this.schemaLocation
+ }}
+ );
+ // add in optional name
+ if(sld.name) {
+ this.writeNode(root, "Name", sld.name);
+ }
+ // add in optional title
+ if(sld.title) {
+ this.writeNode(root, "Title", sld.title);
+ }
+ // add in optional description
+ if(sld.description) {
+ this.writeNode(root, "Abstract", sld.description);
+ }
+ // add in named layers
+ for(var name in sld.namedLayers) {
+ this.writeNode(root, "NamedLayer", sld.namedLayers[name]);
+ }
+ return root;
+ },
+ "Name": function(name) {
+ return this.createElementNSPlus("Name", {value: name});
+ },
+ "Title": function(title) {
+ return this.createElementNSPlus("Title", {value: title});
+ },
+ "Abstract": function(description) {
+ return this.createElementNSPlus(
+ "Abstract", {value: description}
+ );
+ },
+ "NamedLayer": function(layer) {
+ var node = this.createElementNSPlus("NamedLayer");
+
+ // add in required name
+ this.writeNode(node, "Name", layer.name);
+
+ // optional sld:LayerFeatureConstraints here
+
+ // add in named styles
+ if(layer.namedStyles) {
+ for(var i=0, len=layer.namedStyles.length; i<len; ++i) {
+ this.writeNode(
+ node, "NamedStyle", layer.namedStyles[i]
+ );
+ }
+ }
+
+ // add in user styles
+ if(layer.userStyles) {
+ for(var i=0, len=layer.userStyles.length; i<len; ++i) {
+ this.writeNode(
+ node, "UserStyle", layer.userStyles[i]
+ );
+ }
+ }
+
+ return node;
+ },
+ "NamedStyle": function(name) {
+ var node = this.createElementNSPlus("NamedStyle");
+ this.writeNode(node, "Name", name);
+ return node;
+ },
+ "UserStyle": function(style) {
+ var node = this.createElementNSPlus("UserStyle");
+
+ // add in optional name
+ if(style.name) {
+ this.writeNode(node, "Name", style.name);
+ }
+ // add in optional title
+ if(style.title) {
+ this.writeNode(node, "Title", style.title);
+ }
+ // add in optional description
+ if(style.description) {
+ this.writeNode(node, "Abstract", style.description);
+ }
+
+ // add isdefault
+ if(style.isDefault) {
+ this.writeNode(node, "IsDefault", style.isDefault);
+ }
+
+ // add FeatureTypeStyles
+ this.writeNode(node, "FeatureTypeStyle", style);
+
+ return node;
+ },
+ "IsDefault": function(bool) {
+ return this.createElementNSPlus(
+ "IsDefault", {value: (bool) ? "1" : "0"}
+ );
+ },
+ "FeatureTypeStyle": function(style) {
+ var node = this.createElementNSPlus("FeatureTypeStyle");
+
+ // OpenLayers currently stores no Name, Title, Abstract,
+ // FeatureTypeName, or SemanticTypeIdentifier information
+ // related to FeatureTypeStyle
+
+ // add in rules
+ for(var i=0, len=style.rules.length; i<len; ++i) {
+ this.writeNode(node, "Rule", style.rules[i]);
+ }
+
+ return node;
+ },
+ "Rule": function(rule) {
+ var node = this.createElementNSPlus("Rule");
+
+ // add in optional name
+ if(rule.name) {
+ this.writeNode(node, "Name", rule.name);
+ }
+ // add in optional title
+ if(rule.title) {
+ this.writeNode(node, "Title", rule.title);
+ }
+ // add in optional description
+ if(rule.description) {
+ this.writeNode(node, "Abstract", rule.description);
+ }
+
+ // add in LegendGraphic here
+
+ // add in optional filters
+ if(rule.elseFilter) {
+ this.writeNode(node, "ElseFilter");
+ } else if(rule.filter) {
+ this.writeNode(node, "ogc:Filter", rule.filter);
+ }
+
+ // add in scale limits
+ if(rule.minScaleDenominator != undefined) {
+ this.writeNode(
+ node, "MinScaleDenominator", rule.minScaleDenominator
+ );
+ }
+ if(rule.maxScaleDenominator != undefined) {
+ this.writeNode(
+ node, "MaxScaleDenominator", rule.maxScaleDenominator
+ );
+ }
+
+ // add in symbolizers (relies on geometry type keys)
+ var types = OpenLayers.Style.SYMBOLIZER_PREFIXES;
+ var type, symbolizer;
+ for(var i=0, len=types.length; i<len; ++i) {
+ type = types[i];
+ symbolizer = rule.symbolizer[type];
+ if(symbolizer) {
+ this.writeNode(
+ node, type + "Symbolizer", symbolizer
+ );
+ }
+ }
+ return node;
+
+ },
+ "ElseFilter": function() {
+ return this.createElementNSPlus("ElseFilter");
+ },
+ "MinScaleDenominator": function(scale) {
+ return this.createElementNSPlus(
+ "MinScaleDenominator", {value: scale}
+ );
+ },
+ "MaxScaleDenominator": function(scale) {
+ return this.createElementNSPlus(
+ "MaxScaleDenominator", {value: scale}
+ );
+ },
+ "LineSymbolizer": function(symbolizer) {
+ var node = this.createElementNSPlus("LineSymbolizer");
+ this.writeNode(node, "Stroke", symbolizer);
+ return node;
+ },
+ "Stroke": function(symbolizer) {
+ var node = this.createElementNSPlus("Stroke");
+
+ // GraphicFill here
+ // GraphicStroke here
+
+ // add in CssParameters
+ if(symbolizer.strokeColor != undefined) {
+ this.writeNode(
+ node, "CssParameter",
+ {symbolizer: symbolizer, key: "strokeColor"}
+ );
+ }
+ if(symbolizer.strokeOpacity != undefined) {
+ this.writeNode(
+ node, "CssParameter",
+ {symbolizer: symbolizer, key: "strokeOpacity"}
+ );
+ }
+ if(symbolizer.strokeWidth != undefined) {
+ this.writeNode(
+ node, "CssParameter",
+ {symbolizer: symbolizer, key: "strokeWidth"}
+ );
+ }
+ return node;
+ },
+ "CssParameter": function(obj) {
+ // not handling ogc:expressions for now
+ return this.createElementNSPlus("CssParameter", {
+ attributes: {name: this.getCssProperty(obj.key)},
+ value: obj.symbolizer[obj.key]
+ });
+ },
+ "TextSymbolizer": function(symbolizer) {
+ var node = this.createElementNSPlus("TextSymbolizer");
+ // add in optional Label
+ if(symbolizer.label != null) {
+ this.writeNode(node, "Label", symbolizer.label);
+ }
+ // add in optional Font
+ if(symbolizer.fontFamily != null ||
+ symbolizer.fontSize != null) {
+ this.writeNode(node, "Font", symbolizer);
+ }
+ // add in optional Fill
+ if(symbolizer.fillColor != null ||
+ symbolizer.fillOpacity != null) {
+ this.writeNode(node, "Fill", symbolizer);
+ }
+ return node;
+ },
+ "Font": function(symbolizer) {
+ var node = this.createElementNSPlus("Font");
+ // add in CssParameters
+ if(symbolizer.fontFamily) {
+ this.writeNode(
+ node, "CssParameter",
+ {symbolizer: symbolizer, key: "fontFamily"}
+ );
+ }
+ if(symbolizer.fontSize) {
+ this.writeNode(
+ node, "CssParameter",
+ {symbolizer: symbolizer, key: "fontSize"}
+ );
+ }
+ return node;
+ },
+ "Label": function(label) {
+ // only the simplest of ogc:expression handled
+ // {label: "some text and a ${propertyName}"}
+ var node = this.createElementNSPlus("Label");
+ var tokens = label.split("${");
+ node.appendChild(this.createTextNode(tokens[0]));
+ var item, last;
+ for(var i=1, len=tokens.length; i<len; i++) {
+ item = tokens[i];
+ last = item.indexOf("}");
+ if(last > 0) {
+ this.writeNode(
+ node, "ogc:PropertyName",
+ {property: item.substring(0, last)}
+ );
+ node.appendChild(
+ this.createTextNode(item.substring(++last))
+ );
+ } else {
+ // no ending }, so this is a literal ${
+ node.appendChild(
+ this.createTextNode("${" + item)
+ );
+ }
+ }
+ return node;
+ },
+ "PolygonSymbolizer": function(symbolizer) {
+ var node = this.createElementNSPlus("PolygonSymbolizer");
+ this.writeNode(node, "Fill", symbolizer);
+ this.writeNode(node, "Stroke", symbolizer);
+ return node;
+ },
+ "Fill": function(symbolizer) {
+ var node = this.createElementNSPlus("Fill");
+
+ // GraphicFill here
+
+ // add in CssParameters
+ if(symbolizer.fillColor) {
+ this.writeNode(
+ node, "CssParameter",
+ {symbolizer: symbolizer, key: "fillColor"}
+ );
+ }
+ if(symbolizer.fillOpacity) {
+ this.writeNode(
+ node, "CssParameter",
+ {symbolizer: symbolizer, key: "fillOpacity"}
+ );
+ }
+ return node;
+ },
+ "PointSymbolizer": function(symbolizer) {
+ var node = this.createElementNSPlus("PointSymbolizer");
+ this.writeNode(node, "Graphic", symbolizer);
+ return node;
+ },
+ "Graphic": function(symbolizer) {
+ var node = this.createElementNSPlus("Graphic");
+ if(symbolizer.externalGraphic != undefined) {
+ this.writeNode(node, "ExternalGraphic", symbolizer);
+ } else if(symbolizer.graphicName) {
+ this.writeNode(node, "Mark", symbolizer);
+ }
+
+ if(symbolizer.graphicOpacity != undefined) {
+ this.writeNode(node, "Opacity", symbolizer.graphicOpacity);
+ }
+ if(symbolizer.pointRadius != undefined) {
+ this.writeNode(node, "Size", symbolizer.pointRadius * 2);
+ }
+ if(symbolizer.rotation != undefined) {
+ this.writeNode(node, "Rotation", symbolizer.rotation);
+ }
+ return node;
+ },
+ "ExternalGraphic": function(symbolizer) {
+ var node = this.createElementNSPlus("ExternalGraphic");
+ this.writeNode(
+ node, "OnlineResource", symbolizer.externalGraphic
+ );
+ var format = symbolizer.graphicFormat ||
+ this.getGraphicFormat(symbolizer.externalGraphic);
+ this.writeNode(node, "Format", format);
+ return node;
+ },
+ "Mark": function(symbolizer) {
+ var node = this.createElementNSPlus("Mark");
+ this.writeNode(node, "WellKnownName", symbolizer.graphicName);
+ this.writeNode(node, "Fill", symbolizer);
+ this.writeNode(node, "Stroke", symbolizer);
+ return node;
+ },
+ "WellKnownName": function(name) {
+ return this.createElementNSPlus("WellKnownName", {
+ value: name
+ });
+ },
+ "Opacity": function(value) {
+ return this.createElementNSPlus("Opacity", {
+ value: value
+ });
+ },
+ "Size": function(value) {
+ return this.createElementNSPlus("Size", {
+ value: value
+ });
+ },
+ "Rotation": function(value) {
+ return this.createElementNSPlus("Rotation", {
+ value: value
+ });
+ },
+ "OnlineResource": function(href) {
+ return this.createElementNSPlus("OnlineResource", {
+ attributes: {
+ "xlink:type": "simple",
+ "xlink:href": href
+ }
+ });
+ },
+ "Format": function(format) {
+ return this.createElementNSPlus("Format", {
+ value: format
+ });
+ }
+ }
+ },
+
+ /**
+ * Methods below this point are of general use for versioned XML parsers.
+ * These are candidates for an abstract class.
+ */
+
+ /**
+ * Method: getNamespacePrefix
+ * Get the namespace prefix for a given uri from the <namespaces> object.
+ *
+ * Returns:
+ * {String} A namespace prefix or null if none found.
+ */
+ getNamespacePrefix: function(uri) {
+ var prefix = null;
+ if(uri == null) {
+ prefix = this.namespaces[this.defaultPrefix];
+ } else {
+ var gotPrefix = false;
+ for(prefix in this.namespaces) {
+ if(this.namespaces[prefix] == uri) {
+ gotPrefix = true;
+ break;
+ }
+ }
+ if(!gotPrefix) {
+ prefix = null;
+ }
+ }
+ return prefix;
+ },
+
+
+ /**
+ * Method: readChildNodes
+ */
+ readChildNodes: function(node, obj) {
+ var children = node.childNodes;
+ var child, group, reader, prefix, local;
+ for(var i=0, len=children.length; i<len; ++i) {
+ child = children[i];
+ if(child.nodeType == 1) {
+ prefix = this.getNamespacePrefix(child.namespaceURI);
+ local = child.nodeName.split(":").pop();
+ group = this.readers[prefix];
+ if(group) {
+ reader = group[local];
+ if(reader) {
+ reader.apply(this, [child, obj]);
+ }
+ }
+ }
+ }
+ },
+
+ /**
+ * Method: writeNode
+ * Shorthand for applying one of the named writers and appending the
+ * results to a node. If a qualified name is not provided for the
+ * second argument (and a local name is used instead), the namespace
+ * of the parent node will be assumed.
+ *
+ * Parameters:
+ * parent - {DOMElement} Result will be appended to this node.
+ * name - {String} The name of a node to generate. If a qualified name
+ * (e.g. "pre:Name") is used, the namespace prefix is assumed to be
+ * in the <writers> group. If a local name is used (e.g. "Name") then
+ * the namespace of the parent is assumed.
+ * obj - {Object} Structure containing data for the writer.
+ *
+ * Returns:
+ * {DOMElement} The child node.
+ */
+ writeNode: function(parent, name, obj) {
+ var prefix, local;
+ var split = name.indexOf(":");
+ if(split > 0) {
+ prefix = name.substring(0, split);
+ local = name.substring(split + 1);
+ } else {
+ prefix = this.getNamespacePrefix(parent.namespaceURI);
+ local = name;
+ }
+ var child = this.writers[prefix][local].apply(this, [obj]);
+ parent.appendChild(child);
+ return child;
+ },
+
+ /**
+ * Method: createElementNSPlus
+ * Shorthand for creating namespaced elements with optional attributes and
+ * child text nodes.
+ *
+ * Parameters:
+ * name - {String} The qualified node name.
+ * options - {Object} Optional object for node configuration.
+ *
+ * Returns:
+ * {Element} An element node.
+ */
+ createElementNSPlus: function(name, options) {
+ options = options || {};
+ var loc = name.indexOf(":");
+ // order of prefix preference
+ // 1. in the uri option
+ // 2. in the prefix option
+ // 3. in the qualified name
+ // 4. from the defaultPrefix
+ var uri = options.uri || this.namespaces[options.prefix];
+ if(!uri) {
+ loc = name.indexOf(":");
+ uri = this.namespaces[name.substring(0, loc)];
+ }
+ if(!uri) {
+ uri = this.namespaces[this.defaultPrefix];
+ }
+ var node = this.createElementNS(uri, name);
+ if(options.attributes) {
+ this.setAttributes(node, options.attributes);
+ }
+ if(options.value) {
+ node.appendChild(this.createTextNode(options.value));
+ }
+ return node;
+ },
+
+ /**
+ * Method: setAttributes
+ * Set multiple attributes given key value pairs from an object.
+ *
+ * Parameters:
+ * node - {Element} An element node.
+ * obj - {Object || Array} An object whose properties represent attribute
+ * names and values represent attribute values. If an attribute name
+ * is a qualified name ("prefix:local"), the prefix will be looked up
+ * in the parsers {namespaces} object. If the prefix is found,
+ * setAttributeNS will be used instead of setAttribute.
+ */
+ setAttributes: function(node, obj) {
+ var value, loc, alias, uri;
+ for(var name in obj) {
+ value = obj[name].toString();
+ // check for qualified attribute name ("prefix:local")
+ uri = this.namespaces[name.substring(0, name.indexOf(":"))] || null;
+ this.setAttributeNS(node, uri, name, value);
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Format.SLD.v1"
+
+});
+/* ======================================================================
+ OpenLayers/Geometry/Curve.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/MultiPoint.js
+ */
+
+/**
+ * Class: OpenLayers.Geometry.Curve
+ * A Curve is a MultiPoint, whose points are assumed to be connected. To
+ * this end, we provide a "getLength()" function, which iterates through
+ * the points, summing the distances between them.
+ *
+ * Inherits:
+ * - <OpenLayers.Geometry.MultiPoint>
+ */
+OpenLayers.Geometry.Curve = OpenLayers.Class(OpenLayers.Geometry.MultiPoint, {
+
+ /**
+ * 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.Curve
+ *
+ * Parameters:
+ * point - {Array(<OpenLayers.Geometry.Point>)}
+ */
+ initialize: function(points) {
+ OpenLayers.Geometry.MultiPoint.prototype.initialize.apply(this,
+ arguments);
+ },
+
+ /**
+ * APIMethod: getLength
+ *
+ * Returns:
+ * {Float} The length of the curve
+ */
+ getLength: function() {
+ var length = 0.0;
+ if ( this.components && (this.components.length > 1)) {
+ for(var i=1, len=this.components.length; i<len; i++) {
+ length += this.components[i-1].distanceTo(this.components[i]);
+ }
+ }
+ return length;
+ },
+
+ CLASS_NAME: "OpenLayers.Geometry.Curve"
+});
+/* ======================================================================
+ OpenLayers/Format/Filter/v1_0_0.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/Format/Filter/v1.js
+ */
+
+/**
+ * Class: OpenLayers.Format.Filter.v1_0_0
+ * Write ogc:Filter version 1.0.0.
+ *
+ * Inherits from:
+ * - <OpenLayers.Format.Filter.v1>
+ */
+OpenLayers.Format.Filter.v1_0_0 = OpenLayers.Class(
+ OpenLayers.Format.Filter.v1, {
+
+ /**
+ * Constant: VERSION
+ * {String} 1.0.0
+ */
+ VERSION: "1.0.0",
+
+ /**
+ * Property: schemaLocation
+ * {String} http://www.opengis.net/ogc/filter/1.0.0/filter.xsd
+ */
+ schemaLocation: "http://www.opengis.net/ogc/filter/1.0.0/filter.xsd",
+
+ /**
+ * Constructor: OpenLayers.Format.Filter.v1_0_0
+ * Instances of this class are not created directly. Use the
+ * <OpenLayers.Format.Filter> constructor instead.
+ *
+ * Parameters:
+ * options - {Object} An optional object whose properties will be set on
+ * this instance.
+ */
+ initialize: function(options) {
+ OpenLayers.Format.Filter.v1.prototype.initialize.apply(
+ this, [options]
+ );
+ },
+
+ CLASS_NAME: "OpenLayers.Format.Filter.v1_0_0"
+
+});
+/* ======================================================================
+ OpenLayers/Format/SLD/v1_0_0.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/Format/SLD/v1.js
+ */
+
+/**
+ * Class: OpenLayers.Format.SLD.v1_0_0
+ * Write SLD version 1.0.0.
+ *
+ * Inherits from:
+ * - <OpenLayers.Format.SLD.v1>
+ */
+OpenLayers.Format.SLD.v1_0_0 = OpenLayers.Class(
+ OpenLayers.Format.SLD.v1, {
+
+ /**
+ * Constant: VERSION
+ * {String} 1.0.0
+ */
+ VERSION: "1.0.0",
+
+ /**
+ * Property: schemaLocation
+ * {String} http://www.opengis.net/sld
+ * http://schemas.opengis.net/sld/1.0.0/StyledLayerDescriptor.xsd
+ */
+ schemaLocation: "http://www.opengis.net/sld http://schemas.opengis.net/sld/1.0.0/StyledLayerDescriptor.xsd",
+
+ /**
+ * Constructor: OpenLayers.Format.SLD.v1_0_0
+ * Instances of this class are not created directly. Use the
+ * <OpenLayers.Format.SLD> constructor instead.
+ *
+ * Parameters:
+ * options - {Object} An optional object whose properties will be set on
+ * this instance.
+ */
+ initialize: function(options) {
+ OpenLayers.Format.SLD.v1.prototype.initialize.apply(
+ this, [options]
+ );
+ },
+
+ CLASS_NAME: "OpenLayers.Format.SLD.v1_0_0"
+
+});
+/* ======================================================================
+ OpenLayers/Geometry/LineString.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/Curve.js
+ */
+
+/**
+ * Class: OpenLayers.Geometry.LineString
+ * A LineString is a Curve which, once two points have been added to it, can
+ * never be less than two points long.
+ *
+ * Inherits from:
+ * - <OpenLayers.Geometry.Curve>
+ */
+OpenLayers.Geometry.LineString = OpenLayers.Class(OpenLayers.Geometry.Curve, {
+
+ /**
+ * Constructor: OpenLayers.Geometry.LineString
+ * Create a new LineString geometry
+ *
+ * Parameters:
+ * points - {Array(<OpenLayers.Geometry.Point>)} An array of points used to
+ * generate the linestring
+ *
+ */
+ initialize: function(points) {
+ OpenLayers.Geometry.Curve.prototype.initialize.apply(this, arguments);
+ },
+
+ /**
+ * APIMethod: removeComponent
+ * Only allows removal of a point if there are three or more points in
+ * the linestring. (otherwise the result would be just a single point)
+ *
+ * Parameters:
+ * point - {<OpenLayers.Geometry.Point>} The point to be removed
+ */
+ removeComponent: function(point) {
+ if ( this.components && (this.components.length > 2)) {
+ OpenLayers.Geometry.Collection.prototype.removeComponent.apply(this,
+ arguments);
+ }
+ },
+
+ /**
+ * APIMethod: intersects
+ * Test for instersection between two geometries. This is a cheapo
+ * implementation of the Bently-Ottmann algorigithm. It doesn't
+ * really keep track of a sweep line data structure. It is closer
+ * to the brute force method, except that segments are sorted and
+ * potential intersections are only calculated when bounding boxes
+ * intersect.
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry>}
+ *
+ * Returns:
+ * {Boolean} The input geometry intersects this geometry.
+ */
+ intersects: function(geometry) {
+ var intersect = false;
+ var type = geometry.CLASS_NAME;
+ if(type == "OpenLayers.Geometry.LineString" ||
+ type == "OpenLayers.Geometry.LinearRing" ||
+ type == "OpenLayers.Geometry.Point") {
+ var segs1 = this.getSortedSegments();
+ var segs2;
+ if(type == "OpenLayers.Geometry.Point") {
+ segs2 = [{
+ x1: geometry.x, y1: geometry.y,
+ x2: geometry.x, y2: geometry.y
+ }];
+ } else {
+ segs2 = geometry.getSortedSegments();
+ }
+ var seg1, seg1x1, seg1x2, seg1y1, seg1y2,
+ seg2, seg2y1, seg2y2;
+ // sweep right
+ outer: for(var i=0, len=segs1.length; i<len; ++i) {
+ seg1 = segs1[i];
+ seg1x1 = seg1.x1;
+ seg1x2 = seg1.x2;
+ seg1y1 = seg1.y1;
+ seg1y2 = seg1.y2;
+ inner: for(var j=0, jlen=segs2.length; j<jlen; ++j) {
+ seg2 = segs2[j];
+ if(seg2.x1 > seg1x2) {
+ // seg1 still left of seg2
+ break;
+ }
+ if(seg2.x2 < seg1x1) {
+ // seg2 still left of seg1
+ continue;
+ }
+ seg2y1 = seg2.y1;
+ seg2y2 = seg2.y2;
+ if(Math.min(seg2y1, seg2y2) > Math.max(seg1y1, seg1y2)) {
+ // seg2 above seg1
+ continue;
+ }
+ if(Math.max(seg2y1, seg2y2) < Math.min(seg1y1, seg1y2)) {
+ // seg2 below seg1
+ continue;
+ }
+ if(OpenLayers.Geometry.segmentsIntersect(seg1, seg2)) {
+ intersect = true;
+ break outer;
+ }
+ }
+ }
+ } else {
+ intersect = geometry.intersects(this);
+ }
+ return intersect;
+ },
+
+ /**
+ * Method: getSortedSegments
+ *
+ * Returns:
+ * {Array} An array of segment objects. Segment objects have properties
+ * x1, y1, x2, and y2. The start point is represented by x1 and y1.
+ * The end point is represented by x2 and y2. Start and end are
+ * ordered so that x1 < x2.
+ */
+ getSortedSegments: function() {
+ var numSeg = this.components.length - 1;
+ var segments = new Array(numSeg);
+ for(var i=0; i<numSeg; ++i) {
+ point1 = this.components[i];
+ point2 = this.components[i + 1];
+ if(point1.x < point2.x) {
+ segments[i] = {
+ x1: point1.x,
+ y1: point1.y,
+ x2: point2.x,
+ y2: point2.y
+ };
+ } else {
+ segments[i] = {
+ x1: point2.x,
+ y1: point2.y,
+ x2: point1.x,
+ y2: point1.y
+ };
+ }
+ }
+ // more efficient to define this somewhere static
+ function byX1(seg1, seg2) {
+ return seg1.x1 - seg2.x1;
+ }
+ return segments.sort(byX1);
+ },
+
+ CLASS_NAME: "OpenLayers.Geometry.LineString"
+});
+/* ======================================================================
+ OpenLayers/Format/GML.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/Format/XML.js
+ * @requires OpenLayers/Feature/Vector.js
+ * @requires OpenLayers/Geometry/Point.js
+ * @requires OpenLayers/Geometry/MultiPoint.js
+ * @requires OpenLayers/Geometry/LineString.js
+ * @requires OpenLayers/Geometry/MultiLineString.js
+ * @requires OpenLayers/Geometry/Polygon.js
+ * @requires OpenLayers/Geometry/MultiPolygon.js
+ */
+
+/**
+ * Class: OpenLayers.Format.GML
+ * Read/Wite GML. Create a new instance with the <OpenLayers.Format.GML>
+ * constructor. Supports the GML simple features profile.
+ *
+ * Inherits from:
+ * - <OpenLayers.Format>
+ */
+OpenLayers.Format.GML = OpenLayers.Class(OpenLayers.Format.XML, {
+
+ /*
+ * APIProperty: featureNS
+ * {String} Namespace used for feature attributes. Default is
+ * "http://mapserver.gis.umn.edu/mapserver".
+ */
+ featureNS: "http://mapserver.gis.umn.edu/mapserver",
+
+ /**
+ * APIProperty: featurePrefix
+ * {String} Namespace alias (or prefix) for feature nodes. Default is
+ * "feature".
+ */
+ featurePrefix: "feature",
+
+ /*
+ * APIProperty: featureName
+ * {String} Element name for features. Default is "featureMember".
+ */
+ featureName: "featureMember",
+
+ /*
+ * APIProperty: layerName
+ * {String} Name of data layer. Default is "features".
+ */
+ layerName: "features",
+
+ /**
+ * APIProperty: geometryName
+ * {String} Name of geometry element. Defaults to "geometry".
+ */
+ geometryName: "geometry",
+
+ /**
+ * APIProperty: collectionName
+ * {String} Name of featureCollection element.
+ */
+ collectionName: "FeatureCollection",
+
+ /**
+ * APIProperty: gmlns
+ * {String} GML Namespace.
+ */
+ gmlns: "http://www.opengis.net/gml",
+
+ /**
+ * APIProperty: extractAttributes
+ * {Boolean} Extract attributes from GML.
+ */
+ extractAttributes: true,
+
+ /**
+ * APIProperty: xy
+ * {Boolean} Order of the GML coordinate true:(x,y) or false:(y,x)
+ * Changing is not recommended, a new Format should be instantiated.
+ */
+ xy: true,
+
+ /**
+ * Constructor: OpenLayers.Format.GML
+ * Create a new parser for GML.
+ *
+ * Parameters:
+ * options - {Object} An optional object whose properties will be set on
+ * this instance.
+ */
+ initialize: function(options) {
+ // compile regular expressions once instead of every time they are used
+ this.regExes = {
+ trimSpace: (/^\s*|\s*$/g),
+ removeSpace: (/\s*/g),
+ splitSpace: (/\s+/),
+ trimComma: (/\s*,\s*/g)
+ };
+ OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
+ },
+
+ /**
+ * APIMethod: read
+ * Read data from a string, and return a list of features.
+ *
+ * Parameters:
+ * data - {String} or {DOMElement} data to read/parse.
+ *
+ * Returns:
+ * {Array(<OpenLayers.Feature.Vector>)} An array of features.
+ */
+ read: function(data) {
+ if(typeof data == "string") {
+ data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
+ }
+ var featureNodes = this.getElementsByTagNameNS(data.documentElement,
+ this.gmlns,
+ this.featureName);
+ var features = [];
+ for(var i=0; i<featureNodes.length; i++) {
+ var feature = this.parseFeature(featureNodes[i]);
+ if(feature) {
+ features.push(feature);
+ }
+ }
+ return features;
+ },
+
+ /**
+ * Method: parseFeature
+ * This function is the core of the GML parsing code in OpenLayers.
+ * It creates the geometries that are then attached to the returned
+ * feature, and calls parseAttributes() to get attribute data out.
+ *
+ * Parameters:
+ * node - {DOMElement} A GML feature node.
+ */
+ parseFeature: function(node) {
+ // only accept on geometry per feature - look for highest "order"
+ var order = ["MultiPolygon", "Polygon",
+ "MultiLineString", "LineString",
+ "MultiPoint", "Point", "Envelope"];
+ var type, nodeList, geometry, parser;
+ for(var i=0; i<order.length; ++i) {
+ type = order[i];
+ nodeList = this.getElementsByTagNameNS(node, this.gmlns, type);
+ if(nodeList.length > 0) {
+ // only deal with first geometry of this type
+ var parser = this.parseGeometry[type.toLowerCase()];
+ if(parser) {
+ geometry = parser.apply(this, [nodeList[0]]);
+ if (this.internalProjection && this.externalProjection) {
+ geometry.transform(this.externalProjection,
+ this.internalProjection);
+ }
+ } else {
+ OpenLayers.Console.error(OpenLayers.i18n(
+ "unsupportedGeometryType", {'geomType':type}));
+ }
+ // stop looking for different geometry types
+ break;
+ }
+ }
+
+ // construct feature (optionally with attributes)
+ var attributes;
+ if(this.extractAttributes) {
+ attributes = this.parseAttributes(node);
+ }
+ var feature = new OpenLayers.Feature.Vector(geometry, attributes);
+
+ feature.gml = {
+ featureType: node.firstChild.nodeName.split(":")[1],
+ featureNS: node.firstChild.namespaceURI,
+ featureNSPrefix: node.firstChild.prefix
+ };
+
+ // assign fid - this can come from a "fid" or "id" attribute
+ var childNode = node.firstChild;
+ var fid;
+ while(childNode) {
+ if(childNode.nodeType == 1) {
+ fid = childNode.getAttribute("fid") ||
+ childNode.getAttribute("id");
+ if(fid) {
+ break;
+ }
+ }
+ childNode = childNode.nextSibling;
+ }
+ feature.fid = fid;
+ return feature;
+ },
+
+ /**
+ * Property: parseGeometry
+ * Properties of this object are the functions that parse geometries based
+ * on their type.
+ */
+ parseGeometry: {
+
+ /**
+ * Method: parseGeometry.point
+ * Given a GML node representing a point geometry, create an OpenLayers
+ * point geometry.
+ *
+ * Parameters:
+ * node - {DOMElement} A GML node.
+ *
+ * Returns:
+ * {<OpenLayers.Geometry.Point>} A point geometry.
+ */
+ point: function(node) {
+ /**
+ * Three coordinate variations to consider:
+ * 1) <gml:pos>x y z</gml:pos>
+ * 2) <gml:coordinates>x, y, z</gml:coordinates>
+ * 3) <gml:coord><gml:X>x</gml:X><gml:Y>y</gml:Y></gml:coord>
+ */
+ var nodeList, coordString;
+ var coords = [];
+
+ // look for <gml:pos>
+ var nodeList = this.getElementsByTagNameNS(node, this.gmlns, "pos");
+ if(nodeList.length > 0) {
+ coordString = nodeList[0].firstChild.nodeValue;
+ coordString = coordString.replace(this.regExes.trimSpace, "");
+ coords = coordString.split(this.regExes.splitSpace);
+ }
+
+ // look for <gml:coordinates>
+ if(coords.length == 0) {
+ nodeList = this.getElementsByTagNameNS(node, this.gmlns,
+ "coordinates");
+ if(nodeList.length > 0) {
+ coordString = nodeList[0].firstChild.nodeValue;
+ coordString = coordString.replace(this.regExes.removeSpace,
+ "");
+ coords = coordString.split(",");
+ }
+ }
+
+ // look for <gml:coord>
+ if(coords.length == 0) {
+ nodeList = this.getElementsByTagNameNS(node, this.gmlns,
+ "coord");
+ if(nodeList.length > 0) {
+ var xList = this.getElementsByTagNameNS(nodeList[0],
+ this.gmlns, "X");
+ var yList = this.getElementsByTagNameNS(nodeList[0],
+ this.gmlns, "Y");
+ if(xList.length > 0 && yList.length > 0) {
+ coords = [xList[0].firstChild.nodeValue,
+ yList[0].firstChild.nodeValue];
+ }
+ }
+ }
+
+ // preserve third dimension
+ if(coords.length == 2) {
+ coords[2] = null;
+ }
+
+ if (this.xy) {
+ return new OpenLayers.Geometry.Point(coords[0], coords[1],
+ coords[2]);
+ }
+ else{
+ return new OpenLayers.Geometry.Point(coords[1], coords[0],
+ coords[2]);
+ }
+ },
+
+ /**
+ * Method: parseGeometry.multipoint
+ * Given a GML node representing a multipoint geometry, create an
+ * OpenLayers multipoint geometry.
+ *
+ * Parameters:
+ * node - {DOMElement} A GML node.
+ *
+ * Returns:
+ * {<OpenLayers.Geometry.MultiPoint>} A multipoint geometry.
+ */
+ multipoint: function(node) {
+ var nodeList = this.getElementsByTagNameNS(node, this.gmlns,
+ "Point");
+ var components = [];
+ if(nodeList.length > 0) {
+ var point;
+ for(var i=0; i<nodeList.length; ++i) {
+ point = this.parseGeometry.point.apply(this, [nodeList[i]]);
+ if(point) {
+ components.push(point);
+ }
+ }
+ }
+ return new OpenLayers.Geometry.MultiPoint(components);
+ },
+
+ /**
+ * Method: parseGeometry.linestring
+ * Given a GML node representing a linestring geometry, create an
+ * OpenLayers linestring geometry.
+ *
+ * Parameters:
+ * node - {DOMElement} A GML node.
+ *
+ * Returns:
+ * {<OpenLayers.Geometry.LineString>} A linestring geometry.
+ */
+ linestring: function(node, ring) {
+ /**
+ * Two coordinate variations to consider:
+ * 1) <gml:posList dimension="d">x0 y0 z0 x1 y1 z1</gml:posList>
+ * 2) <gml:coordinates>x0, y0, z0 x1, y1, z1</gml:coordinates>
+ */
+ var nodeList, coordString;
+ var coords = [];
+ var points = [];
+
+ // look for <gml:posList>
+ nodeList = this.getElementsByTagNameNS(node, this.gmlns, "posList");
+ if(nodeList.length > 0) {
+ coordString = this.concatChildValues(nodeList[0]);
+ coordString = coordString.replace(this.regExes.trimSpace, "");
+ coords = coordString.split(this.regExes.splitSpace);
+ var dim = parseInt(nodeList[0].getAttribute("dimension"));
+ var j, x, y, z;
+ for(var i=0; i<coords.length/dim; ++i) {
+ j = i * dim;
+ x = coords[j];
+ y = coords[j+1];
+ z = (dim == 2) ? null : coords[j+2];
+ if (this.xy) {
+ points.push(new OpenLayers.Geometry.Point(x, y, z));
+ } else {
+ points.push(new OpenLayers.Geometry.Point(y, x, z));
+ }
+ }
+ }
+
+ // look for <gml:coordinates>
+ if(coords.length == 0) {
+ nodeList = this.getElementsByTagNameNS(node, this.gmlns,
+ "coordinates");
+ if(nodeList.length > 0) {
+ coordString = this.concatChildValues(nodeList[0]);
+ coordString = coordString.replace(this.regExes.trimSpace,
+ "");
+ coordString = coordString.replace(this.regExes.trimComma,
+ ",");
+ var pointList = coordString.split(this.regExes.splitSpace);
+ for(var i=0; i<pointList.length; ++i) {
+ coords = pointList[i].split(",");
+ if(coords.length == 2) {
+ coords[2] = null;
+ }
+ if (this.xy) {
+ points.push(new OpenLayers.Geometry.Point(coords[0],
+ coords[1],
+ coords[2]));
+ } else {
+ points.push(new OpenLayers.Geometry.Point(coords[1],
+ coords[0],
+ coords[2]));
+ }
+ }
+ }
+ }
+
+ var line = null;
+ if(points.length != 0) {
+ if(ring) {
+ line = new OpenLayers.Geometry.LinearRing(points);
+ } else {
+ line = new OpenLayers.Geometry.LineString(points);
+ }
+ }
+ return line;
+ },
+
+ /**
+ * Method: parseGeometry.multilinestring
+ * Given a GML node representing a multilinestring geometry, create an
+ * OpenLayers multilinestring geometry.
+ *
+ * Parameters:
+ * node - {DOMElement} A GML node.
+ *
+ * Returns:
+ * {<OpenLayers.Geometry.MultiLineString>} A multilinestring geometry.
+ */
+ multilinestring: function(node) {
+ var nodeList = this.getElementsByTagNameNS(node, this.gmlns,
+ "LineString");
+ var components = [];
+ if(nodeList.length > 0) {
+ var line;
+ for(var i=0; i<nodeList.length; ++i) {
+ line = this.parseGeometry.linestring.apply(this,
+ [nodeList[i]]);
+ if(line) {
+ components.push(line);
+ }
+ }
+ }
+ return new OpenLayers.Geometry.MultiLineString(components);
+ },
+
+ /**
+ * Method: parseGeometry.polygon
+ * Given a GML node representing a polygon geometry, create an
+ * OpenLayers polygon geometry.
+ *
+ * Parameters:
+ * node - {DOMElement} A GML node.
+ *
+ * Returns:
+ * {<OpenLayers.Geometry.Polygon>} A polygon geometry.
+ */
+ polygon: function(node) {
+ var nodeList = this.getElementsByTagNameNS(node, this.gmlns,
+ "LinearRing");
+ var components = [];
+ if(nodeList.length > 0) {
+ // this assumes exterior ring first, inner rings after
+ var ring;
+ for(var i=0; i<nodeList.length; ++i) {
+ ring = this.parseGeometry.linestring.apply(this,
+ [nodeList[i], true]);
+ if(ring) {
+ components.push(ring);
+ }
+ }
+ }
+ return new OpenLayers.Geometry.Polygon(components);
+ },
+
+ /**
+ * Method: parseGeometry.multipolygon
+ * Given a GML node representing a multipolygon geometry, create an
+ * OpenLayers multipolygon geometry.
+ *
+ * Parameters:
+ * node - {DOMElement} A GML node.
+ *
+ * Returns:
+ * {<OpenLayers.Geometry.MultiPolygon>} A multipolygon geometry.
+ */
+ multipolygon: function(node) {
+ var nodeList = this.getElementsByTagNameNS(node, this.gmlns,
+ "Polygon");
+ var components = [];
+ if(nodeList.length > 0) {
+ var polygon;
+ for(var i=0; i<nodeList.length; ++i) {
+ polygon = this.parseGeometry.polygon.apply(this,
+ [nodeList[i]]);
+ if(polygon) {
+ components.push(polygon);
+ }
+ }
+ }
+ return new OpenLayers.Geometry.MultiPolygon(components);
+ },
+
+ envelope: function(node) {
+ var components = [];
+ var coordString;
+ var envelope;
+
+ var lpoint = this.getElementsByTagNameNS(node, this.gmlns, "lowerCorner");
+ if (lpoint.length > 0) {
+ var coords = [];
+
+ if(lpoint.length > 0) {
+ coordString = lpoint[0].firstChild.nodeValue;
+ coordString = coordString.replace(this.regExes.trimSpace, "");
+ coords = coordString.split(this.regExes.splitSpace);
+ }
+
+ if(coords.length == 2) {
+ coords[2] = null;
+ }
+ if (this.xy) {
+ var lowerPoint = new OpenLayers.Geometry.Point(coords[0], coords[1],coords[2]);
+ } else {
+ var lowerPoint = new OpenLayers.Geometry.Point(coords[1], coords[0],coords[2]);
+ }
+ }
+
+ var upoint = this.getElementsByTagNameNS(node, this.gmlns, "upperCorner");
+ if (upoint.length > 0) {
+ var coords = [];
+
+ if(upoint.length > 0) {
+ coordString = upoint[0].firstChild.nodeValue;
+ coordString = coordString.replace(this.regExes.trimSpace, "");
+ coords = coordString.split(this.regExes.splitSpace);
+ }
+
+ if(coords.length == 2) {
+ coords[2] = null;
+ }
+ if (this.xy) {
+ var upperPoint = new OpenLayers.Geometry.Point(coords[0], coords[1],coords[2]);
+ } else {
+ var upperPoint = new OpenLayers.Geometry.Point(coords[1], coords[0],coords[2]);
+ }
+ }
+
+ if (lowerPoint && upperPoint) {
+ components.push(new OpenLayers.Geometry.Point(lowerPoint.x, lowerPoint.y));
+ components.push(new OpenLayers.Geometry.Point(upperPoint.x, lowerPoint.y));
+ components.push(new OpenLayers.Geometry.Point(upperPoint.x, upperPoint.y));
+ components.push(new OpenLayers.Geometry.Point(lowerPoint.x, upperPoint.y));
+ components.push(new OpenLayers.Geometry.Point(lowerPoint.x, lowerPoint.y));
+
+ var ring = new OpenLayers.Geometry.LinearRing(components);
+ envelope = new OpenLayers.Geometry.Polygon([ring]);
+ }
+ return envelope;
+ }
+ },
+
+ /**
+ * Method: parseAttributes
+ *
+ * Parameters:
+ * node - {<DOMElement>}
+ *
+ * Returns:
+ * {Object} An attributes object.
+ */
+ parseAttributes: function(node) {
+ var attributes = {};
+ // assume attributes are children of the first type 1 child
+ var childNode = node.firstChild;
+ var children, i, child, grandchildren, grandchild, name, value;
+ while(childNode) {
+ if(childNode.nodeType == 1) {
+ // attributes are type 1 children with one type 3 child
+ children = childNode.childNodes;
+ for(i=0; i<children.length; ++i) {
+ child = children[i];
+ if(child.nodeType == 1) {
+ grandchildren = child.childNodes;
+ if(grandchildren.length == 1) {
+ grandchild = grandchildren[0];
+ if(grandchild.nodeType == 3 ||
+ grandchild.nodeType == 4) {
+ name = (child.prefix) ?
+ child.nodeName.split(":")[1] :
+ child.nodeName;
+ value = grandchild.nodeValue.replace(
+ this.regExes.trimSpace, "");
+ attributes[name] = value;
+ }
+ }
+ }
+ }
+ break;
+ }
+ childNode = childNode.nextSibling;
+ }
+ return attributes;
+ },
+
+ /**
+ * APIMethod: write
+ * Generate a GML document string given a list of features.
+ *
+ * Parameters:
+ * features - {Array(<OpenLayers.Feature.Vector>)} List of features to
+ * serialize into a string.
+ *
+ * Returns:
+ * {String} A string representing the GML document.
+ */
+ write: function(features) {
+ if(!(features instanceof Array)) {
+ features = [features];
+ }
+ var gml = this.createElementNS("http://www.opengis.net/wfs",
+ "wfs:" + this.collectionName);
+ for(var i=0; i<features.length; i++) {
+ gml.appendChild(this.createFeatureXML(features[i]));
+ }
+ return OpenLayers.Format.XML.prototype.write.apply(this, [gml]);
+ },
+
+ /**
+ * Method: createFeatureXML
+ * Accept an OpenLayers.Feature.Vector, and build a GML node for it.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>} The feature to be built as GML.
+ *
+ * Returns:
+ * {DOMElement} A node reprensting the feature in GML.
+ */
+ createFeatureXML: function(feature) {
+ var geometry = feature.geometry;
+ var geometryNode = this.buildGeometryNode(geometry);
+ var geomContainer = this.createElementNS(this.featureNS,
+ this.featurePrefix + ":" +
+ this.geometryName);
+ geomContainer.appendChild(geometryNode);
+ var featureNode = this.createElementNS(this.gmlns,
+ "gml:" + this.featureName);
+ var featureContainer = this.createElementNS(this.featureNS,
+ this.featurePrefix + ":" +
+ this.layerName);
+ var fid = feature.fid || feature.id;
+ featureContainer.setAttribute("fid", fid);
+ featureContainer.appendChild(geomContainer);
+ for(var attr in feature.attributes) {
+ var attrText = this.createTextNode(feature.attributes[attr]);
+ var nodename = attr.substring(attr.lastIndexOf(":") + 1);
+ var attrContainer = this.createElementNS(this.featureNS,
+ this.featurePrefix + ":" +
+ nodename);
+ attrContainer.appendChild(attrText);
+ featureContainer.appendChild(attrContainer);
+ }
+ featureNode.appendChild(featureContainer);
+ return featureNode;
+ },
+
+ /**
+ * APIMethod: buildGeometryNode
+ */
+ buildGeometryNode: function(geometry) {
+ if (this.externalProjection && this.internalProjection) {
+ geometry = geometry.clone();
+ geometry.transform(this.internalProjection,
+ this.externalProjection);
+ }
+ var className = geometry.CLASS_NAME;
+ var type = className.substring(className.lastIndexOf(".") + 1);
+ var builder = this.buildGeometry[type.toLowerCase()];
+ return builder.apply(this, [geometry]);
+ },
+
+ /**
+ * Property: buildGeometry
+ * Object containing methods to do the actual geometry node building
+ * based on geometry type.
+ */
+ buildGeometry: {
+ // TBD retrieve the srs from layer
+ // srsName is non-standard, so not including it until it's right.
+ // gml.setAttribute("srsName",
+ // "http://www.opengis.net/gml/srs/epsg.xml#4326");
+
+ /**
+ * Method: buildGeometry.point
+ * Given an OpenLayers point geometry, create a GML point.
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry.Point>} A point geometry.
+ *
+ * Returns:
+ * {DOMElement} A GML point node.
+ */
+ point: function(geometry) {
+ var gml = this.createElementNS(this.gmlns, "gml:Point");
+ gml.appendChild(this.buildCoordinatesNode(geometry));
+ return gml;
+ },
+
+ /**
+ * Method: buildGeometry.multipoint
+ * Given an OpenLayers multipoint geometry, create a GML multipoint.
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry.MultiPoint>} A multipoint geometry.
+ *
+ * Returns:
+ * {DOMElement} A GML multipoint node.
+ */
+ multipoint: function(geometry) {
+ var gml = this.createElementNS(this.gmlns, "gml:MultiPoint");
+ var points = geometry.components;
+ var pointMember, pointGeom;
+ for(var i=0; i<points.length; i++) {
+ pointMember = this.createElementNS(this.gmlns,
+ "gml:pointMember");
+ pointGeom = this.buildGeometry.point.apply(this,
+ [points[i]]);
+ pointMember.appendChild(pointGeom);
+ gml.appendChild(pointMember);
+ }
+ return gml;
+ },
+
+ /**
+ * Method: buildGeometry.linestring
+ * Given an OpenLayers linestring geometry, create a GML linestring.
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry.LineString>} A linestring geometry.
+ *
+ * Returns:
+ * {DOMElement} A GML linestring node.
+ */
+ linestring: function(geometry) {
+ var gml = this.createElementNS(this.gmlns, "gml:LineString");
+ gml.appendChild(this.buildCoordinatesNode(geometry));
+ return gml;
+ },
+
+ /**
+ * Method: buildGeometry.multilinestring
+ * Given an OpenLayers multilinestring geometry, create a GML
+ * multilinestring.
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry.MultiLineString>} A multilinestring
+ * geometry.
+ *
+ * Returns:
+ * {DOMElement} A GML multilinestring node.
+ */
+ multilinestring: function(geometry) {
+ var gml = this.createElementNS(this.gmlns, "gml:MultiLineString");
+ var lines = geometry.components;
+ var lineMember, lineGeom;
+ for(var i=0; i<lines.length; ++i) {
+ lineMember = this.createElementNS(this.gmlns,
+ "gml:lineStringMember");
+ lineGeom = this.buildGeometry.linestring.apply(this,
+ [lines[i]]);
+ lineMember.appendChild(lineGeom);
+ gml.appendChild(lineMember);
+ }
+ return gml;
+ },
+
+ /**
+ * Method: buildGeometry.linearring
+ * Given an OpenLayers linearring geometry, create a GML linearring.
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry.LinearRing>} A linearring geometry.
+ *
+ * Returns:
+ * {DOMElement} A GML linearring node.
+ */
+ linearring: function(geometry) {
+ var gml = this.createElementNS(this.gmlns, "gml:LinearRing");
+ gml.appendChild(this.buildCoordinatesNode(geometry));
+ return gml;
+ },
+
+ /**
+ * Method: buildGeometry.polygon
+ * Given an OpenLayers polygon geometry, create a GML polygon.
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry.Polygon>} A polygon geometry.
+ *
+ * Returns:
+ * {DOMElement} A GML polygon node.
+ */
+ polygon: function(geometry) {
+ var gml = this.createElementNS(this.gmlns, "gml:Polygon");
+ var rings = geometry.components;
+ var ringMember, ringGeom, type;
+ for(var i=0; i<rings.length; ++i) {
+ type = (i==0) ? "outerBoundaryIs" : "innerBoundaryIs";
+ ringMember = this.createElementNS(this.gmlns,
+ "gml:" + type);
+ ringGeom = this.buildGeometry.linearring.apply(this,
+ [rings[i]]);
+ ringMember.appendChild(ringGeom);
+ gml.appendChild(ringMember);
+ }
+ return gml;
+ },
+
+ /**
+ * Method: buildGeometry.multipolygon
+ * Given an OpenLayers multipolygon geometry, create a GML multipolygon.
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry.MultiPolygon>} A multipolygon
+ * geometry.
+ *
+ * Returns:
+ * {DOMElement} A GML multipolygon node.
+ */
+ multipolygon: function(geometry) {
+ var gml = this.createElementNS(this.gmlns, "gml:MultiPolygon");
+ var polys = geometry.components;
+ var polyMember, polyGeom;
+ for(var i=0; i<polys.length; ++i) {
+ polyMember = this.createElementNS(this.gmlns,
+ "gml:polygonMember");
+ polyGeom = this.buildGeometry.polygon.apply(this,
+ [polys[i]]);
+ polyMember.appendChild(polyGeom);
+ gml.appendChild(polyMember);
+ }
+ return gml;
+ }
+ },
+
+ /**
+ * Method: buildCoordinates
+ * builds the coordinates XmlNode
+ * (code)
+ * <gml:coordinates decimal="." cs="," ts=" ">...</gml:coordinates>
+ * (end)
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry>}
+ *
+ * Returns:
+ * {XmlNode} created xmlNode
+ */
+ buildCoordinatesNode: function(geometry) {
+ var coordinatesNode = this.createElementNS(this.gmlns,
+ "gml:coordinates");
+ coordinatesNode.setAttribute("decimal", ".");
+ coordinatesNode.setAttribute("cs", ",");
+ coordinatesNode.setAttribute("ts", " ");
+
+ var points = (geometry.components) ? geometry.components : [geometry];
+ var parts = [];
+ for(var i=0; i<points.length; i++) {
+ parts.push(points[i].x + "," + points[i].y);
+ }
+
+ var txtNode = this.createTextNode(parts.join(" "));
+ coordinatesNode.appendChild(txtNode);
+
+ return coordinatesNode;
+ },
+
+ CLASS_NAME: "OpenLayers.Format.GML"
+});
+/* ======================================================================
+ OpenLayers/Format/GPX.js
+ ====================================================================== */
+
+/* Copyright (c) 2006-2007 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/Format/XML.js
+ * @requires OpenLayers/Feature/Vector.js
+ * @requires OpenLayers/Geometry/Point.js
+ * @requires OpenLayers/Geometry/LineString.js
+ *
+ * Class: OpenLayers.Format.GPX
+ * Read/write GPX parser. Create a new instance with the
+ * <OpenLayers.Format.GPX> constructor.
+ *
+ * Inherits from:
+ * - <OpenLayers.Format.XML>
+ */
+OpenLayers.Format.GPX = OpenLayers.Class(OpenLayers.Format.XML, {
+ /**
+ * APIProperty: extractWaypoints
+ * {Boolean} Extract waypoints from GPX. (default: true)
+ */
+ extractWaypoints: true,
+
+ /**
+ * APIProperty: extractTracks
+ * {Boolean} Extract tracks from GPX. (default: true)
+ */
+ extractTracks: true,
+
+ /**
+ * APIProperty: extractRoutes
+ * {Boolean} Extract routes from GPX. (default: true)
+ */
+ extractRoutes: true,
+
+ /**
+ * APIProperty: extractAttributes
+ * {Boolean} Extract feature attributes from GPX. (default: true)
+ * NOTE: Attributes as part of extensions to the GPX standard may not
+ * be extracted.
+ */
+ extractAttributes: true,
+
+ /**
+ * Constructor: OpenLayers.Format.GPX
+ * Create a new parser for GPX.
+ *
+ * Parameters:
+ * options - {Object} An optional object whose properties will be set on
+ * this instance.
+ */
+ initialize: function(options) {
+ OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
+ },
+
+ /**
+ * APIMethod: read
+ * Return a list of features from a GPX doc
+ *
+ * Parameters:
+ * doc - {Element}
+ *
+ * Returns:
+ * An Array of <OpenLayers.Feature.Vector>s
+ */
+ read: function(doc) {
+ if (typeof doc == "string") {
+ doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc]);
+ }
+ var features = [];
+
+ if(this.extractWaypoints) {
+ var waypoints = doc.getElementsByTagName("wpt");
+ for (var l = 0, len = waypoints.length; l < len; l++) {
+ var attrs = {};
+ if(this.extractAttributes) {
+ attrs = this.parseAttributes(waypoints[l]);
+ }
+ var wpt = new OpenLayers.Geometry.Point(waypoints[l].getAttribute("lon"), waypoints[l].getAttribute("lat"));
+ features.push(new OpenLayers.Feature.Vector(wpt, attrs));
+ }
+ }
+
+ if(this.extractTracks) {
+ var tracks = doc.getElementsByTagName("trk");
+ for (var i=0, len=tracks.length; i<len; i++) {
+ // Attributes are only in trk nodes, not trkseg nodes
+ var attrs = {}
+ if(this.extractAttributes) {
+ attrs = this.parseAttributes(tracks[i]);
+ }
+
+ var segs = this.getElementsByTagNameNS(tracks[i], tracks[i].namespaceURI, "trkseg");
+ for (var j = 0, seglen = segs.length; j < seglen; j++) {
+ // We don't yet support extraction of trkpt attributes
+ // All trksegs of a trk get that trk's attributes
+ var track = this.extractSegment(segs[j], "trkpt");
+ features.push(new OpenLayers.Feature.Vector(track, attrs));
+ }
+ }
+ }
+
+ if(this.extractRoutes) {
+ var routes = doc.getElementsByTagName("rte");
+ for (var k=0, klen=routes.length; k<klen; k++) {
+ var attrs = {}
+ if(this.extractAttributes) {
+ attrs = this.parseAttributes(routes[k]);
+ }
+ var route = this.extractSegment(routes[k], "rtept");
+ features.push(new OpenLayers.Feature.Vector(route, attrs));
+ }
+ }
+
+ if (this.internalProjection && this.externalProjection) {
+ for (var g = 0, featLength = features.length; g < featLength; g++) {
+ features[g].geometry.transform(this.externalProjection,
+ this.internalProjection);
+ }
+ }
+
+ return features;
+ },
+
+ /**
+ * Method: extractSegment
+ *
+ * Parameters:
+ * segment - {<DOMElement>} a trkseg or rte node to parse
+ * segmentType - {String} nodeName of waypoints that form the line
+ *
+ * Returns:
+ * {<OpenLayers.Geometry.LineString>} A linestring geometry
+ */
+ extractSegment: function(segment, segmentType) {
+ var points = this.getElementsByTagNameNS(segment, segment.namespaceURI, segmentType);
+ var point_features = [];
+ for (var i = 0, len = points.length; i < len; i++) {
+ point_features.push(new OpenLayers.Geometry.Point(points[i].getAttribute("lon"), points[i].getAttribute("lat")));
+ }
+ return new OpenLayers.Geometry.LineString(point_features);
+ },
+
+ /**
+ * Method: parseAttributes
+ *
+ * Parameters:
+ * node - {<DOMElement>}
+ *
+ * Returns:
+ * {Object} An attributes object.
+ */
+ parseAttributes: function(node) {
+ // node is either a wpt, trk or rte
+ // attributes are children of the form <attr>value</attr>
+ var attributes = {};
+ var attrNode = node.firstChild;
+ while(attrNode) {
+ if(attrNode.nodeType == 1) {
+ var value = attrNode.firstChild;
+ if(value.nodeType == 3 || value.nodeType == 4) {
+ name = (attrNode.prefix) ?
+ attrNode.nodeName.split(":")[1] :
+ attrNode.nodeName;
+ attributes[name] = value.nodeValue;
+ }
+ }
+ attrNode = attrNode.nextSibling;
+ }
+ return attributes;
+ },
+
+ CLASS_NAME: "OpenLayers.Format.GPX"
+});
+/* ======================================================================
+ OpenLayers/Format/GeoJSON.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/Format/JSON.js
+ * @requires OpenLayers/Feature/Vector.js
+ * @requires OpenLayers/Geometry/Point.js
+ * @requires OpenLayers/Geometry/MultiPoint.js
+ * @requires OpenLayers/Geometry/LineString.js
+ * @requires OpenLayers/Geometry/MultiLineString.js
+ * @requires OpenLayers/Geometry/Polygon.js
+ * @requires OpenLayers/Geometry/MultiPolygon.js
+ */
+
+/**
+ * Class: OpenLayers.Format.GeoJSON
+ * Read and write GeoJSON. Create a new parser with the
+ * <OpenLayers.Format.GeoJSON> constructor.
+ *
+ * Inherits from:
+ * - <OpenLayers.Format.JSON>
+ */
+OpenLayers.Format.GeoJSON = OpenLayers.Class(OpenLayers.Format.JSON, {
+
+ /**
+ * Constructor: OpenLayers.Format.GeoJSON
+ * Create a new parser for GeoJSON.
+ *
+ * Parameters:
+ * options - {Object} An optional object whose properties will be set on
+ * this instance.
+ */
+ initialize: function(options) {
+ OpenLayers.Format.JSON.prototype.initialize.apply(this, [options]);
+ },
+
+ /**
+ * APIMethod: read
+ * Deserialize a GeoJSON string.
+ *
+ * Parameters:
+ * json - {String} A GeoJSON string
+ * type - {String} Optional string that determines the structure of
+ * the output. Supported values are "Geometry", "Feature", and
+ * "FeatureCollection". If absent or null, a default of
+ * "FeatureCollection" is assumed.
+ * filter - {Function} A function which will be called for every key and
+ * value at every level of the final result. Each value will be
+ * replaced by the result of the filter function. This can be used to
+ * reform generic objects into instances of classes, or to transform
+ * date strings into Date objects.
+ *
+ * Returns:
+ * {Object} The return depends on the value of the type argument. If type
+ * is "FeatureCollection" (the default), the return will be an array
+ * of <OpenLayers.Feature.Vector>. If type is "Geometry", the input json
+ * must represent a single geometry, and the return will be an
+ * <OpenLayers.Geometry>. If type is "Feature", the input json must
+ * represent a single feature, and the return will be an
+ * <OpenLayers.Feature.Vector>.
+ */
+ read: function(json, type, filter) {
+ type = (type) ? type : "FeatureCollection";
+ var results = null;
+ var obj = null;
+ if (typeof json == "string") {
+ obj = OpenLayers.Format.JSON.prototype.read.apply(this,
+ [json, filter]);
+ } else {
+ obj = json;
+ }
+ if(!obj) {
+ OpenLayers.Console.error("Bad JSON: " + json);
+ } else if(typeof(obj.type) != "string") {
+ OpenLayers.Console.error("Bad GeoJSON - no type: " + json);
+ } else if(this.isValidType(obj, type)) {
+ switch(type) {
+ case "Geometry":
+ try {
+ results = this.parseGeometry(obj);
+ } catch(err) {
+ OpenLayers.Console.error(err);
+ }
+ break;
+ case "Feature":
+ try {
+ results = this.parseFeature(obj);
+ results.type = "Feature";
+ } catch(err) {
+ OpenLayers.Console.error(err);
+ }
+ break;
+ case "FeatureCollection":
+ // for type FeatureCollection, we allow input to be any type
+ results = [];
+ switch(obj.type) {
+ case "Feature":
+ try {
+ results.push(this.parseFeature(obj));
+ } catch(err) {
+ results = null;
+ OpenLayers.Console.error(err);
+ }
+ break;
+ case "FeatureCollection":
+ for(var i=0, len=obj.features.length; i<len; ++i) {
+ try {
+ results.push(this.parseFeature(obj.features[i]));
+ } catch(err) {
+ results = null;
+ OpenLayers.Console.error(err);
+ }
+ }
+ break;
+ default:
+ try {
+ var geom = this.parseGeometry(obj);
+ results.push(new OpenLayers.Feature.Vector(geom));
+ } catch(err) {
+ results = null;
+ OpenLayers.Console.error(err);
+ }
+ }
+ break;
+ }
+ }
+ return results;
+ },
+
+ /**
+ * Method: isValidType
+ * Check if a GeoJSON object is a valid representative of the given type.
+ *
+ * Returns:
+ * {Boolean} The object is valid GeoJSON object of the given type.
+ */
+ isValidType: function(obj, type) {
+ var valid = false;
+ switch(type) {
+ case "Geometry":
+ if(OpenLayers.Util.indexOf(
+ ["Point", "MultiPoint", "LineString", "MultiLineString",
+ "Polygon", "MultiPolygon", "Box", "GeometryCollection"],
+ obj.type) == -1) {
+ // unsupported geometry type
+ OpenLayers.Console.error("Unsupported geometry type: " +
+ obj.type);
+ } else {
+ valid = true;
+ }
+ break;
+ case "FeatureCollection":
+ // allow for any type to be converted to a feature collection
+ valid = true;
+ break;
+ default:
+ // for Feature types must match
+ if(obj.type == type) {
+ valid = true;
+ } else {
+ OpenLayers.Console.error("Cannot convert types from " +
+ obj.type + " to " + type);
+ }
+ }
+ return valid;
+ },
+
+ /**
+ * Method: parseFeature
+ * Convert a feature object from GeoJSON into an
+ * <OpenLayers.Feature.Vector>.
+ *
+ * Parameters:
+ * obj - {Object} An object created from a GeoJSON object
+ *
+ * Returns:
+ * {<OpenLayers.Feature.Vector>} A feature.
+ */
+ parseFeature: function(obj) {
+ var feature, geometry, attributes;
+ attributes = (obj.properties) ? obj.properties : {};
+ try {
+ geometry = this.parseGeometry(obj.geometry);
+ } catch(err) {
+ // deal with bad geometries
+ throw err;
+ }
+ feature = new OpenLayers.Feature.Vector(geometry, attributes);
+ if(obj.id) {
+ feature.fid = obj.id;
+ }
+ return feature;
+ },
+
+ /**
+ * Method: parseGeometry
+ * Convert a geometry object from GeoJSON into an <OpenLayers.Geometry>.
+ *
+ * Parameters:
+ * obj - {Object} An object created from a GeoJSON object
+ *
+ * Returns:
+ * {<OpenLayers.Geometry>} A geometry.
+ */
+ parseGeometry: function(obj) {
+ if (obj == null) {
+ return null;
+ }
+ var geometry;
+ if(obj.type == "GeometryCollection") {
+ if(!(obj.geometries instanceof Array)) {
+ throw "GeometryCollection must have geometries array: " + obj;
+ }
+ var numGeom = obj.geometries.length;
+ var components = new Array(numGeom);
+ for(var i=0; i<numGeom; ++i) {
+ components[i] = this.parseGeometry.apply(
+ this, [obj.geometries[i]]
+ );
+ }
+ geometry = new OpenLayers.Geometry.Collection(components);
+ } else {
+ if(!(obj.coordinates instanceof Array)) {
+ throw "Geometry must have coordinates array: " + obj;
+ }
+ if(!this.parseCoords[obj.type.toLowerCase()]) {
+ throw "Unsupported geometry type: " + obj.type;
+ }
+ try {
+ geometry = this.parseCoords[obj.type.toLowerCase()].apply(
+ this, [obj.coordinates]
+ );
+ } catch(err) {
+ // deal with bad coordinates
+ throw err;
+ }
+ }
+ if (this.internalProjection && this.externalProjection) {
+ geometry.transform(this.externalProjection,
+ this.internalProjection);
+ }
+ return geometry;
+ },
+
+ /**
+ * Property: parseCoords
+ * Object with properties corresponding to the GeoJSON geometry types.
+ * Property values are functions that do the actual parsing.
+ */
+ parseCoords: {
+ /**
+ * Method: parseCoords.point
+ * Convert a coordinate array from GeoJSON into an
+ * <OpenLayers.Geometry>.
+ *
+ * Parameters:
+ * array - {Object} The coordinates array from the GeoJSON fragment.
+ *
+ * Returns:
+ * {<OpenLayers.Geometry>} A geometry.
+ */
+ "point": function(array) {
+ if(array.length != 2) {
+ throw "Only 2D points are supported: " + array;
+ }
+ return new OpenLayers.Geometry.Point(array[0], array[1]);
+ },
+
+ /**
+ * Method: parseCoords.multipoint
+ * Convert a coordinate array from GeoJSON into an
+ * <OpenLayers.Geometry>.
+ *
+ * Parameters:
+ * array {Object} The coordinates array from the GeoJSON fragment.
+ *
+ * Returns:
+ * {<OpenLayers.Geometry>} A geometry.
+ */
+ "multipoint": function(array) {
+ var points = [];
+ var p = null;
+ for(var i=0, len=array.length; i<len; ++i) {
+ try {
+ p = this.parseCoords["point"].apply(this, [array[i]]);
+ } catch(err) {
+ throw err;
+ }
+ points.push(p);
+ }
+ return new OpenLayers.Geometry.MultiPoint(points);
+ },
+
+ /**
+ * Method: parseCoords.linestring
+ * Convert a coordinate array from GeoJSON into an
+ * <OpenLayers.Geometry>.
+ *
+ * Parameters:
+ * array - {Object} The coordinates array from the GeoJSON fragment.
+ *
+ * Returns:
+ * {<OpenLayers.Geometry>} A geometry.
+ */
+ "linestring": function(array) {
+ var points = [];
+ var p = null;
+ for(var i=0, len=array.length; i<len; ++i) {
+ try {
+ p = this.parseCoords["point"].apply(this, [array[i]]);
+ } catch(err) {
+ throw err;
+ }
+ points.push(p);
+ }
+ return new OpenLayers.Geometry.LineString(points);
+ },
+
+ /**
+ * Method: parseCoords.multilinestring
+ * Convert a coordinate array from GeoJSON into an
+ * <OpenLayers.Geometry>.
+ *
+ * Parameters:
+ * array - {Object} The coordinates array from the GeoJSON fragment.
+ *
+ * Returns:
+ * {<OpenLayers.Geometry>} A geometry.
+ */
+ "multilinestring": function(array) {
+ var lines = [];
+ var l = null;
+ for(var i=0, len=array.length; i<len; ++i) {
+ try {
+ l = this.parseCoords["linestring"].apply(this, [array[i]]);
+ } catch(err) {
+ throw err;
+ }
+ lines.push(l);
+ }
+ return new OpenLayers.Geometry.MultiLineString(lines);
+ },
+
+ /**
+ * Method: parseCoords.polygon
+ * Convert a coordinate array from GeoJSON into an
+ * <OpenLayers.Geometry>.
+ *
+ * Returns:
+ * {<OpenLayers.Geometry>} A geometry.
+ */
+ "polygon": function(array) {
+ var rings = [];
+ var r, l;
+ for(var i=0, len=array.length; i<len; ++i) {
+ try {
+ l = this.parseCoords["linestring"].apply(this, [array[i]]);
+ } catch(err) {
+ throw err;
+ }
+ r = new OpenLayers.Geometry.LinearRing(l.components);
+ rings.push(r);
+ }
+ return new OpenLayers.Geometry.Polygon(rings);
+ },
+
+ /**
+ * Method: parseCoords.multipolygon
+ * Convert a coordinate array from GeoJSON into an
+ * <OpenLayers.Geometry>.
+ *
+ * Parameters:
+ * array - {Object} The coordinates array from the GeoJSON fragment.
+ *
+ * Returns:
+ * {<OpenLayers.Geometry>} A geometry.
+ */
+ "multipolygon": function(array) {
+ var polys = [];
+ var p = null;
+ for(var i=0, len=array.length; i<len; ++i) {
+ try {
+ p = this.parseCoords["polygon"].apply(this, [array[i]]);
+ } catch(err) {
+ throw err;
+ }
+ polys.push(p);
+ }
+ return new OpenLayers.Geometry.MultiPolygon(polys);
+ },
+
+ /**
+ * Method: parseCoords.box
+ * Convert a coordinate array from GeoJSON into an
+ * <OpenLayers.Geometry>.
+ *
+ * Parameters:
+ * array - {Object} The coordinates array from the GeoJSON fragment.
+ *
+ * Returns:
+ * {<OpenLayers.Geometry>} A geometry.
+ */
+ "box": function(array) {
+ if(array.length != 2) {
+ throw "GeoJSON box coordinates must have 2 elements";
+ }
+ return new OpenLayers.Geometry.Polygon([
+ new OpenLayers.Geometry.LinearRing([
+ new OpenLayers.Geometry.Point(array[0][0], array[0][1]),
+ new OpenLayers.Geometry.Point(array[1][0], array[0][1]),
+ new OpenLayers.Geometry.Point(array[1][0], array[1][1]),
+ new OpenLayers.Geometry.Point(array[0][0], array[1][1]),
+ new OpenLayers.Geometry.Point(array[0][0], array[0][1])
+ ])
+ ]);
+ }
+
+ },
+
+ /**
+ * APIMethod: write
+ * Serialize a feature, geometry, array of features into a GeoJSON string.
+ *
+ * Parameters:
+ * obj - {Object} An <OpenLayers.Feature.Vector>, <OpenLayers.Geometry>,
+ * or an array of features.
+ * pretty - {Boolean} Structure the output with newlines and indentation.
+ * Default is false.
+ *
+ * Returns:
+ * {String} The GeoJSON string representation of the input geometry,
+ * features, or array of features.
+ */
+ write: function(obj, pretty) {
+ var geojson = {
+ "type": null
+ };
+ if(obj instanceof Array) {
+ geojson.type = "FeatureCollection";
+ var numFeatures = obj.length;
+ geojson.features = new Array(numFeatures);
+ for(var i=0; i<numFeatures; ++i) {
+ var element = obj[i];
+ if(!element instanceof OpenLayers.Feature.Vector) {
+ var msg = "FeatureCollection only supports collections " +
+ "of features: " + element;
+ throw msg;
+ }
+ geojson.features[i] = this.extract.feature.apply(
+ this, [element]
+ );
+ }
+ } else if (obj.CLASS_NAME.indexOf("OpenLayers.Geometry") == 0) {
+ geojson = this.extract.geometry.apply(this, [obj]);
+ } else if (obj instanceof OpenLayers.Feature.Vector) {
+ geojson = this.extract.feature.apply(this, [obj]);
+ if(obj.layer && obj.layer.projection) {
+ geojson.crs = this.createCRSObject(obj);
+ }
+ }
+ return OpenLayers.Format.JSON.prototype.write.apply(this,
+ [geojson, pretty]);
+ },
+
+ /**
+ * Method: createCRSObject
+ * Create the CRS object for an object.
+ *
+ * Parameters:
+ * object - {<OpenLayers.Feature.Vector>}
+ *
+ * Returns:
+ * {Object} An object which can be assigned to the crs property
+ * of a GeoJSON object.
+ */
+ createCRSObject: function(object) {
+ var proj = object.layer.projection.toString();
+ var crs = {};
+ if (proj.match(/epsg:/i)) {
+ var code = parseInt(proj.substring(proj.indexOf(":") + 1));
+ if (code == 4326) {
+ crs = {
+ "type": "OGC",
+ "properties": {
+ "urn": "urn:ogc:def:crs:OGC:1.3:CRS84"
+ }
+ };
+ } else {
+ crs = {
+ "type": "EPSG",
+ "properties": {
+ "code": code
+ }
+ };
+ }
+ }
+ return crs;
+ },
+
+ /**
+ * Property: extract
+ * Object with properties corresponding to the GeoJSON types.
+ * Property values are functions that do the actual value extraction.
+ */
+ extract: {
+ /**
+ * Method: extract.feature
+ * Return a partial GeoJSON object representing a single feature.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>}
+ *
+ * Returns:
+ * {Object} An object representing the point.
+ */
+ 'feature': function(feature) {
+ var geom = this.extract.geometry.apply(this, [feature.geometry]);
+ return {
+ "type": "Feature",
+ "id": feature.fid == null ? feature.id : feature.fid,
+ "properties": feature.attributes,
+ "geometry": geom
+ };
+ },
+
+ /**
+ * Method: extract.geometry
+ * Return a GeoJSON object representing a single geometry.
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry>}
+ *
+ * Returns:
+ * {Object} An object representing the geometry.
+ */
+ 'geometry': function(geometry) {
+ if (geometry == null) {
+ return null;
+ }
+ if (this.internalProjection && this.externalProjection) {
+ geometry = geometry.clone();
+ geometry.transform(this.internalProjection,
+ this.externalProjection);
+ }
+ var geometryType = geometry.CLASS_NAME.split('.')[2];
+ var data = this.extract[geometryType.toLowerCase()].apply(this, [geometry]);
+ var json;
+ if(geometryType == "Collection") {
+ json = {
+ "type": "GeometryCollection",
+ "geometries": data
+ };
+ } else {
+ json = {
+ "type": geometryType,
+ "coordinates": data
+ };
+ }
+
+ return json;
+ },
+
+ /**
+ * Method: extract.point
+ * Return an array of coordinates from a point.
+ *
+ * Parameters:
+ * point - {<OpenLayers.Geometry.Point>}
+ *
+ * Returns:
+ * {Array} An array of coordinates representing the point.
+ */
+ 'point': function(point) {
+ return [point.x, point.y];
+ },
+
+ /**
+ * Method: extract.multipoint
+ * Return an array of point coordinates from a multipoint.
+ *
+ * Parameters:
+ * multipoint - {<OpenLayers.Geometry.MultiPoint>}
+ *
+ * Returns:
+ * {Array} An array of point coordinate arrays representing
+ * the multipoint.
+ */
+ 'multipoint': function(multipoint) {
+ var array = [];
+ for(var i=0, len=multipoint.components.length; i<len; ++i) {
+ array.push(this.extract.point.apply(this, [multipoint.components[i]]));
+ }
+ return array;
+ },
+
+ /**
+ * Method: extract.linestring
+ * Return an array of coordinate arrays from a linestring.
+ *
+ * Parameters:
+ * linestring - {<OpenLayers.Geometry.LineString>}
+ *
+ * Returns:
+ * {Array} An array of coordinate arrays representing
+ * the linestring.
+ */
+ 'linestring': function(linestring) {
+ var array = [];
+ for(var i=0, len=linestring.components.length; i<len; ++i) {
+ array.push(this.extract.point.apply(this, [linestring.components[i]]));
+ }
+ return array;
+ },
+
+ /**
+ * Method: extract.multilinestring
+ * Return an array of linestring arrays from a linestring.
+ *
+ * Parameters:
+ * linestring - {<OpenLayers.Geometry.MultiLineString>}
+ *
+ * Returns:
+ * {Array} An array of linestring arrays representing
+ * the multilinestring.
+ */
+ 'multilinestring': function(multilinestring) {
+ var array = [];
+ for(var i=0, len=multilinestring.components.length; i<len; ++i) {
+ array.push(this.extract.linestring.apply(this, [multilinestring.components[i]]));
+ }
+ return array;
+ },
+
+ /**
+ * Method: extract.polygon
+ * Return an array of linear ring arrays from a polygon.
+ *
+ * Parameters:
+ * polygon - {<OpenLayers.Geometry.Polygon>}
+ *
+ * Returns:
+ * {Array} An array of linear ring arrays representing the polygon.
+ */
+ 'polygon': function(polygon) {
+ var array = [];
+ for(var i=0, len=polygon.components.length; i<len; ++i) {
+ array.push(this.extract.linestring.apply(this, [polygon.components[i]]));
+ }
+ return array;
+ },
+
+ /**
+ * Method: extract.multipolygon
+ * Return an array of polygon arrays from a multipolygon.
+ *
+ * Parameters:
+ * multipolygon - {<OpenLayers.Geometry.MultiPolygon>}
+ *
+ * Returns:
+ * {Array} An array of polygon arrays representing
+ * the multipolygon
+ */
+ 'multipolygon': function(multipolygon) {
+ var array = [];
+ for(var i=0, len=multipolygon.components.length; i<len; ++i) {
+ array.push(this.extract.polygon.apply(this, [multipolygon.components[i]]));
+ }
+ return array;
+ },
+
+ /**
+ * Method: extract.collection
+ * Return an array of geometries from a geometry collection.
+ *
+ * Parameters:
+ * collection - {<OpenLayers.Geometry.Collection>}
+ *
+ * Returns:
+ * {Array} An array of geometry objects representing the geometry
+ * collection.
+ */
+ 'collection': function(collection) {
+ var len = collection.components.length;
+ var array = new Array(len);
+ for(var i=0; i<len; ++i) {
+ array[i] = this.extract.geometry.apply(
+ this, [collection.components[i]]
+ );
+ }
+ return array;
+ }
+
+
+ },
+
+ CLASS_NAME: "OpenLayers.Format.GeoJSON"
+
+});
+/* ======================================================================
+ OpenLayers/Format/GeoRSS.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/Format/XML.js
+ * @requires OpenLayers/Feature/Vector.js
+ * @requires OpenLayers/Geometry/Point.js
+ * @requires OpenLayers/Geometry/LineString.js
+ * @requires OpenLayers/Geometry/Polygon.js
+ */
+
+/**
+ * Class: OpenLayers.Format.GeoRSS
+ * Read/write GeoRSS parser. Create a new instance with the
+ * <OpenLayers.Format.GeoRSS> constructor.
+ *
+ * Inherits from:
+ * - <OpenLayers.Format.XML>
+ */
+OpenLayers.Format.GeoRSS = OpenLayers.Class(OpenLayers.Format.XML, {
+
+ /**
+ * APIProperty: rssns
+ * {String} RSS namespace to use. Defaults to
+ * "http://backend.userland.com/rss2"
+ */
+ rssns: "http://backend.userland.com/rss2",
+
+ /**
+ * APIProperty: featurens
+ * {String} Feature Attributes namespace. Defaults to
+ * "http://mapserver.gis.umn.edu/mapserver"
+ */
+ featureNS: "http://mapserver.gis.umn.edu/mapserver",
+
+ /**
+ * APIProperty: georssns
+ * {String} GeoRSS namespace to use. Defaults to
+ * "http://www.georss.org/georss"
+ */
+ georssns: "http://www.georss.org/georss",
+
+ /**
+ * APIProperty: geons
+ * {String} W3C Geo namespace to use. Defaults to
+ * "http://www.w3.org/2003/01/geo/wgs84_pos#"
+ */
+ geons: "http://www.w3.org/2003/01/geo/wgs84_pos#",
+
+ /**
+ * APIProperty: featureTitle
+ * {String} Default title for features. Defaults to "Untitled"
+ */
+ featureTitle: "Untitled",
+
+ /**
+ * APIProperty: featureDescription
+ * {String} Default description for features. Defaults to "No Description"
+ */
+ featureDescription: "No Description",
+
+ /**
+ * Property: gmlParse
+ * {Object} GML Format object for parsing features
+ * Non-API and only created if necessary
+ */
+ gmlParser: null,
+
+ /**
+ * APIProperty: xy
+ * {Boolean} Order of the GML coordinate: true:(x,y) or false:(y,x)
+ * For GeoRSS the default is (y,x), therefore: false
+ */
+ xy: false,
+
+ /**
+ * Constructor: OpenLayers.Format.GeoRSS
+ * Create a new parser for GeoRSS.
+ *
+ * Parameters:
+ * options - {Object} An optional object whose properties will be set on
+ * this instance.
+ */
+ initialize: function(options) {
+ OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
+ },
+
+ /**
+ * Method: createGeometryFromItem
+ * Return a geometry from a GeoRSS Item.
+ *
+ * Parameters:
+ * item - {DOMElement} A GeoRSS item node.
+ *
+ * Returns:
+ * {<OpenLayers.Geometry>} A geometry representing the node.
+ */
+ createGeometryFromItem: function(item) {
+ var point = this.getElementsByTagNameNS(item, this.georssns, "point");
+ var lat = this.getElementsByTagNameNS(item, this.geons, 'lat');
+ var lon = this.getElementsByTagNameNS(item, this.geons, 'long');
+
+ var line = this.getElementsByTagNameNS(item,
+ this.georssns,
+ "line");
+ var polygon = this.getElementsByTagNameNS(item,
+ this.georssns,
+ "polygon");
+ var where = this.getElementsByTagNameNS(item,
+ this.georssns,
+ "where");
+ if (point.length > 0 || (lat.length > 0 && lon.length > 0)) {
+ var location;
+ if (point.length > 0) {
+ location = OpenLayers.String.trim(
+ point[0].firstChild.nodeValue).split(/\s+/);
+ if (location.length !=2) {
+ location = OpenLayers.String.trim(
+ point[0].firstChild.nodeValue).split(/\s*,\s*/);
+ }
+ } else {
+ location = [parseFloat(lat[0].firstChild.nodeValue),
+ parseFloat(lon[0].firstChild.nodeValue)];
+ }
+
+ var geometry = new OpenLayers.Geometry.Point(parseFloat(location[1]),
+ parseFloat(location[0]));
+
+ } else if (line.length > 0) {
+ var coords = OpenLayers.String.trim(this.concatChildValues(line[0])).split(/\s+/);
+ var components = [];
+ var point;
+ for (var i=0, len=coords.length; i<len; i+=2) {
+ point = new OpenLayers.Geometry.Point(parseFloat(coords[i+1]),
+ parseFloat(coords[i]));
+ components.push(point);
+ }
+ geometry = new OpenLayers.Geometry.LineString(components);
+ } else if (polygon.length > 0) {
+ var coords = OpenLayers.String.trim(this.concatChildValues(polygon[0])).split(/\s+/);
+ var components = [];
+ var point;
+ for (var i=0, len=coords.length; i<len; i+=2) {
+ point = new OpenLayers.Geometry.Point(parseFloat(coords[i+1]),
+ parseFloat(coords[i]));
+ components.push(point);
+ }
+ geometry = new OpenLayers.Geometry.Polygon([new OpenLayers.Geometry.LinearRing(components)]);
+ } else if (where.length > 0) {
+ if (!this.gmlParser) {
+ this.gmlParser = new OpenLayers.Format.GML({'xy': this.xy});
+ }
+ var feature = this.gmlParser.parseFeature(where[0]);
+ geometry = feature.geometry;
+ }
+
+ if (geometry && this.internalProjection && this.externalProjection) {
+ geometry.transform(this.externalProjection,
+ this.internalProjection);
+ }
+
+ return geometry;
+ },
+
+ /**
+ * Method: createFeatureFromItem
+ * Return a feature from a GeoRSS Item.
+ *
+ * Parameters:
+ * item - {DOMElement} A GeoRSS item node.
+ *
+ * Returns:
+ * {<OpenLayers.Feature.Vector>} A feature representing the item.
+ */
+ createFeatureFromItem: function(item) {
+ var geometry = this.createGeometryFromItem(item);
+
+ /* Provide defaults for title and description */
+ var title = this.getChildValue(item, "*", "title", this.featureTitle);
+
+ /* First try RSS descriptions, then Atom summaries */
+ var description = this.getChildValue(
+ item, "*", "description",
+ this.getChildValue(item, "*", "content", this.featureDescription)
+ );
+
+ /* If no link URL is found in the first child node, try the
+ href attribute */
+ var link = this.getChildValue(item, "*", "link");
+ if(!link) {
+ try {
+ link = this.getElementsByTagNameNS(item, "*", "link")[0].getAttribute("href");
+ } catch(e) {
+ link = null;
+ }
+ }
+
+ var id = this.getChildValue(item, "*", "id", null);
+
+ var data = {
+ "title": title,
+ "description": description,
+ "link": link
+ };
+ var feature = new OpenLayers.Feature.Vector(geometry, data);
+ feature.fid = id;
+ return feature;
+ },
+
+ /**
+ * Method: getChildValue
+ *
+ * Parameters:
+ * node - {DOMElement}
+ * nsuri - {String} Child node namespace uri ("*" for any).
+ * name - {String} Child node name.
+ * def - {String} Optional string default to return if no child found.
+ *
+ * Returns:
+ * {String} The value of the first child with the given tag name. Returns
+ * default value or empty string if none found.
+ */
+ getChildValue: function(node, nsuri, name, def) {
+ var value;
+ var eles = this.getElementsByTagNameNS(node, nsuri, name);
+ if(eles && eles[0] && eles[0].firstChild
+ && eles[0].firstChild.nodeValue) {
+ value = eles[0].firstChild.nodeValue;
+ } else {
+ value = (def == undefined) ? "" : def;
+ }
+ return value;
+ },
+
+ /**
+ * APIMethod: read
+ * Return a list of features from a GeoRSS doc
+
+ * Parameters:
+ * data - {Element}
+ *
+ * Returns:
+ * An Array of <OpenLayers.Feature.Vector>s
+ */
+ read: function(doc) {
+ if (typeof doc == "string") {
+ doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc]);
+ }
+
+ /* Try RSS items first, then Atom entries */
+ var itemlist = null;
+ itemlist = this.getElementsByTagNameNS(doc, '*', 'item');
+ if (itemlist.length == 0) {
+ itemlist = this.getElementsByTagNameNS(doc, '*', 'entry');
+ }
+
+ var numItems = itemlist.length;
+ var features = new Array(numItems);
+ for(var i=0; i<numItems; i++) {
+ features[i] = this.createFeatureFromItem(itemlist[i]);
+ }
+ return features;
+ },
+
+
+ /**
+ * APIMethod: write
+ * Accept Feature Collection, and return a string.
+ *
+ * Parameters:
+ * features - {Array(<OpenLayers.Feature.Vector>)} List of features to serialize into a string.
+ */
+ write: function(features) {
+ var georss;
+ if(features instanceof Array) {
+ georss = this.createElementNS(this.rssns, "rss");
+ for(var i=0, len=features.length; i<len; i++) {
+ georss.appendChild(this.createFeatureXML(features[i]));
+ }
+ } else {
+ georss = this.createFeatureXML(features);
+ }
+ return OpenLayers.Format.XML.prototype.write.apply(this, [georss]);
+ },
+
+ /**
+ * Method: createFeatureXML
+ * Accept an <OpenLayers.Feature.Vector>, and build a geometry for it.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>}
+ *
+ * Returns:
+ * {DOMElement}
+ */
+ createFeatureXML: function(feature) {
+ var geometryNode = this.buildGeometryNode(feature.geometry);
+ var featureNode = this.createElementNS(this.rssns, "item");
+ var titleNode = this.createElementNS(this.rssns, "title");
+ titleNode.appendChild(this.createTextNode(feature.attributes.title ? feature.attributes.title : ""));
+ var descNode = this.createElementNS(this.rssns, "description");
+ descNode.appendChild(this.createTextNode(feature.attributes.description ? feature.attributes.description : ""));
+ featureNode.appendChild(titleNode);
+ featureNode.appendChild(descNode);
+ if (feature.attributes.link) {
+ var linkNode = this.createElementNS(this.rssns, "link");
+ linkNode.appendChild(this.createTextNode(feature.attributes.link));
+ featureNode.appendChild(linkNode);
+ }
+ for(var attr in feature.attributes) {
+ if (attr == "link" || attr == "title" || attr == "description") { continue; }
+ var attrText = this.createTextNode(feature.attributes[attr]);
+ var nodename = attr;
+ if (attr.search(":") != -1) {
+ nodename = attr.split(":")[1];
+ }
+ var attrContainer = this.createElementNS(this.featureNS, "feature:"+nodename);
+ attrContainer.appendChild(attrText);
+ featureNode.appendChild(attrContainer);
+ }
+ featureNode.appendChild(geometryNode);
+ return featureNode;
+ },
+
+ /**
+ * Method: buildGeometryNode
+ * builds a GeoRSS node with a given geometry
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry>}
+ *
+ * Returns:
+ * {DOMElement} A gml node.
+ */
+ buildGeometryNode: function(geometry) {
+ if (this.internalProjection && this.externalProjection) {
+ geometry = geometry.clone();
+ geometry.transform(this.internalProjection,
+ this.externalProjection);
+ }
+ var node;
+ // match Polygon
+ if (geometry.CLASS_NAME == "OpenLayers.Geometry.Polygon") {
+ node = this.createElementNS(this.georssns, 'georss:polygon');
+
+ node.appendChild(this.buildCoordinatesNode(geometry.components[0]));
+ }
+ // match LineString
+ else if (geometry.CLASS_NAME == "OpenLayers.Geometry.LineString") {
+ node = this.createElementNS(this.georssns, 'georss:line');
+
+ node.appendChild(this.buildCoordinatesNode(geometry));
+ }
+ // match Point
+ else if (geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
+ node = this.createElementNS(this.georssns, 'georss:point');
+ node.appendChild(this.buildCoordinatesNode(geometry));
+ } else {
+ throw "Couldn't parse " + geometry.CLASS_NAME;
+ }
+ return node;
+ },
+
+ /**
+ * Method: buildCoordinatesNode
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry>}
+ */
+ buildCoordinatesNode: function(geometry) {
+ var points = null;
+
+ if (geometry.components) {
+ points = geometry.components;
+ }
+
+ var path;
+ if (points) {
+ var numPoints = points.length;
+ var parts = new Array(numPoints);
+ for (var i = 0; i < numPoints; i++) {
+ parts[i] = points[i].y + " " + points[i].x;
+ }
+ path = parts.join(" ");
+ } else {
+ path = geometry.y + " " + geometry.x;
+ }
+ return this.createTextNode(path);
+ },
+
+ CLASS_NAME: "OpenLayers.Format.GeoRSS"
+});
+/* ======================================================================
+ OpenLayers/Format/KML.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/Format/XML.js
+ * @requires OpenLayers/Feature/Vector.js
+ * @requires OpenLayers/Geometry/Point.js
+ * @requires OpenLayers/Geometry/LineString.js
+ * @requires OpenLayers/Geometry/Polygon.js
+ * @requires OpenLayers/Geometry/Collection.js
+ * @requires OpenLayers/Request/XMLHttpRequest.js
+ */
+
+/**
+ * Class: OpenLayers.Format.KML
+ * Read/Wite KML. Create a new instance with the <OpenLayers.Format.KML>
+ * constructor.
+ *
+ * Inherits from:
+ * - <OpenLayers.Format.XML>
+ */
+OpenLayers.Format.KML = OpenLayers.Class(OpenLayers.Format.XML, {
+
+ /**
+ * APIProperty: kmlns
+ * {String} KML Namespace to use. Defaults to 2.0 namespace.
+ */
+ kmlns: "http://earth.google.com/kml/2.0",
+
+ /**
+ * APIProperty: placemarksDesc
+ * {String} Name of the placemarks. Default is "No description available."
+ */
+ placemarksDesc: "No description available",
+
+ /**
+ * APIProperty: foldersName
+ * {String} Name of the folders. Default is "OpenLayers export."
+ */
+ foldersName: "OpenLayers export",
+
+ /**
+ * APIProperty: foldersDesc
+ * {String} Description of the folders. Default is "Exported on [date]."
+ */
+ foldersDesc: "Exported on " + new Date(),
+
+ /**
+ * APIProperty: extractAttributes
+ * {Boolean} Extract attributes from KML. Default is true.
+ * Extracting styleUrls requires this to be set to true
+ */
+ extractAttributes: true,
+
+ /**
+ * Property: extractStyles
+ * {Boolean} Extract styles from KML. Default is false.
+ * Extracting styleUrls also requires extractAttributes to be
+ * set to true
+ */
+ extractStyles: false,
+
+ /**
+ * Property: internalns
+ * {String} KML Namespace to use -- defaults to the namespace of the
+ * Placemark node being parsed, but falls back to kmlns.
+ */
+ internalns: null,
+
+ /**
+ * Property: features
+ * {Array} Array of features
+ *
+ */
+ features: null,
+
+ /**
+ * Property: styles
+ * {Object} Storage of style objects
+ *
+ */
+ styles: null,
+
+ /**
+ * Property: styleBaseUrl
+ * {String}
+ */
+ styleBaseUrl: "",
+
+ /**
+ * Property: fetched
+ * {Object} Storage of KML URLs that have been fetched before
+ * in order to prevent reloading them.
+ */
+ fetched: null,
+
+ /**
+ * APIProperty: maxDepth
+ * {Integer} Maximum depth for recursive loading external KML URLs
+ * Defaults to 0: do no external fetching
+ */
+ maxDepth: 0,
+
+ /**
+ * Constructor: OpenLayers.Format.KML
+ * Create a new parser for KML.
+ *
+ * Parameters:
+ * options - {Object} An optional object whose properties will be set on
+ * this instance.
+ */
+ initialize: function(options) {
+ // compile regular expressions once instead of every time they are used
+ this.regExes = {
+ trimSpace: (/^\s*|\s*$/g),
+ removeSpace: (/\s*/g),
+ splitSpace: (/\s+/),
+ trimComma: (/\s*,\s*/g),
+ kmlColor: (/(\w{2})(\w{2})(\w{2})(\w{2})/),
+ kmlIconPalette: (/root:\/\/icons\/palette-(\d+)(\.\w+)/),
+ straightBracket: (/\$\[(.*?)\]/g)
+ };
+ OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
+ },
+
+ /**
+ * APIMethod: read
+ * Read data from a string, and return a list of features.
+ *
+ * Parameters:
+ * data - {String} or {DOMElement} data to read/parse.
+ *
+ * Returns:
+ * {Array(<OpenLayers.Feature.Vector>)} List of features.
+ */
+ read: function(data) {
+ this.features = [];
+ this.styles = {};
+ this.fetched = {};
+
+ // Set default options
+ var options = {
+ depth: this.maxDepth,
+ styleBaseUrl: this.styleBaseUrl
+ };
+
+ return this.parseData(data, options);
+ },
+
+ /**
+ * Method: parseData
+ * Read data from a string, and return a list of features.
+ *
+ * Parameters:
+ * data - {String} or {DOMElement} data to read/parse.
+ * options - {Object} Hash of options
+ *
+ * Returns:
+ * {Array(<OpenLayers.Feature.Vector>)} List of features.
+ */
+ parseData: function(data, options) {
+ if(typeof data == "string") {
+ data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
+ }
+
+ // Loop throught the following node types in this order and
+ // process the nodes found
+ var types = ["Link", "NetworkLink", "Style", "StyleMap", "Placemark"];
+ for(var i=0, len=types.length; i<len; ++i) {
+ var type = types[i];
+
+ var nodes = this.getElementsByTagNameNS(data, "*", type);
+
+ // skip to next type if no nodes are found
+ if(nodes.length == 0) {
+ continue;
+ }
+
+ switch (type.toLowerCase()) {
+
+ // Fetch external links
+ case "link":
+ case "networklink":
+ this.parseLinks(nodes, options);
+ break;
+
+ // parse style information
+ case "style":
+ if (this.extractStyles) {
+ this.parseStyles(nodes, options);
+ }
+ break;
+ case "stylemap":
+ if (this.extractStyles) {
+ this.parseStyleMaps(nodes, options);
+ }
+ break;
+
+ // parse features
+ case "placemark":
+ this.parseFeatures(nodes, options);
+ break;
+ }
+ }
+
+ return this.features;
+ },
+
+ /**
+ * Method: parseLinks
+ * Finds URLs of linked KML documents and fetches them
+ *
+ * Parameters:
+ * nodes - {Array} of {DOMElement} data to read/parse.
+ * options - {Object} Hash of options
+ *
+ */
+ parseLinks: function(nodes, options) {
+
+ // Fetch external links <NetworkLink> and <Link>
+ // Don't do anything if we have reached our maximum depth for recursion
+ if (options.depth >= this.maxDepth) {
+ return false;
+ }
+
+ // increase depth
+ var newOptions = OpenLayers.Util.extend({}, options);
+ newOptions.depth++;
+
+ for(var i=0, len=nodes.length; i<len; i++) {
+ var href = this.parseProperty(nodes[i], "*", "href");
+ if(href && !this.fetched[href]) {
+ this.fetched[href] = true; // prevent reloading the same urls
+ var data = this.fetchLink(href);
+ if (data) {
+ this.parseData(data, newOptions);
+ }
+ }
+ }
+
+ },
+
+ /**
+ * Method: fetchLink
+ * Fetches a URL and returns the result
+ *
+ * Parameters:
+ * href - {String} url to be fetched
+ *
+ */
+ fetchLink: function(href) {
+ var request = OpenLayers.Request.GET({url: href, async: false});
+ if (request) {
+ return request.responseText;
+ }
+ },
+
+ /**
+ * Method: parseStyles
+ * Looks for <Style> nodes in the data and parses them
+ * Also parses <StyleMap> nodes, but only uses the 'normal' key
+ *
+ * Parameters:
+ * nodes - {Array} of {DOMElement} data to read/parse.
+ * options - {Object} Hash of options
+ *
+ */
+ parseStyles: function(nodes, options) {
+ for(var i=0, len=nodes.length; i<len; i++) {
+ var style = this.parseStyle(nodes[i]);
+ if(style) {
+ styleName = (options.styleBaseUrl || "") + "#" + style.id;
+
+ this.styles[styleName] = style;
+ }
+ }
+ },
+
+ /**
+ * Method: parseStyle
+ * Parses the children of a <Style> node and builds the style hash
+ * accordingly
+ *
+ * Parameters:
+ * node - {DOMElement} <Style> node
+ *
+ */
+ parseStyle: function(node) {
+ var style = {};
+
+ var types = ["LineStyle", "PolyStyle", "IconStyle", "BalloonStyle"];
+ var type, nodeList, geometry, parser;
+ for(var i=0, len=types.length; i<len; ++i) {
+ type = types[i];
+ styleTypeNode = this.getElementsByTagNameNS(node,
+ "*", type)[0];
+ if(!styleTypeNode) {
+ continue;
+ }
+
+ // only deal with first geometry of this type
+ switch (type.toLowerCase()) {
+ case "linestyle":
+ var color = this.parseProperty(styleTypeNode, "*", "color");
+ if (color) {
+ var matches = (color.toString()).match(
+ this.regExes.kmlColor);
+
+ // transparency
+ var alpha = matches[1];
+ style["strokeOpacity"] = parseInt(alpha, 16) / 255;
+
+ // rgb colors (google uses bgr)
+ var b = matches[2];
+ var g = matches[3];
+ var r = matches[4];
+ style["strokeColor"] = "#" + r + g + b;
+ }
+
+ var width = this.parseProperty(styleTypeNode, "*", "width");
+ if (width) {
+ style["strokeWidth"] = width;
+ }
+
+ case "polystyle":
+ var color = this.parseProperty(styleTypeNode, "*", "color");
+ if (color) {
+ var matches = (color.toString()).match(
+ this.regExes.kmlColor);
+
+ // transparency
+ var alpha = matches[1];
+ style["fillOpacity"] = parseInt(alpha, 16) / 255;
+
+ // rgb colors (google uses bgr)
+ var b = matches[2];
+ var g = matches[3];
+ var r = matches[4];
+ style["fillColor"] = "#" + r + g + b;
+ }
+
+ break;
+ case "iconstyle":
+ // set scale
+ var scale = parseFloat(this.parseProperty(styleTypeNode,
+ "*", "scale") || 1);
+
+ // set default width and height of icon
+ var width = 32 * scale;
+ var height = 32 * scale;
+
+ var iconNode = this.getElementsByTagNameNS(styleTypeNode,
+ "*",
+ "Icon")[0];
+ if (iconNode) {
+ var href = this.parseProperty(iconNode, "*", "href");
+ if (href) {
+
+ var w = this.parseProperty(iconNode, "*", "w");
+ var h = this.parseProperty(iconNode, "*", "h");
+
+ // Settings for Google specific icons that are 64x64
+ // We set the width and height to 64 and halve the
+ // scale to prevent icons from being too big
+ var google = "http://maps.google.com/mapfiles/kml";
+ if (OpenLayers.String.startsWith(
+ href, google) && !w && !h) {
+ w = 64;
+ h = 64;
+ scale = scale / 2;
+ }
+
+ // if only dimension is defined, make sure the
+ // other one has the same value
+ w = w || h;
+ h = h || w;
+
+ if (w) {
+ width = parseInt(w) * scale;
+ }
+
+ if (h) {
+ height = parseInt(h) * scale;
+ }
+
+ // support for internal icons
+ // (/root://icons/palette-x.png)
+ // x and y tell the position on the palette:
+ // - in pixels
+ // - starting from the left bottom
+ // We translate that to a position in the list
+ // and request the appropriate icon from the
+ // google maps website
+ var matches = href.match(this.regExes.kmlIconPalette);
+ if (matches) {
+ var palette = matches[1];
+ var file_extension = matches[2];
+
+ var x = this.parseProperty(iconNode, "*", "x");
+ var y = this.parseProperty(iconNode, "*", "y");
+
+ var posX = x ? x/32 : 0;
+ var posY = y ? (7 - y/32) : 7;
+
+ var pos = posY * 8 + posX;
+ href = "http://maps.google.com/mapfiles/kml/pal"
+ + palette + "/icon" + pos + file_extension;
+ }
+
+ style["graphicOpacity"] = 1; // fully opaque
+ style["externalGraphic"] = href;
+ }
+
+ }
+
+
+ // hotSpots define the offset for an Icon
+ var hotSpotNode = this.getElementsByTagNameNS(styleTypeNode,
+ "*",
+ "hotSpot")[0];
+ if (hotSpotNode) {
+ var x = parseFloat(hotSpotNode.getAttribute("x"));
+ var y = parseFloat(hotSpotNode.getAttribute("y"));
+
+ var xUnits = hotSpotNode.getAttribute("xunits");
+ if (xUnits == "pixels") {
+ style["graphicXOffset"] = -x * scale;
+ }
+ else if (xUnits == "insetPixels") {
+ style["graphicXOffset"] = -width + (x * scale);
+ }
+ else if (xUnits == "fraction") {
+ style["graphicXOffset"] = -width * x;
+ }
+
+ var yUnits = hotSpotNode.getAttribute("yunits");
+ if (yUnits == "pixels") {
+ style["graphicYOffset"] = -height + (y * scale) + 1;
+ }
+ else if (yUnits == "insetPixels") {
+ style["graphicYOffset"] = -(y * scale) + 1;
+ }
+ else if (yUnits == "fraction") {
+ style["graphicYOffset"] = -height * (1 - y) + 1;
+ }
+ }
+
+ style["graphicWidth"] = width;
+ style["graphicHeight"] = height;
+ break;
+
+ case "balloonstyle":
+ var balloonStyle = OpenLayers.Util.getXmlNodeValue(
+ styleTypeNode);
+ if (balloonStyle) {
+ style["balloonStyle"] = balloonStyle.replace(
+ this.regExes.straightBracket, "${$1}");
+ }
+ break;
+ default:
+ }
+ }
+
+ // Some polygons have no line color, so we use the fillColor for that
+ if (!style["strokeColor"] && style["fillColor"]) {
+ style["strokeColor"] = style["fillColor"];
+ }
+
+ var id = node.getAttribute("id");
+ if (id && style) {
+ style.id = id;
+ }
+
+ return style;
+ },
+
+ /**
+ * Method: parseStyleMaps
+ * Looks for <Style> nodes in the data and parses them
+ * Also parses <StyleMap> nodes, but only uses the 'normal' key
+ *
+ * Parameters:
+ * nodes - {Array} of {DOMElement} data to read/parse.
+ * options - {Object} Hash of options
+ *
+ */
+ parseStyleMaps: function(nodes, options) {
+ // Only the default or "normal" part of the StyleMap is processed now
+ // To do the select or "highlight" bit, we'd need to change lots more
+
+ for(var i=0, len=nodes.length; i<len; i++) {
+ var node = nodes[i];
+ var pairs = this.getElementsByTagNameNS(node, "*",
+ "Pair");
+
+ var id = node.getAttribute("id");
+ for (var j=0, jlen=pairs.length; j<jlen; j++) {
+ var pair = pairs[j];
+ // Use the shortcut in the SLD format to quickly retrieve the
+ // value of a node. Maybe it's good to have a method in
+ // Format.XML to do this
+ var key = this.parseProperty(pair, "*", "key");
+ var styleUrl = this.parseProperty(pair, "*", "styleUrl");
+
+ if (styleUrl && key == "normal") {
+ this.styles[(options.styleBaseUrl || "") + "#" + id] =
+ this.styles[(options.styleBaseUrl || "") + styleUrl];
+ }
+
+ if (styleUrl && key == "highlight") {
+ // TODO: implement the "select" part
+ }
+
+ }
+ }
+
+ },
+
+
+ /**
+ * Method: parseFeatures
+ * Loop through all Placemark nodes and parse them.
+ * Will create a list of features
+ *
+ * Parameters:
+ * nodes - {Array} of {DOMElement} data to read/parse.
+ * options - {Object} Hash of options
+ *
+ */
+ parseFeatures: function(nodes, options) {
+ var features = new Array(nodes.length);
+ for(var i=0, len=nodes.length; i<len; i++) {
+ var featureNode = nodes[i];
+ var feature = this.parseFeature.apply(this,[featureNode]) ;
+ if(feature) {
+
+ // Create reference to styleUrl
+ if (this.extractStyles && feature.attributes &&
+ feature.attributes.styleUrl) {
+ feature.style = this.getStyle(feature.attributes.styleUrl);
+ }
+
+ if (this.extractStyles) {
+ // Make sure that <Style> nodes within a placemark are
+ // processed as well
+ var inlineStyleNode = this.getElementsByTagNameNS(featureNode,
+ "*",
+ "Style")[0];
+ if (inlineStyleNode) {
+ var inlineStyle= this.parseStyle(inlineStyleNode);
+ if (inlineStyle) {
+ feature.style = OpenLayers.Util.extend(
+ feature.style, inlineStyle
+ );
+ }
+ }
+ }
+
+ // add feature to list of features
+ features[i] = feature;
+ } else {
+ throw "Bad Placemark: " + i;
+ }
+ }
+
+ // add new features to existing feature list
+ this.features = this.features.concat(features);
+ },
+
+ /**
+ * Method: parseFeature
+ * This function is the core of the KML parsing code in OpenLayers.
+ * It creates the geometries that are then attached to the returned
+ * feature, and calls parseAttributes() to get attribute data out.
+ *
+ * Parameters:
+ * node - {DOMElement}
+ *
+ * Returns:
+ * {<OpenLayers.Feature.Vector>} A vector feature.
+ */
+ parseFeature: function(node) {
+ // only accept one geometry per feature - look for highest "order"
+ var order = ["MultiGeometry", "Polygon", "LineString", "Point"];
+ var type, nodeList, geometry, parser;
+ for(var i=0, len=order.length; i<len; ++i) {
+ type = order[i];
+ this.internalns = node.namespaceURI ?
+ node.namespaceURI : this.kmlns;
+ nodeList = this.getElementsByTagNameNS(node,
+ this.internalns, type);
+ if(nodeList.length > 0) {
+ // only deal with first geometry of this type
+ var parser = this.parseGeometry[type.toLowerCase()];
+ if(parser) {
+ geometry = parser.apply(this, [nodeList[0]]);
+ if (this.internalProjection && this.externalProjection) {
+ geometry.transform(this.externalProjection,
+ this.internalProjection);
+ }
+ } else {
+ OpenLayers.Console.error(OpenLayers.i18n(
+ "unsupportedGeometryType", {'geomType':type}));
+ }
+ // stop looking for different geometry types
+ break;
+ }
+ }
+
+ // construct feature (optionally with attributes)
+ var attributes;
+ if(this.extractAttributes) {
+ attributes = this.parseAttributes(node);
+ }
+ var feature = new OpenLayers.Feature.Vector(geometry, attributes);
+
+ var fid = node.getAttribute("id") || node.getAttribute("name");
+ if(fid != null) {
+ feature.fid = fid;
+ }
+
+ return feature;
+ },
+
+ /**
+ * Method: getStyle
+ * Retrieves a style from a style hash using styleUrl as the key
+ * If the styleUrl doesn't exist yet, we try to fetch it
+ * Internet
+ *
+ * Parameters:
+ * styleUrl - {String} URL of style
+ * options - {Object} Hash of options
+ *
+ * Returns:
+ * {Object} - (reference to) Style hash
+ */
+ getStyle: function(styleUrl, options) {
+
+ var styleBaseUrl = OpenLayers.Util.removeTail(styleUrl);
+
+ var newOptions = OpenLayers.Util.extend({}, options);
+ newOptions.depth++;
+ newOptions.styleBaseUrl = styleBaseUrl;
+
+ // Fetch remote Style URLs (if not fetched before)
+ if (!this.styles[styleUrl]
+ && !OpenLayers.String.startsWith(styleUrl, "#")
+ && newOptions.depth <= this.maxDepth
+ && !this.fetched[styleBaseUrl] ) {
+
+ var data = this.fetchLink(styleBaseUrl);
+ if (data) {
+ this.parseData(data, newOptions);
+ }
+
+ }
+
+ // return requested style
+ var style = this.styles[styleUrl];
+ return style;
+ },
+
+ /**
+ * Property: parseGeometry
+ * Properties of this object are the functions that parse geometries based
+ * on their type.
+ */
+ parseGeometry: {
+
+ /**
+ * Method: parseGeometry.point
+ * Given a KML node representing a point geometry, create an OpenLayers
+ * point geometry.
+ *
+ * Parameters:
+ * node - {DOMElement} A KML Point node.
+ *
+ * Returns:
+ * {<OpenLayers.Geometry.Point>} A point geometry.
+ */
+ point: function(node) {
+ var nodeList = this.getElementsByTagNameNS(node, this.internalns,
+ "coordinates");
+ var coords = [];
+ if(nodeList.length > 0) {
+ var coordString = nodeList[0].firstChild.nodeValue;
+ coordString = coordString.replace(this.regExes.removeSpace, "");
+ coords = coordString.split(",");
+ }
+
+ var point = null;
+ if(coords.length > 1) {
+ // preserve third dimension
+ if(coords.length == 2) {
+ coords[2] = null;
+ }
+ point = new OpenLayers.Geometry.Point(coords[0], coords[1],
+ coords[2]);
+ } else {
+ throw "Bad coordinate string: " + coordString;
+ }
+ return point;
+ },
+
+ /**
+ * Method: parseGeometry.linestring
+ * Given a KML node representing a linestring geometry, create an
+ * OpenLayers linestring geometry.
+ *
+ * Parameters:
+ * node - {DOMElement} A KML LineString node.
+ *
+ * Returns:
+ * {<OpenLayers.Geometry.LineString>} A linestring geometry.
+ */
+ linestring: function(node, ring) {
+ var nodeList = this.getElementsByTagNameNS(node, this.internalns,
+ "coordinates");
+ var line = null;
+ if(nodeList.length > 0) {
+ var coordString = this.concatChildValues(nodeList[0]);
+
+ coordString = coordString.replace(this.regExes.trimSpace,
+ "");
+ coordString = coordString.replace(this.regExes.trimComma,
+ ",");
+ var pointList = coordString.split(this.regExes.splitSpace);
+ var numPoints = pointList.length;
+ var points = new Array(numPoints);
+ var coords, numCoords;
+ for(var i=0; i<numPoints; ++i) {
+ coords = pointList[i].split(",");
+ numCoords = coords.length;
+ if(numCoords > 1) {
+ if(coords.length == 2) {
+ coords[2] = null;
+ }
+ points[i] = new OpenLayers.Geometry.Point(coords[0],
+ coords[1],
+ coords[2]);
+ } else {
+ throw "Bad LineString point coordinates: " +
+ pointList[i];
+ }
+ }
+ if(numPoints) {
+ if(ring) {
+ line = new OpenLayers.Geometry.LinearRing(points);
+ } else {
+ line = new OpenLayers.Geometry.LineString(points);
+ }
+ } else {
+ throw "Bad LineString coordinates: " + coordString;
+ }
+ }
+
+ return line;
+ },
+
+ /**
+ * Method: parseGeometry.polygon
+ * Given a KML node representing a polygon geometry, create an
+ * OpenLayers polygon geometry.
+ *
+ * Parameters:
+ * node - {DOMElement} A KML Polygon node.
+ *
+ * Returns:
+ * {<OpenLayers.Geometry.Polygon>} A polygon geometry.
+ */
+ polygon: function(node) {
+ var nodeList = this.getElementsByTagNameNS(node, this.internalns,
+ "LinearRing");
+ var numRings = nodeList.length;
+ var components = new Array(numRings);
+ if(numRings > 0) {
+ // this assumes exterior ring first, inner rings after
+ var ring;
+ for(var i=0, len=nodeList.length; i<len; ++i) {
+ ring = this.parseGeometry.linestring.apply(this,
+ [nodeList[i], true]);
+ if(ring) {
+ components[i] = ring;
+ } else {
+ throw "Bad LinearRing geometry: " + i;
+ }
+ }
+ }
+ return new OpenLayers.Geometry.Polygon(components);
+ },
+
+ /**
+ * Method: parseGeometry.multigeometry
+ * Given a KML node representing a multigeometry, create an
+ * OpenLayers geometry collection.
+ *
+ * Parameters:
+ * node - {DOMElement} A KML MultiGeometry node.
+ *
+ * Returns:
+ * {<OpenLayers.Geometry.Collection>} A geometry collection.
+ */
+ multigeometry: function(node) {
+ var child, parser;
+ var parts = [];
+ var children = node.childNodes;
+ for(var i=0, len=children.length; i<len; ++i ) {
+ child = children[i];
+ if(child.nodeType == 1) {
+ var type = (child.prefix) ?
+ child.nodeName.split(":")[1] :
+ child.nodeName;
+ var parser = this.parseGeometry[type.toLowerCase()];
+ if(parser) {
+ parts.push(parser.apply(this, [child]));
+ }
+ }
+ }
+ return new OpenLayers.Geometry.Collection(parts);
+ }
+
+ },
+
+ /**
+ * Method: parseAttributes
+ *
+ * Parameters:
+ * node - {DOMElement}
+ *
+ * Returns:
+ * {Object} An attributes object.
+ */
+ parseAttributes: function(node) {
+ var attributes = {};
+ // assume attribute nodes are type 1 children with a type 3 or 4 child
+ var child, grandchildren, grandchild;
+ var children = node.childNodes;
+ for(var i=0, len=children.length; i<len; ++i) {
+ child = children[i];
+ if(child.nodeType == 1) {
+ grandchildren = child.childNodes;
+ if(grandchildren.length == 1 || grandchildren.length == 3) {
+ var grandchild;
+ switch (grandchildren.length) {
+ case 1:
+ grandchild = grandchildren[0];
+ break;
+ case 3:
+ default:
+ grandchild = grandchildren[1];
+ break;
+ }
+ if(grandchild.nodeType == 3 || grandchild.nodeType == 4) {
+ var name = (child.prefix) ?
+ child.nodeName.split(":")[1] :
+ child.nodeName;
+ var value = OpenLayers.Util.getXmlNodeValue(grandchild);
+ if (value) {
+ value = value.replace(this.regExes.trimSpace, "");
+ attributes[name] = value;
+ }
+ }
+ }
+ }
+ }
+ return attributes;
+ },
+
+
+ /**
+ * Method: parseProperty
+ * Convenience method to find a node and return its value
+ *
+ * Parameters:
+ * xmlNode - {<DOMElement>}
+ * namespace - {String} namespace of the node to find
+ * tagName - {String} name of the property to parse
+ *
+ * Returns:
+ * {String} The value for the requested property (defaults to null)
+ */
+ parseProperty: function(xmlNode, namespace, tagName) {
+ var value;
+ var nodeList = this.getElementsByTagNameNS(xmlNode, namespace, tagName);
+ try {
+ value = OpenLayers.Util.getXmlNodeValue(nodeList[0]);
+ } catch(e) {
+ value = null;
+ }
+
+ return value;
+ },
+
+ /**
+ * APIMethod: write
+ * Accept Feature Collection, and return a string.
+ *
+ * Parameters:
+ * features - {Array(<OpenLayers.Feature.Vector>} An array of features.
+ *
+ * Returns:
+ * {String} A KML string.
+ */
+ write: function(features) {
+ if(!(features instanceof Array)) {
+ features = [features];
+ }
+ var kml = this.createElementNS(this.kmlns, "kml");
+ var folder = this.createFolderXML();
+ for(var i=0, len=features.length; i<len; ++i) {
+ folder.appendChild(this.createPlacemarkXML(features[i]));
+ }
+ kml.appendChild(folder);
+ return OpenLayers.Format.XML.prototype.write.apply(this, [kml]);
+ },
+
+ /**
+ * Method: createFolderXML
+ * Creates and returns a KML folder node
+ *
+ * Returns:
+ * {DOMElement}
+ */
+ createFolderXML: function() {
+ // Folder name
+ var folderName = this.createElementNS(this.kmlns, "name");
+ var folderNameText = this.createTextNode(this.foldersName);
+ folderName.appendChild(folderNameText);
+
+ // Folder description
+ var folderDesc = this.createElementNS(this.kmlns, "description");
+ var folderDescText = this.createTextNode(this.foldersDesc);
+ folderDesc.appendChild(folderDescText);
+
+ // Folder
+ var folder = this.createElementNS(this.kmlns, "Folder");
+ folder.appendChild(folderName);
+ folder.appendChild(folderDesc);
+
+ return folder;
+ },
+
+ /**
+ * Method: createPlacemarkXML
+ * Creates and returns a KML placemark node representing the given feature.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>}
+ *
+ * Returns:
+ * {DOMElement}
+ */
+ createPlacemarkXML: function(feature) {
+ // Placemark name
+ var placemarkName = this.createElementNS(this.kmlns, "name");
+ var name = (feature.attributes.name) ?
+ feature.attributes.name : feature.id;
+ placemarkName.appendChild(this.createTextNode(name));
+
+ // Placemark description
+ var placemarkDesc = this.createElementNS(this.kmlns, "description");
+ var desc = (feature.attributes.description) ?
+ feature.attributes.description : this.placemarksDesc;
+ placemarkDesc.appendChild(this.createTextNode(desc));
+
+ // Placemark
+ var placemarkNode = this.createElementNS(this.kmlns, "Placemark");
+ if(feature.fid != null) {
+ placemarkNode.setAttribute("id", feature.fid);
+ }
+ placemarkNode.appendChild(placemarkName);
+ placemarkNode.appendChild(placemarkDesc);
+
+ // Geometry node (Point, LineString, etc. nodes)
+ var geometryNode = this.buildGeometryNode(feature.geometry);
+ placemarkNode.appendChild(geometryNode);
+
+ // TBD - deal with remaining (non name/description) attributes.
+ return placemarkNode;
+ },
+
+ /**
+ * Method: buildGeometryNode
+ * Builds and returns a KML geometry node with the given geometry.
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry>}
+ *
+ * Returns:
+ * {DOMElement}
+ */
+ buildGeometryNode: function(geometry) {
+ if (this.internalProjection && this.externalProjection) {
+ geometry = geometry.clone();
+ geometry.transform(this.internalProjection,
+ this.externalProjection);
+ }
+ var className = geometry.CLASS_NAME;
+ var type = className.substring(className.lastIndexOf(".") + 1);
+ var builder = this.buildGeometry[type.toLowerCase()];
+ var node = null;
+ if(builder) {
+ node = builder.apply(this, [geometry]);
+ }
+ return node;
+ },
+
+ /**
+ * Property: buildGeometry
+ * Object containing methods to do the actual geometry node building
+ * based on geometry type.
+ */
+ buildGeometry: {
+ // TBD: Anybody care about namespace aliases here (these nodes have
+ // no prefixes)?
+
+ /**
+ * Method: buildGeometry.point
+ * Given an OpenLayers point geometry, create a KML point.
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry.Point>} A point geometry.
+ *
+ * Returns:
+ * {DOMElement} A KML point node.
+ */
+ point: function(geometry) {
+ var kml = this.createElementNS(this.kmlns, "Point");
+ kml.appendChild(this.buildCoordinatesNode(geometry));
+ return kml;
+ },
+
+ /**
+ * Method: buildGeometry.multipoint
+ * Given an OpenLayers multipoint geometry, create a KML
+ * GeometryCollection.
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry.Point>} A multipoint geometry.
+ *
+ * Returns:
+ * {DOMElement} A KML GeometryCollection node.
+ */
+ multipoint: function(geometry) {
+ return this.buildGeometry.collection.apply(this, [geometry]);
+ },
+
+ /**
+ * Method: buildGeometry.linestring
+ * Given an OpenLayers linestring geometry, create a KML linestring.
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry.LineString>} A linestring geometry.
+ *
+ * Returns:
+ * {DOMElement} A KML linestring node.
+ */
+ linestring: function(geometry) {
+ var kml = this.createElementNS(this.kmlns, "LineString");
+ kml.appendChild(this.buildCoordinatesNode(geometry));
+ return kml;
+ },
+
+ /**
+ * Method: buildGeometry.multilinestring
+ * Given an OpenLayers multilinestring geometry, create a KML
+ * GeometryCollection.
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry.Point>} A multilinestring geometry.
+ *
+ * Returns:
+ * {DOMElement} A KML GeometryCollection node.
+ */
+ multilinestring: function(geometry) {
+ return this.buildGeometry.collection.apply(this, [geometry]);
+ },
+
+ /**
+ * Method: buildGeometry.linearring
+ * Given an OpenLayers linearring geometry, create a KML linearring.
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry.LinearRing>} A linearring geometry.
+ *
+ * Returns:
+ * {DOMElement} A KML linearring node.
+ */
+ linearring: function(geometry) {
+ var kml = this.createElementNS(this.kmlns, "LinearRing");
+ kml.appendChild(this.buildCoordinatesNode(geometry));
+ return kml;
+ },
+
+ /**
+ * Method: buildGeometry.polygon
+ * Given an OpenLayers polygon geometry, create a KML polygon.
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry.Polygon>} A polygon geometry.
+ *
+ * Returns:
+ * {DOMElement} A KML polygon node.
+ */
+ polygon: function(geometry) {
+ var kml = this.createElementNS(this.kmlns, "Polygon");
+ var rings = geometry.components;
+ var ringMember, ringGeom, type;
+ for(var i=0, len=rings.length; i<len; ++i) {
+ type = (i==0) ? "outerBoundaryIs" : "innerBoundaryIs";
+ ringMember = this.createElementNS(this.kmlns, type);
+ ringGeom = this.buildGeometry.linearring.apply(this,
+ [rings[i]]);
+ ringMember.appendChild(ringGeom);
+ kml.appendChild(ringMember);
+ }
+ return kml;
+ },
+
+ /**
+ * Method: buildGeometry.multipolygon
+ * Given an OpenLayers multipolygon geometry, create a KML
+ * GeometryCollection.
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry.Point>} A multipolygon geometry.
+ *
+ * Returns:
+ * {DOMElement} A KML GeometryCollection node.
+ */
+ multipolygon: function(geometry) {
+ return this.buildGeometry.collection.apply(this, [geometry]);
+ },
+
+ /**
+ * Method: buildGeometry.collection
+ * Given an OpenLayers geometry collection, create a KML MultiGeometry.
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry.Collection>} A geometry collection.
+ *
+ * Returns:
+ * {DOMElement} A KML MultiGeometry node.
+ */
+ collection: function(geometry) {
+ var kml = this.createElementNS(this.kmlns, "MultiGeometry");
+ var child;
+ for(var i=0, len=geometry.components.length; i<len; ++i) {
+ child = this.buildGeometryNode.apply(this,
+ [geometry.components[i]]);
+ if(child) {
+ kml.appendChild(child);
+ }
+ }
+ return kml;
+ }
+ },
+
+ /**
+ * Method: buildCoordinatesNode
+ * Builds and returns the KML coordinates node with the given geometry
+ * <coordinates>...</coordinates>
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry>}
+ *
+ * Return:
+ * {DOMElement}
+ */
+ buildCoordinatesNode: function(geometry) {
+ var coordinatesNode = this.createElementNS(this.kmlns, "coordinates");
+
+ var path;
+ var points = geometry.components;
+ if(points) {
+ // LineString or LinearRing
+ var point;
+ var numPoints = points.length;
+ var parts = new Array(numPoints);
+ for(var i=0; i<numPoints; ++i) {
+ point = points[i];
+ parts[i] = point.x + "," + point.y;
+ }
+ path = parts.join(" ");
+ } else {
+ // Point
+ path = geometry.x + "," + geometry.y;
+ }
+
+ var txtNode = this.createTextNode(path);
+ coordinatesNode.appendChild(txtNode);
+
+ return coordinatesNode;
+ },
+
+ CLASS_NAME: "OpenLayers.Format.KML"
+});
+/* ======================================================================
+ OpenLayers/Format/OSM.js
+ ====================================================================== */
+
+/* Copyright (c) 2006-2007 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/Format/XML.js
+ * @requires OpenLayers/Feature/Vector.js
+ * @requires OpenLayers/Geometry/Point.js
+ * @requires OpenLayers/Geometry/LineString.js
+ * @requires OpenLayers/Geometry/Polygon.js
+ */
+
+/**
+ * Class: OpenLayers.Format.OSM
+ * OSM parser. Create a new instance with the
+ * <OpenLayers.Format.OSM> constructor.
+ *
+ * Inherits from:
+ * - <OpenLayers.Format.XML>
+ */
+OpenLayers.Format.OSM = OpenLayers.Class(OpenLayers.Format.XML, {
+
+ /**
+ * APIProperty: checkTags
+ * {Boolean} Should tags be checked to determine whether something
+ * should be treated as a seperate node. Will slow down parsing.
+ * Default is false.
+ */
+ checkTags: false,
+
+ /**
+ * Property: interestingTagsExclude
+ * {Array} List of tags to exclude from 'interesting' checks on nodes.
+ * Must be set when creating the format. Will only be used if checkTags
+ * is set.
+ */
+ interestingTagsExclude: null,
+
+ /**
+ * APIProperty: areaTags
+ * {Array} List of tags indicating that something is an area.
+ * Must be set when creating the format. Will only be used if
+ * checkTags is true.
+ */
+ areaTags: null,
+
+ /**
+ * Constructor: OpenLayers.Format.OSM
+ * Create a new parser for OSM.
+ *
+ * Parameters:
+ * options - {Object} An optional object whose properties will be set on
+ * this instance.
+ */
+ initialize: function(options) {
+ var layer_defaults = {
+ 'interestingTagsExclude': ['source', 'source_ref',
+ 'source:ref', 'history', 'attribution', 'created_by'],
+ 'areaTags': ['area', 'building', 'leisure', 'tourism', 'ruins',
+ 'historic', 'landuse', 'military', 'natural', 'sport']
+ };
+
+ layer_defaults = OpenLayers.Util.extend(layer_defaults, options);
+
+ var interesting = {};
+ for (var i = 0; i < layer_defaults.interestingTagsExclude.length; i++) {
+ interesting[layer_defaults.interestingTagsExclude[i]] = true;
+ }
+ layer_defaults.interestingTagsExclude = interesting;
+
+ var area = {};
+ for (var i = 0; i < layer_defaults.areaTags.length; i++) {
+ area[layer_defaults.areaTags[i]] = true;
+ }
+ layer_defaults.areaTags = area;
+
+ OpenLayers.Format.XML.prototype.initialize.apply(this, [layer_defaults]);
+ },
+
+ /**
+ * APIMethod: read
+ * Return a list of features from a OSM doc
+
+ * Parameters:
+ * data - {Element}
+ *
+ * Returns:
+ * An Array of <OpenLayers.Feature.Vector>s
+ */
+ read: function(doc) {
+ if (typeof doc == "string") {
+ doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc]);
+ }
+
+ var nodes = this.getNodes(doc);
+ var ways = this.getWays(doc);
+
+ // Geoms will contain at least ways.length entries.
+ var feat_list = new Array(ways.length);
+
+ for (var i = 0; i < ways.length; i++) {
+ // We know the minimal of this one ahead of time. (Could be -1
+ // due to areas/polygons)
+ var point_list = new Array(ways[i].nodes.length);
+
+ var poly = this.isWayArea(ways[i]) ? 1 : 0;
+ for (var j = 0; j < ways[i].nodes.length; j++) {
+ var node = nodes[ways[i].nodes[j]];
+
+ var point = new OpenLayers.Geometry.Point(node.lon, node.lat);
+
+ // Since OSM is topological, we stash the node ID internally.
+ point.osm_id = parseInt(ways[i].nodes[j]);
+ point_list[j] = point;
+
+ // We don't display nodes if they're used inside other
+ // elements.
+ node.used = true;
+ }
+ var geometry = null;
+ if (poly) {
+ geometry = new OpenLayers.Geometry.Polygon(
+ new OpenLayers.Geometry.LinearRing(point_list));
+ } else {
+ geometry = new OpenLayers.Geometry.LineString(point_list);
+ }
+ if (this.internalProjection && this.externalProjection) {
+ geometry.transform(this.externalProjection,
+ this.internalProjection);
+ }
+ var feat = new OpenLayers.Feature.Vector(geometry,
+ ways[i].tags);
+ feat.osm_id = parseInt(ways[i].id);
+ feat.fid = "way." + feat.osm_id;
+ feat_list[i] = feat;
+ }
+ for (var node_id in nodes) {
+ var node = nodes[node_id];
+ if (!node.used || this.checkTags) {
+ var tags = null;
+
+ if (this.checkTags) {
+ var result = this.getTags(node.node, true);
+ if (node.used && !result[1]) {
+ continue;
+ }
+ tags = result[0];
+ } else {
+ tags = this.getTags(node.node);
+ }
+
+ var feat = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(node['lon'], node['lat']),
+ tags);
+ if (this.internalProjection && this.externalProjection) {
+ feat.geometry.transform(this.externalProjection,
+ this.internalProjection);
+ }
+ feat.osm_id = parseInt(node_id);
+ feat.fid = "node." + feat.osm_id;
+ feat_list.push(feat);
+ }
+ // Memory cleanup
+ node.node = null;
+ }
+ return feat_list;
+ },
+
+ /**
+ * Method: getNodes
+ * Return the node items from a doc.
+ *
+ * Parameters:
+ * node - {DOMElement} node to parse tags from
+ */
+ getNodes: function(doc) {
+ var node_list = doc.getElementsByTagName("node");
+ var nodes = {};
+ for (var i = 0; i < node_list.length; i++) {
+ var node = node_list[i];
+ var id = node.getAttribute("id");
+ nodes[id] = {
+ 'lat': node.getAttribute("lat"),
+ 'lon': node.getAttribute("lon"),
+ 'node': node
+ };
+ }
+ return nodes;
+ },
+
+ /**
+ * Method: getWays
+ * Return the way items from a doc.
+ *
+ * Parameters:
+ * node - {DOMElement} node to parse tags from
+ */
+ getWays: function(doc) {
+ var way_list = doc.getElementsByTagName("way");
+ var return_ways = [];
+ for (var i = 0; i < way_list.length; i++) {
+ var way = way_list[i];
+ var way_object = {
+ id: way.getAttribute("id")
+ };
+
+ way_object.tags = this.getTags(way);
+
+ var node_list = way.getElementsByTagName("nd");
+
+ way_object.nodes = new Array(node_list.length);
+
+ for (var j = 0; j < node_list.length; j++) {
+ way_object.nodes[j] = node_list[j].getAttribute("ref");
+ }
+ return_ways.push(way_object);
+ }
+ return return_ways;
+
+ },
+
+ /**
+ * Method: getTags
+ * Return the tags list attached to a specific DOM element.
+ *
+ * Parameters:
+ * node - {DOMElement} node to parse tags from
+ * interesting_tags - {Boolean} whether the return from this function should
+ * return a boolean indicating that it has 'interesting tags' --
+ * tags like attribution and source are ignored. (To change the list
+ * of tags, see interestingTagsExclude)
+ *
+ * Returns:
+ * tags - {Object} hash of tags
+ * interesting - {Boolean} if interesting_tags is passed, returns
+ * whether there are any interesting tags on this element.
+ */
+ getTags: function(dom_node, interesting_tags) {
+ var tag_list = dom_node.getElementsByTagName("tag");
+ var tags = {};
+ var interesting = false;
+ for (var j = 0; j < tag_list.length; j++) {
+ var key = tag_list[j].getAttribute("k");
+ tags[key] = tag_list[j].getAttribute("v");
+ if (interesting_tags) {
+ if (!this.interestingTagsExclude[key]) {
+ interesting = true;
+ }
+ }
+ }
+ return interesting_tags ? [tags, interesting] : tags;
+ },
+
+ /**
+ * Method: isWayArea
+ * Given a way object from getWays, check whether the tags and geometry
+ * indicate something is an area.
+ *
+ * Returns:
+ * {Boolean}
+ */
+ isWayArea: function(way) {
+ var poly_shaped = false;
+ var poly_tags = false;
+
+ if (way.nodes[0] == way.nodes[way.nodes.length - 1]) {
+ poly_shaped = true;
+ }
+ if (this.checkTags) {
+ for(var key in way.tags) {
+ if (this.areaTags[key]) {
+ poly_tags = true;
+ break;
+ }
+ }
+ }
+ return poly_shaped && (this.checkTags ? poly_tags : true);
+ },
+
+ /**
+ * APIMethod: write
+ * Takes a list of features, returns a serialized OSM format file for use
+ * in tools like JOSM.
+ *
+ * Parameters:
+ * features - {Array(<OpenLayers.Feature.Vector>)}
+ */
+ write: function(features) {
+ if (!(features instanceof Array)) {
+ features = [features];
+ }
+
+ this.osm_id = 1;
+ this.created_nodes = {};
+ var root_node = this.createElementNS(null, "osm");
+ root_node.setAttribute("version", "0.5");
+ root_node.setAttribute("generator", "OpenLayers "+ OpenLayers.VERSION_NUMBER);
+
+ // Loop backwards, because the deserializer puts nodes last, and
+ // we want them first if possible
+ for(var i = features.length - 1; i >= 0; i--) {
+ var nodes = this.createFeatureNodes(features[i]);
+ for (var j = 0; j < nodes.length; j++) {
+ root_node.appendChild(nodes[j]);
+ }
+ }
+ return OpenLayers.Format.XML.prototype.write.apply(this, [root_node]);
+ },
+
+ /**
+ * Method: createFeatureNodes
+ * Takes a feature, returns a list of nodes from size 0->n.
+ * Will include all pieces of the serialization that are required which
+ * have not already been created. Calls out to createXML based on geometry
+ * type.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>}
+ */
+ createFeatureNodes: function(feature) {
+ var nodes = [];
+ var className = feature.geometry.CLASS_NAME;
+ var type = className.substring(className.lastIndexOf(".") + 1);
+ type = type.toLowerCase();
+ var builder = this.createXML[type];
+ if (builder) {
+ nodes = builder.apply(this, [feature]);
+ }
+ return nodes;
+ },
+
+ /**
+ * Method: createXML
+ * Takes a feature, returns a list of nodes from size 0->n.
+ * Will include all pieces of the serialization that are required which
+ * have not already been created.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>}
+ */
+ createXML: {
+ 'point': function(point) {
+ var id = null;
+ var geometry = point.geometry ? point.geometry : point;
+ var already_exists = false; // We don't return anything if the node
+ // has already been created
+ if (point.osm_id) {
+ id = point.osm_id;
+ if (this.created_nodes[id]) {
+ already_exists = true;
+ }
+ } else {
+ id = -this.osm_id;
+ this.osm_id++;
+ }
+ if (already_exists) {
+ node = this.created_nodes[id];
+ } else {
+ var node = this.createElementNS(null, "node");
+ }
+ this.created_nodes[id] = node;
+ node.setAttribute("id", id);
+ node.setAttribute("lon", geometry.x);
+ node.setAttribute("lat", geometry.y);
+ if (point.attributes) {
+ this.serializeTags(point, node);
+ }
+ this.setState(point, node);
+ return already_exists ? [] : [node];
+ },
+ linestring: function(feature) {
+ var nodes = [];
+ var geometry = feature.geometry;
+ if (feature.osm_id) {
+ id = feature.osm_id;
+ } else {
+ id = -this.osm_id;
+ this.osm_id++;
+ }
+ var way = this.createElementNS(null, "way");
+ way.setAttribute("id", id);
+ for (var i = 0; i < geometry.components.length; i++) {
+ var node = this.createXML['point'].apply(this, [geometry.components[i]]);
+ if (node.length) {
+ node = node[0];
+ var node_ref = node.getAttribute("id");
+ nodes.push(node);
+ } else {
+ node_ref = geometry.components[i].osm_id;
+ node = this.created_nodes[node_ref];
+ }
+ this.setState(feature, node);
+ var nd_dom = this.createElementNS(null, "nd");
+ nd_dom.setAttribute("ref", node_ref);
+ way.appendChild(nd_dom);
+ }
+ this.serializeTags(feature, way);
+ nodes.push(way);
+
+ return nodes;
+ },
+ polygon: function(feature) {
+ var attrs = OpenLayers.Util.extend({'area':'yes'}, feature.attributes);
+ var feat = new OpenLayers.Feature.Vector(feature.geometry.components[0], attrs);
+ feat.osm_id = feature.osm_id;
+ return this.createXML['linestring'].apply(this, [feat]);
+ }
+ },
+
+ /**
+ * Method: serializeTags
+ * Given a feature, serialize the attributes onto the given node.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>}
+ * node - {DOMNode}
+ */
+ serializeTags: function(feature, node) {
+ for (var key in feature.attributes) {
+ var tag = this.createElementNS(null, "tag");
+ tag.setAttribute("k", key);
+ tag.setAttribute("v", feature.attributes[key]);
+ node.appendChild(tag);
+ }
+ },
+
+ /**
+ * Method: setState
+ * OpenStreetMap has a convention that 'state' is stored for modification or deletion.
+ * This allows the file to be uploaded via JOSM or the bulk uploader tool.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>}
+ * node - {DOMNode}
+ */
+ setState: function(feature, node) {
+ if (feature.state) {
+ var state = null;
+ switch(feature.state) {
+ case OpenLayers.State.UPDATE:
+ state = "modify";
+ case OpenLayers.State.DELETE:
+ state = "delete";
+ }
+ if (state) {
+ node.setAttribute("action", state);
+ }
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Format.OSM"
+});
+/* ======================================================================
+ 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
+ ====================================================================== */
+
+/* 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/Point.js
+ * @requires OpenLayers/Geometry/Point.js
+ * @requires OpenLayers/Geometry/LineString.js
+ */
+
+/**
+ * Class: OpenLayers.Handler.Path
+ * Handler to draw a path on the map. Path is displayed on mouse down,
+ * moves on mouse move, and is finished on mouse up.
+ *
+ * Inherits from:
+ * - <OpenLayers.Handler.Point>
+ */
+OpenLayers.Handler.Path = OpenLayers.Class(OpenLayers.Handler.Point, {
+
+ /**
+ * Property: line
+ * {<OpenLayers.Feature.Vector>}
+ */
+ line: null,
+
+ /**
+ * Property: freehand
+ * {Boolean} In freehand mode, the handler starts the path on mouse down,
+ * adds a point for every mouse move, and finishes the path on mouse up.
+ * Outside of freehand mode, a point is added to the path on every mouse
+ * click and double-click finishes the path.
+ */
+ freehand: false,
+
+ /**
+ * Property: freehandToggle
+ * {String} If set, freehandToggle is checked on mouse events and will set
+ * the freehand mode to the opposite of this.freehand. To disallow
+ * toggling between freehand and non-freehand mode, set freehandToggle to
+ * null. Acceptable toggle values are 'shiftKey', 'ctrlKey', and 'altKey'.
+ */
+ freehandToggle: 'shiftKey',
+
+ /**
+ * Constructor: OpenLayers.Handler.Path
+ * Create a new path hander
+ *
+ * Parameters:
+ * control - {<OpenLayers.Control>}
+ * callbacks - {Object} An object with a 'done' property whos value is a
+ * function to be called when the path drawing is finished. The
+ * callback should expect to recieve a single argument, the line
+ * string geometry. If the callbacks object contains a 'point'
+ * property, this function will be sent each point as they are added.
+ * 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 optional object with properties to be set on the
+ * handler
+ */
+ initialize: function(control, callbacks, options) {
+ OpenLayers.Handler.Point.prototype.initialize.apply(this, arguments);
+ },
+
+ /**
+ * Method: createFeature
+ * Add temporary geometries
+ */
+ createFeature: function() {
+ this.line = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.LineString());
+ this.point = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point());
+ },
+
+ /**
+ * Method: destroyFeature
+ * Destroy temporary geometries
+ */
+ destroyFeature: function() {
+ OpenLayers.Handler.Point.prototype.destroyFeature.apply(this);
+ if(this.line) {
+ this.line.destroy();
+ }
+ this.line = null;
+ },
+
+ /**
+ * Method: addPoint
+ * Add point to geometry. Send the point index to override
+ * the behavior of LinearRing that disregards adding duplicate points.
+ */
+ addPoint: function() {
+ this.line.geometry.addComponent(this.point.geometry.clone(),
+ this.line.geometry.components.length);
+ this.callback("point", [this.point.geometry]);
+ },
+
+ /**
+ * Method: freehandMode
+ * Determine whether to behave in freehand mode or not.
+ *
+ * Returns:
+ * {Boolean}
+ */
+ freehandMode: function(evt) {
+ return (this.freehandToggle && evt[this.freehandToggle]) ?
+ !this.freehand : this.freehand;
+ },
+
+ /**
+ * Method: modifyFeature
+ * Modify the existing geometry given the new point
+ */
+ modifyFeature: function() {
+ var index = this.line.geometry.components.length - 1;
+ this.line.geometry.components[index].x = this.point.geometry.x;
+ this.line.geometry.components[index].y = this.point.geometry.y;
+ this.line.geometry.components[index].clearBounds();
+ },
+
+ /**
+ * Method: drawFeature
+ * Render geometries on the temporary layer.
+ */
+ drawFeature: function() {
+ this.layer.drawFeature(this.line, this.style);
+ this.layer.drawFeature(this.point, this.style);
+ },
+
+ /**
+ * Method: geometryClone
+ * Return a clone of the relevant geometry.
+ *
+ * Returns:
+ * {<OpenLayers.Geometry.LineString>}
+ */
+ geometryClone: function() {
+ return this.line.geometry.clone();
+ },
+
+ /**
+ * Method: mousedown
+ * Handle mouse down. Add a new point to the geometry and
+ * render it. Return determines whether to propagate the event on the map.
+ *
+ * Parameters:
+ * evt - {Event} The browser event
+ *
+ * Returns:
+ * {Boolean} Allow event propagation
+ */
+ mousedown: function(evt) {
+ // ignore double-clicks
+ if (this.lastDown && this.lastDown.equals(evt.xy)) {
+ return false;
+ }
+ if(this.lastDown == null) {
+ this.createFeature();
+ }
+ this.mouseDown = true;
+ this.lastDown = evt.xy;
+ var lonlat = this.control.map.getLonLatFromPixel(evt.xy);
+ this.point.geometry.x = lonlat.lon;
+ this.point.geometry.y = lonlat.lat;
+ if((this.lastUp == null) || !this.lastUp.equals(evt.xy)) {
+ this.addPoint();
+ }
+ this.drawFeature();
+ this.drawing = true;
+ return false;
+ },
+
+ /**
+ * Method: mousemove
+ * Handle mouse move. Adjust the geometry and redraw.
+ * Return determines whether to propagate the event on the map.
+ *
+ * Parameters:
+ * evt - {Event} The browser event
+ *
+ * Returns:
+ * {Boolean} Allow event propagation
+ */
+ mousemove: function (evt) {
+ if(this.drawing) {
+ var lonlat = this.map.getLonLatFromPixel(evt.xy);
+ this.point.geometry.x = lonlat.lon;
+ this.point.geometry.y = lonlat.lat;
+ this.point.geometry.clearBounds();
+ if(this.mouseDown && this.freehandMode(evt)) {
+ this.addPoint();
+ } else {
+ this.modifyFeature();
+ }
+ this.drawFeature();
+ }
+ return true;
+ },
+
+ /**
+ * Method: mouseup
+ * Handle mouse up. Send the latest point in the geometry to
+ * the control. Return determines whether to propagate the event on the map.
+ *
+ * Parameters:
+ * evt - {Event} The browser event
+ *
+ * Returns:
+ * {Boolean} Allow event propagation
+ */
+ mouseup: function (evt) {
+ this.mouseDown = false;
+ if(this.drawing) {
+ if(this.freehandMode(evt)) {
+ this.finalize();
+ } else {
+ if(this.lastUp == null) {
+ this.addPoint();
+ }
+ this.lastUp = evt.xy;
+ }
+ return false;
+ }
+ return true;
+ },
+
+ /**
+ * Method: dblclick
+ * Handle double-clicks. Finish the geometry and send it back
+ * to the control.
+ *
+ * Parameters:
+ * evt - {Event} The browser event
+ *
+ * Returns:
+ * {Boolean} Allow event propagation
+ */
+ dblclick: function(evt) {
+ if(!this.freehandMode(evt)) {
+ var index = this.line.geometry.components.length - 1;
+ this.line.geometry.removeComponent(this.line.geometry.components[index]);
+ this.finalize();
+ }
+ return false;
+ },
+
+ CLASS_NAME: "OpenLayers.Handler.Path"
+});
+/* ======================================================================
+ OpenLayers/Format/WFS.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/Format/GML.js
+ */
+
+/**
+ * Class: OpenLayers.Format.WFS
+ * Read/Write WFS.
+ */
+OpenLayers.Format.WFS = OpenLayers.Class(OpenLayers.Format.GML, {
+
+ /**
+ * Property: layer
+ */
+ layer: null,
+
+ /**
+ * APIProperty: wfsns
+ */
+ wfsns: "http://www.opengis.net/wfs",
+
+ /**
+ * Property: ogcns
+ */
+ ogcns: "http://www.opengis.net/ogc",
+
+ /*
+ * Constructor: OpenLayers.Format.WFS
+ * Create a WFS-T formatter. This requires a layer: that layer should
+ * have two properties: geometry_column and typename. The parser
+ * for this format is subclassed entirely from GML: There is a writer
+ * only, which uses most of the code from the GML layer, and wraps
+ * it in transactional elements.
+ *
+ * Parameters:
+ * options - {Object}
+ * layer - {<OpenLayers.Layer>}
+ */
+
+ initialize: function(options, layer) {
+ OpenLayers.Format.GML.prototype.initialize.apply(this, [options]);
+ this.layer = layer;
+ if (this.layer.featureNS) {
+ this.featureNS = this.layer.featureNS;
+ }
+ if (this.layer.options.geometry_column) {
+ this.geometryName = this.layer.options.geometry_column;
+ }
+ if (this.layer.options.typename) {
+ this.featureName = this.layer.options.typename;
+ }
+ },
+
+ /**
+ * Method: write
+ * Takes a feature list, and generates a WFS-T Transaction
+ *
+ * Parameters:
+ * features - {Array(<OpenLayers.Feature.Vector>)}
+ */
+ write: function(features) {
+
+ var transaction = this.createElementNS(this.wfsns, 'wfs:Transaction');
+ transaction.setAttribute("version","1.0.0");
+ transaction.setAttribute("service","WFS");
+ for (var i=0; i < features.length; i++) {
+ switch (features[i].state) {
+ case OpenLayers.State.INSERT:
+ transaction.appendChild(this.insert(features[i]));
+ break;
+ case OpenLayers.State.UPDATE:
+ transaction.appendChild(this.update(features[i]));
+ break;
+ case OpenLayers.State.DELETE:
+ transaction.appendChild(this.remove(features[i]));
+ break;
+ }
+ }
+
+ return OpenLayers.Format.XML.prototype.write.apply(this,[transaction]);
+ },
+
+ /**
+ * Method: createFeatureXML
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>}
+ */
+ createFeatureXML: function(feature) {
+ var geometryNode = this.buildGeometryNode(feature.geometry);
+ var geomContainer = this.createElementNS(this.featureNS, "feature:" + this.geometryName);
+ geomContainer.appendChild(geometryNode);
+ var featureContainer = this.createElementNS(this.featureNS, "feature:" + this.featureName);
+ featureContainer.appendChild(geomContainer);
+ for(var attr in feature.attributes) {
+ var attrText = this.createTextNode(feature.attributes[attr]);
+ var nodename = attr;
+ if (attr.search(":") != -1) {
+ nodename = attr.split(":")[1];
+ }
+ var attrContainer = this.createElementNS(this.featureNS, "feature:" + nodename);
+ attrContainer.appendChild(attrText);
+ featureContainer.appendChild(attrContainer);
+ }
+ return featureContainer;
+ },
+
+ /**
+ * Method: insert
+ * Takes a feature, and generates a WFS-T Transaction "Insert"
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>}
+ */
+ insert: function(feature) {
+ var insertNode = this.createElementNS(this.wfsns, 'wfs:Insert');
+ insertNode.appendChild(this.createFeatureXML(feature));
+ return insertNode;
+ },
+
+ /**
+ * Method: update
+ * Takes a feature, and generates a WFS-T Transaction "Update"
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>}
+ */
+ update: function(feature) {
+ if (!feature.fid) { OpenLayers.Console.userError(OpenLayers.i18n("noFID")); }
+ var updateNode = this.createElementNS(this.wfsns, 'wfs:Update');
+ updateNode.setAttribute("typeName", this.layerName);
+
+ var propertyNode = this.createElementNS(this.wfsns, 'wfs:Property');
+ var nameNode = this.createElementNS(this.wfsns, 'wfs:Name');
+
+ var txtNode = this.createTextNode(this.geometryName);
+ nameNode.appendChild(txtNode);
+ propertyNode.appendChild(nameNode);
+
+ var valueNode = this.createElementNS(this.wfsns, 'wfs:Value');
+
+ var geometryNode = this.buildGeometryNode(feature.geometry);
+
+ if(feature.layer){
+ geometryNode.setAttribute(
+ "srsName", feature.layer.projection.getCode()
+ );
+ }
+
+ valueNode.appendChild(geometryNode);
+
+ propertyNode.appendChild(valueNode);
+ updateNode.appendChild(propertyNode);
+
+ // add in attributes
+ for(var propName in feature.attributes) {
+ propertyNode = this.createElementNS(this.wfsns, 'wfs:Property');
+ nameNode = this.createElementNS(this.wfsns, 'wfs:Name');
+ nameNode.appendChild(this.createTextNode(propName));
+ propertyNode.appendChild(nameNode);
+ valueNode = this.createElementNS(this.wfsns, 'wfs:Value');
+ valueNode.appendChild(this.createTextNode(feature.attributes[propName]));
+ propertyNode.appendChild(valueNode);
+ updateNode.appendChild(propertyNode);
+ }
+
+
+ var filterNode = this.createElementNS(this.ogcns, 'ogc:Filter');
+ var filterIdNode = this.createElementNS(this.ogcns, 'ogc:FeatureId');
+ filterIdNode.setAttribute("fid", feature.fid);
+ filterNode.appendChild(filterIdNode);
+ updateNode.appendChild(filterNode);
+
+ return updateNode;
+ },
+
+ /**
+ * Method: remove
+ * Takes a feature, and generates a WFS-T Transaction "Delete"
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>}
+ */
+ remove: function(feature) {
+ if (!feature.fid) {
+ OpenLayers.Console.userError(OpenLayers.i18n("noFID"));
+ return false;
+ }
+ var deleteNode = this.createElementNS(this.wfsns, 'wfs:Delete');
+ deleteNode.setAttribute("typeName", this.layerName);
+
+ var filterNode = this.createElementNS(this.ogcns, 'ogc:Filter');
+ var filterIdNode = this.createElementNS(this.ogcns, 'ogc:FeatureId');
+ filterIdNode.setAttribute("fid", feature.fid);
+ filterNode.appendChild(filterIdNode);
+ deleteNode.appendChild(filterNode);
+
+ return deleteNode;
+ },
+
+ /**
+ * APIMethod: destroy
+ * Remove ciruclar ref to layer
+ */
+ destroy: function() {
+ this.layer = null;
+ },
+
+ CLASS_NAME: "OpenLayers.Format.WFS"
+});
+/* ======================================================================
+ OpenLayers/Handler/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/Handler/Path.js
+ * @requires OpenLayers/Geometry/Polygon.js
+ */
+
+/**
+ * Class: OpenLayers.Handler.Polygon
+ * Handler to draw a polygon on the map. Polygon is displayed on mouse down,
+ * moves on mouse move, and is finished on mouse up.
+ *
+ * Inherits from:
+ * - <OpenLayers.Handler.Path>
+ * - <OpenLayers.Handler>
+ */
+OpenLayers.Handler.Polygon = OpenLayers.Class(OpenLayers.Handler.Path, {
+
+ /**
+ * Parameter: polygon
+ * {<OpenLayers.Feature.Vector>}
+ */
+ polygon: null,
+
+ /**
+ * Constructor: OpenLayers.Handler.Polygon
+ * Create a Polygon Handler.
+ *
+ * Parameters:
+ * control - {<OpenLayers.Control>}
+ * callbacks - {Object} An object with a 'done' property whos value is
+ * a function to be called when the path drawing is
+ * finished. The callback should expect to recieve a
+ * single argument, the polygon geometry.
+ * If the callbacks object contains a 'point'
+ * property, this function will be sent each point
+ * as they are added. 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}
+ */
+ initialize: function(control, callbacks, options) {
+ OpenLayers.Handler.Path.prototype.initialize.apply(this, arguments);
+ },
+
+ /**
+ * Method: createFeature
+ * Add temporary geometries
+ */
+ createFeature: function() {
+ this.polygon = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Polygon());
+ this.line = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.LinearRing());
+ this.polygon.geometry.addComponent(this.line.geometry);
+ this.point = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point());
+ },
+
+ /**
+ * Method: destroyFeature
+ * Destroy temporary geometries
+ */
+ destroyFeature: function() {
+ OpenLayers.Handler.Path.prototype.destroyFeature.apply(this);
+ if(this.polygon) {
+ this.polygon.destroy();
+ }
+ this.polygon = null;
+ },
+
+ /**
+ * Method: modifyFeature
+ * Modify the existing geometry given the new point
+ *
+ */
+ modifyFeature: function() {
+ var index = this.line.geometry.components.length - 2;
+ this.line.geometry.components[index].x = this.point.geometry.x;
+ this.line.geometry.components[index].y = this.point.geometry.y;
+ this.line.geometry.components[index].clearBounds();
+ },
+
+ /**
+ * Method: drawFeature
+ * Render geometries on the temporary layer.
+ */
+ drawFeature: function() {
+ this.layer.drawFeature(this.polygon, this.style);
+ this.layer.drawFeature(this.point, this.style);
+ },
+
+ /**
+ * Method: geometryClone
+ * Return a clone of the relevant geometry.
+ *
+ * Returns:
+ * {<OpenLayers.Geometry.Polygon>}
+ */
+ geometryClone: function() {
+ return this.polygon.geometry.clone();
+ },
+
+ /**
+ * Method: dblclick
+ * Handle double-clicks. Finish the geometry and send it back
+ * to the control.
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ dblclick: function(evt) {
+ if(!this.freehandMode(evt)) {
+ // remove the penultimate point
+ var index = this.line.geometry.components.length - 2;
+ this.line.geometry.removeComponent(this.line.geometry.components[index]);
+ this.finalize();
+ }
+ return false;
+ },
+
+ CLASS_NAME: "OpenLayers.Handler.Polygon"
+});
+/* ======================================================================
+ OpenLayers/Control/EditingToolbar.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/Panel.js
+ * @requires OpenLayers/Control/Navigation.js
+ * @requires OpenLayers/Control/DrawFeature.js
+ * @requires OpenLayers/Handler/Point.js
+ * @requires OpenLayers/Handler/Path.js
+ * @requires OpenLayers/Handler/Polygon.js
+ */
+
+/**
+ * Class: OpenLayers.Control.EditingToolbar
+ */
+OpenLayers.Control.EditingToolbar = OpenLayers.Class(
+ OpenLayers.Control.Panel, {
+
+ /**
+ * Constructor: OpenLayers.Control.EditingToolbar
+ * Create an editing toolbar for a given layer.
+ *
+ * Parameters:
+ * layer - {<OpenLayers.Layer.Vector>}
+ * options - {Object}
+ */
+ initialize: function(layer, options) {
+ OpenLayers.Control.Panel.prototype.initialize.apply(this, [options]);
+
+ this.addControls(
+ [ new OpenLayers.Control.Navigation() ]
+ );
+ var controls = [
+ new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Point, {'displayClass': 'olControlDrawFeaturePoint'}),
+ new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Path, {'displayClass': 'olControlDrawFeaturePath'}),
+ new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Polygon, {'displayClass': 'olControlDrawFeaturePolygon'})
+ ];
+ for (var i=0, len=controls.length; i<len; i++) {
+ controls[i].featureAdded = function(feature) { feature.state = OpenLayers.State.INSERT; };
+ }
+ this.addControls(controls);
+ },
+
+ /**
+ * Method: draw
+ * calls the default draw, and then activates mouse defaults.
+ *
+ * Returns:
+ * {DOMElement}
+ */
+ draw: function() {
+ var div = OpenLayers.Control.Panel.prototype.draw.apply(this, arguments);
+ this.activateControl(this.controls[0]);
+ return div;
+ },
+
+ CLASS_NAME: "OpenLayers.Control.EditingToolbar"
+});
Modified: trunk/lib/OpenLayers/OpenLayersCompressed.js
===================================================================
--- trunk/lib/OpenLayers/OpenLayersCompressed.js 2008-09-05 14:31:06 UTC (rev 1498)
+++ trunk/lib/OpenLayers/OpenLayersCompressed.js 2008-09-05 14:39:19 UTC (rev 1499)
@@ -43,41 +43,27 @@
*
**/
-var OpenLayers={singleFile:true};(function(){var singleFile=(typeof OpenLayers=="object"&&OpenLayers.singleFile);window.OpenLayers={_scriptName:(!singleFile)?"lib/OpenLayers.js":"OpenLayers.js",_getScriptLocation:function(){var scriptLocation="";var scriptName=OpenLayers._scriptName;var scripts=document.getElementsByTagName('script');for(var i=0;i<scripts.length;i++){var src=scripts[i].getAttribute('src');if(src){var index=src.lastIndexOf(scriptName);var pathLength=src.lastIndexOf('?');if(pathLength<0){pathLength=src.length;}
+/**
+ * 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
+ */
+var OpenLayers={singleFile:true};(function(){var singleFile=(typeof OpenLayers=="object"&&OpenLayers.singleFile);window.OpenLayers={_scriptName:(!singleFile)?"lib/OpenLayers.js":"OpenLayers.js",_getScriptLocation:function(){var scriptLocation="";var scriptName=OpenLayers._scriptName;var scripts=document.getElementsByTagName('script');for(var i=0,len=scripts.length;i<len;i++){var src=scripts[i].getAttribute('src');if(src){var index=src.lastIndexOf(scriptName);var pathLength=src.lastIndexOf('?');if(pathLength<0){pathLength=src.length;}
if((index>-1)&&(index+scriptName.length==pathLength)){scriptLocation=src.slice(0,pathLength-scriptName.length);break;}}}
-return scriptLocation;}};if(!singleFile){var jsfiles=new Array("OpenLayers/Util.js","OpenLayers/BaseTypes.js","OpenLayers/BaseTypes/Class.js","OpenLayers/BaseTypes/Bounds.js","OpenLayers/BaseTypes/Element.js","OpenLayers/BaseTypes/LonLat.js","OpenLayers/BaseTypes/Pixel.js","OpenLayers/BaseTypes/Size.js","OpenLayers/Console.js","OpenLayers/Tween.js","Rico/Corner.js","Rico/Color.js","OpenLayers/Ajax.js","OpenLayers/Events.js","OpenLayers/Projection.js","OpenLayers/Map.js","OpenLayers/Layer.js","OpenLayers/Icon.js","OpenLayers/Marker.js","OpenLayers/Marker/Box.js","OpenLayers/Popup.js","OpenLayers/Tile.js","OpenLayers/Tile/Image.js","OpenLayers/Tile/WFS.js","OpenLayers/Layer/Image.js","OpenLayers/Layer/SphericalMercator.js","OpenLayers/Layer/EventPane.js","OpenLayers/Layer/FixedZoomLevels.js","OpenLayers/Layer/Google.js","OpenLayers/Layer/VirtualEarth.js","OpenLayers/Layer/Yahoo.js","OpenLayers/Layer/HTTPRequest.js","OpenLayers/Layer/Grid.js","OpenLayers/Layer/MapGuide.js","OpenLayers/Layer/MapServer.js","OpenLayers/Layer/MapServer/Untiled.js","OpenLayers/Layer/KaMap.js","OpenLayers/Layer/MultiMap.js","OpenLayers/Layer/Markers.js","OpenLayers/Layer/Text.js","OpenLayers/Layer/WorldWind.js","OpenLayers/Layer/WMS.js","OpenLayers/Layer/WMS/Untiled.js","OpenLayers/Layer/GeoRSS.js","OpenLayers/Layer/Boxes.js","OpenLayers/Layer/TMS.js","OpenLayers/Layer/TileCache.js","OpenLayers/Popup/Anchored.js","OpenLayers/Popup/AnchoredBubble.js","OpenLayers/Popup/Framed.js","OpenLayers/Popup/FramedCloud.js","OpenLayers/Feature.js","OpenLayers/Feature/Vector.js","OpenLayers/Feature/WFS.js","OpenLayers/Handler.js","OpenLayers/Handler/Click.js","OpenLayers/Handler/Hover.js","OpenLayers/Handler/Point.js","OpenLayers/Handler/Path.js","OpenLayers/Handler/Polygon.js","OpenLayers/Handler/Feature.js","OpenLayers/Handler/Drag.js","OpenLayers/Handler/RegularPolygon.js","OpenLayers/Handler/Box.js","OpenLayers/Handler/MouseWheel.js","OpenLayers/Handler/Keyboard.js","OpenLayers/Control.js","OpenLayers/Control/Attribution.js","OpenLayers/Control/Button.js","OpenLayers/Control/ZoomBox.js","OpenLayers/Control/ZoomToMaxExtent.js","OpenLayers/Control/DragPan.js","OpenLayers/Control/Navigation.js","OpenLayers/Control/MouseDefaults.js","OpenLayers/Control/MousePosition.js","OpenLayers/Control/OverviewMap.js","OpenLayers/Control/KeyboardDefaults.js","OpenLayers/Control/PanZoom.js","OpenLayers/Control/PanZoomBar.js","OpenLayers/Control/ArgParser.js","OpenLayers/Control/Permalink.js","OpenLayers/Control/Scale.js","OpenLayers/Control/ScaleLine.js","OpenLayers/Control/LayerSwitcher.js","OpenLayers/Control/DrawFeature.js","OpenLayers/Control/DragFeature.js","OpenLayers/Control/ModifyFeature.js","OpenLayers/Control/Panel.js","OpenLayers/Control/SelectFeature.js","OpenLayers/Control/NavigationHistory.js","OpenLayers/Geometry.js","OpenLayers/Geometry/Rectangle.js","OpenLayers/Geometry/Collection.js","OpenLayers/Geometry/Point.js","OpenLayers/Geometry/MultiPoint.js","OpenLayers/Geometry/Curve.js","OpenLayers/Geometry/LineString.js","OpenLayers/Geometry/LinearRing.js","OpenLayers/Geometry/Polygon.js","OpenLayers/Geometry/MultiLineString.js","OpenLayers/Geometry/MultiPolygon.js","OpenLayers/Geometry/Surface.js","OpenLayers/Renderer.js","OpenLayers/Renderer/Elements.js","OpenLayers/Renderer/SVG.js","OpenLayers/Renderer/VML.js","OpenLayers/Layer/Vector.js","OpenLayers/Layer/PointTrack.js","OpenLayers/Layer/GML.js","OpenLayers/Style.js","OpenLayers/StyleMap.js","OpenLayers/Rule.js","OpenLayers/Filter.js","OpenLayers/Filter/FeatureId.js","OpenLayers/Filter/Logical.js","OpenLayers/Filter/Comparison.js","OpenLayers/Format.js","OpenLayers/Format/XML.js","OpenLayers/Format/GML.js","OpenLayers/Format/KML.js","OpenLayers/Format/GeoRSS.js","OpenLayers/Format/WFS.js","OpenLayers/Format/WKT.js","OpenLayers/Format/OSM.js","OpenLayers/Format/SLD.js","OpenLayers/Format/SLD/v1.js","OpenLayers/Format/SLD/v1_0_0.js","OpenLayers/Format/Text.js","OpenLayers/Format/JSON.js","OpenLayers/Format/GeoJSON.js","OpenLayers/Format/WMC.js","OpenLayers/Format/WMC/v1.js","OpenLayers/Format/WMC/v1_0_0.js","OpenLayers/Format/WMC/v1_1_0.js","OpenLayers/Layer/WFS.js","OpenLayers/Control/MouseToolbar.js","OpenLayers/Control/NavToolbar.js","OpenLayers/Control/EditingToolbar.js","OpenLayers/Lang.js","OpenLayers/Lang/en.js");var agent=navigator.userAgent;var docWrite=(agent.match("MSIE")||agent.match("Safari"));if(docWrite){var allScriptTags=new Array(jsfiles.length);}
-var host=OpenLayers._getScriptLocation()+"lib/";for(var i=0;i<jsfiles.length;i++){if(docWrite){allScriptTags[i]="<script src='"+host+jsfiles[i]+"'></script>";}else{var s=document.createElement("script");s.src=host+jsfiles[i];var h=document.getElementsByTagName("head").length?document.getElementsByTagName("head")[0]:document.body;h.appendChild(s);}}
-if(docWrite){document.write(allScriptTags.join(""));}}})();OpenLayers.VERSION_NUMBER="$Revision$";OpenLayers.String={startsWith:function(str,sub){return(str.indexOf(sub)==0);},contains:function(str,sub){return(str.indexOf(sub)!=-1);},trim:function(str){return str.replace(/^\s*(.*?)\s*$/,"$1");},camelize:function(str){var oStringList=str.split('-');var camelizedString=oStringList[0];for(var i=1;i<oStringList.length;i++){var s=oStringList[i];camelizedString+=s.charAt(0).toUpperCase()+s.substring(1);}
-return camelizedString;},format:function(template,context,args){if(!context){context=window;}
-var tokens=template.split("${");var item,last,replacement;for(var i=1;i<tokens.length;i++){item=tokens[i];last=item.indexOf("}");if(last>0){replacement=context[item.substring(0,last)];if(typeof replacement=="function"){replacement=args?replacement.apply(null,args):replacement();}
-tokens[i]=replacement+item.substring(++last);}else{tokens[i]="${"+item;}}
-return tokens.join("");}};if(!String.prototype.startsWith){String.prototype.startsWith=function(sStart){OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",{'newMethod':'OpenLayers.String.startsWith'}));return OpenLayers.String.startsWith(this,sStart);};}
-if(!String.prototype.contains){String.prototype.contains=function(str){OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",{'newMethod':'OpenLayers.String.contains'}));return OpenLayers.String.contains(this,str);};}
-if(!String.prototype.trim){String.prototype.trim=function(){OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",{'newMethod':'OpenLayers.String.trim'}));return OpenLayers.String.trim(this);};}
-if(!String.prototype.camelize){String.prototype.camelize=function(){OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",{'newMethod':'OpenLayers.String.camelize'}));return OpenLayers.String.camelize(this);};}
-OpenLayers.Number={decimalSeparator:".",thousandsSeparator:",",limitSigDigs:function(num,sig){var fig=0;if(sig>0){fig=parseFloat(num.toPrecision(sig));}
-return fig;},format:function(num,dec,tsep,dsep){dec=(typeof dec!="undefined")?dec:0;tsep=(typeof tsep!="undefined")?tsep:OpenLayers.Number.thousandsSeparator;dsep=(typeof dsep!="undefined")?dsep:OpenLayers.Number.decimalSeparator;if(dec!=null){num=parseFloat(num.toFixed(dec));}
-var parts=num.toString().split(".");if(parts.length==1&&dec==null){dec=0;}
-var integer=parts[0];if(tsep){var thousands=/(-?[0-9]+)([0-9]{3})/;while(thousands.test(integer)){integer=integer.replace(thousands,"$1"+tsep+"$2");}}
-var str;if(dec==0){str=integer;}else{var rem=parts.length>1?parts[1]:"0";if(dec!=null){rem=rem+new Array(dec-rem.length+1).join("0");}
-str=integer+dsep+rem;}
-return str;}};if(!Number.prototype.limitSigDigs){Number.prototype.limitSigDigs=function(sig){OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",{'newMethod':'OpenLayers.String.limitSigDigs'}));return OpenLayers.Number.limitSigDigs(this,sig);};}
-OpenLayers.Function={bind:function(func,object){var args=Array.prototype.slice.apply(arguments,[2]);return function(){var newArgs=args.concat(Array.prototype.slice.apply(arguments,[0]));return func.apply(object,newArgs);};},bindAsEventListener:function(func,object){return function(event){return func.call(object,event||window.event);};}};if(!Function.prototype.bind){Function.prototype.bind=function(){OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",{'newMethod':'OpenLayers.String.bind'}));Array.prototype.unshift.apply(arguments,[this]);return OpenLayers.Function.bind.apply(null,arguments);};}
-if(!Function.prototype.bindAsEventListener){Function.prototype.bindAsEventListener=function(object){OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",{'newMethod':'OpenLayers.String.bindAsEventListener'}));return OpenLayers.Function.bindAsEventListener(this,object);};}
-OpenLayers.Array={filter:function(array,callback,caller){var selected=[];if(Array.prototype.filter){selected=array.filter(callback,caller);}else{var len=array.length;if(typeof callback!="function"){throw new TypeError();}
-for(var i=0;i<len;i++){if(i in array){var val=array[i];if(callback.call(caller,val,i,array)){selected.push(val);}}}}
-return selected;}};OpenLayers.Class=function(){var Class=function(){if(arguments&&arguments[0]!=OpenLayers.Class.isPrototype){this.initialize.apply(this,arguments);}};var extended={};var parent;for(var i=0;i<arguments.length;++i){if(typeof arguments[i]=="function"){parent=arguments[i].prototype;}else{parent=arguments[i];}
-OpenLayers.Util.extend(extended,parent);}
-Class.prototype=extended;return Class;};OpenLayers.Class.isPrototype=function(){};OpenLayers.Class.create=function(){return function(){if(arguments&&arguments[0]!=OpenLayers.Class.isPrototype){this.initialize.apply(this,arguments);}};};OpenLayers.Class.inherit=function(){var superClass=arguments[0];var proto=new superClass(OpenLayers.Class.isPrototype);for(var i=1;i<arguments.length;i++){if(typeof arguments[i]=="function"){var mixin=arguments[i];arguments[i]=new mixin(OpenLayers.Class.isPrototype);}
-OpenLayers.Util.extend(proto,arguments[i]);}
-return proto;};OpenLayers.Util={};OpenLayers.Util.getElement=function(){var elements=[];for(var i=0;i<arguments.length;i++){var element=arguments[i];if(typeof element=='string'){element=document.getElementById(element);}
+return scriptLocation;}};if(!singleFile){var jsfiles=new Array("OpenLayers/Util.js","OpenLayers/BaseTypes.js","OpenLayers/BaseTypes/Class.js","OpenLayers/BaseTypes/Bounds.js","OpenLayers/BaseTypes/Element.js","OpenLayers/BaseTypes/LonLat.js","OpenLayers/BaseTypes/Pixel.js","OpenLayers/BaseTypes/Size.js","OpenLayers/Console.js","OpenLayers/Tween.js","Rico/Corner.js","Rico/Color.js","OpenLayers/Ajax.js","OpenLayers/Request.js","OpenLayers/Request/XMLHttpRequest.js","OpenLayers/Events.js","OpenLayers/Projection.js","OpenLayers/Map.js","OpenLayers/Layer.js","OpenLayers/Icon.js","OpenLayers/Marker.js","OpenLayers/Marker/Box.js","OpenLayers/Popup.js","OpenLayers/Tile.js","OpenLayers/Tile/Image.js","OpenLayers/Tile/WFS.js","OpenLayers/Layer/Image.js","OpenLayers/Layer/SphericalMercator.js","OpenLayers/Layer/EventPane.js","OpenLayers/Layer/FixedZoomLevels.js","OpenLayers/Layer/Google.js","OpenLayers/Layer/VirtualEarth.js","OpenLayers/Layer/Yahoo.js","OpenLayers/Layer/HTTPRequest.js","OpenLayers/Layer/Grid.js","OpenLayers/Layer/MapGuide.js","OpenLayers/Layer/MapServer.js","OpenLayers/Layer/MapServer/Untiled.js","OpenLayers/Layer/KaMap.js","OpenLayers/Layer/MultiMap.js","OpenLayers/Layer/Markers.js","OpenLayers/Layer/Text.js","OpenLayers/Layer/WorldWind.js","OpenLayers/Layer/WMS.js","OpenLayers/Layer/WMS/Untiled.js","OpenLayers/Layer/GeoRSS.js","OpenLayers/Layer/Boxes.js","OpenLayers/Layer/TMS.js","OpenLayers/Layer/TileCache.js","OpenLayers/Popup/Anchored.js","OpenLayers/Popup/AnchoredBubble.js","OpenLayers/Popup/Framed.js","OpenLayers/Popup/FramedCloud.js","OpenLayers/Feature.js","OpenLayers/Feature/Vector.js","OpenLayers/Feature/WFS.js","OpenLayers/Handler.js","OpenLayers/Handler/Click.js","OpenLayers/Handler/Hover.js","OpenLayers/Handler/Point.js","OpenLayers/Handler/Path.js","OpenLayers/Handler/Polygon.js","OpenLayers/Handler/Feature.js","OpenLayers/Handler/Drag.js","OpenLayers/Handler/RegularPolygon.js","OpenLayers/Handler/Box.js","OpenLayers/Handler/MouseWheel.js","OpenLayers/Handler/Keyboard.js","OpenLayers/Control.js","OpenLayers/Control/Attribution.js","OpenLayers/Control/Button.js","OpenLayers/Control/ZoomBox.js","OpenLayers/Control/ZoomToMaxExtent.js","OpenLayers/Control/DragPan.js","OpenLayers/Control/Navigation.js","OpenLayers/Control/MouseDefaults.js","OpenLayers/Control/MousePosition.js","OpenLayers/Control/OverviewMap.js","OpenLayers/Control/KeyboardDefaults.js","OpenLayers/Control/PanZoom.js","OpenLayers/Control/PanZoomBar.js","OpenLayers/Control/ArgParser.js","OpenLayers/Control/Permalink.js","OpenLayers/Control/Scale.js","OpenLayers/Control/ScaleLine.js","OpenLayers/Control/LayerSwitcher.js","OpenLayers/Control/DrawFeature.js","OpenLayers/Control/DragFeature.js","OpenLayers/Control/ModifyFeature.js","OpenLayers/Control/Panel.js","OpenLayers/Control/SelectFeature.js","OpenLayers/Control/NavigationHistory.js","OpenLayers/Geometry.js","OpenLayers/Geometry/Rectangle.js","OpenLayers/Geometry/Collection.js","OpenLayers/Geometry/Point.js","OpenLayers/Geometry/MultiPoint.js","OpenLayers/Geometry/Curve.js","OpenLayers/Geometry/LineString.js","OpenLayers/Geometry/LinearRing.js","OpenLayers/Geometry/Polygon.js","OpenLayers/Geometry/MultiLineString.js","OpenLayers/Geometry/MultiPolygon.js","OpenLayers/Geometry/Surface.js","OpenLayers/Renderer.js","OpenLayers/Renderer/Elements.js","OpenLayers/Renderer/SVG.js","OpenLayers/Renderer/Canvas.js","OpenLayers/Renderer/VML.js","OpenLayers/Layer/Vector.js","OpenLayers/Strategy.js","OpenLayers/Strategy/Fixed.js","OpenLayers/Protocol.js","OpenLayers/Layer/PointTrack.js","OpenLayers/Layer/GML.js","OpenLayers/Style.js","OpenLayers/StyleMap.js","OpenLayers/Rule.js","OpenLayers/Filter.js","OpenLayers/Filter/FeatureId.js","OpenLayers/Filter/Logical.js","OpenLayers/Filter/Comparison.js","OpenLayers/Format.js","OpenLayers/Format/XML.js","OpenLayers/Format/GML.js","OpenLayers/Format/KML.js","OpenLayers/Format/GeoRSS.js","OpenLayers/Format/WFS.js","OpenLayers/Format/WKT.js","OpenLayers/Format/OSM.js","OpenLayers/Format/GPX.js","OpenLayers/Format/SLD.js","OpenLayers/Format/SLD/v1.js","OpenLayers/Format/SLD/v1_0_0.js","OpenLayers/Format/SLD/v1.js","OpenLayers/Format/Filter.js","OpenLayers/Format/Filter/v1.js","OpenLayers/Format/Filter/v1_0_0.js","OpenLayers/Format/Text.js","OpenLayers/Format/JSON.js","OpenLayers/Format/GeoJSON.js","OpenLayers/Format/WMC.js","OpenLayers/Format/WMC/v1.js","OpenLayers/Format/WMC/v1_0_0.js","OpenLayers/Format/WMC/v1_1_0.js","OpenLayers/Layer/WFS.js","OpenLayers/Control/MouseToolbar.js","OpenLayers/Control/NavToolbar.js","OpenLayers/Control/EditingToolbar.js","OpenLayers/Lang.js","OpenLayers/Lang/en.js");var agent=navigator.userAgent;var docWrite=(agent.match("MSIE")||agent.match("Safari"));if(docWrite){var allScriptTags=new Array(jsfiles.length);}
+var host=OpenLayers._getScriptLocation()+"lib/";for(var i=0,len=jsfiles.length;i<len;i++){if(docWrite){allScriptTags[i]="<script src='"+host+jsfiles[i]+"'></script>";}else{var s=document.createElement("script");s.src=host+jsfiles[i];var h=document.getElementsByTagName("head").length?document.getElementsByTagName("head")[0]:document.body;h.appendChild(s);}}
+if(docWrite){document.write(allScriptTags.join(""));}}})();OpenLayers.VERSION_NUMBER="$Revision$";OpenLayers.Util={};OpenLayers.Util.getElement=function(){var elements=[];for(var i=0,len=arguments.length;i<len;i++){var element=arguments[i];if(typeof element=='string'){element=document.getElementById(element);}
if(arguments.length==1){return element;}
elements.push(element);}
return elements;};if($==null){var $=OpenLayers.Util.getElement;}
-OpenLayers.Util.extend=function(destination,source){if(destination&&source){for(var property in source){var value=source[property];if(value!==undefined){destination[property]=value;}}
+OpenLayers.Util.extend=function(destination,source){destination=destination||{};if(source){for(var property in source){var value=source[property];if(value!==undefined){destination[property]=value;}}
var sourceIsEvt=typeof window.Event=="function"&&source instanceof window.Event;if(!sourceIsEvt&&source.hasOwnProperty&&source.hasOwnProperty('toString')){destination.toString=source.toString;}}
return destination;};OpenLayers.Util.removeItem=function(array,item){for(var i=array.length-1;i>=0;i--){if(array[i]==item){array.splice(i,1);}}
-return array;};OpenLayers.Util.clearArray=function(array){OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",{'newMethod':'array = []'}));array.length=0;};OpenLayers.Util.indexOf=function(array,obj){for(var i=0;i<array.length;i++){if(array[i]==obj){return i;}}
+return array;};OpenLayers.Util.clearArray=function(array){OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",{'newMethod':'array = []'}));array.length=0;};OpenLayers.Util.indexOf=function(array,obj){for(var i=0,len=array.length;i<len;i++){if(array[i]==obj){return i;}}
return-1;};OpenLayers.Util.modifyDOMElement=function(element,id,px,sz,position,border,overflow,opacity){if(id){element.id=id;}
if(px){element.style.left=px.x+"px";element.style.top=px.y+"px";}
if(sz){element.style.width=sz.w+"px";element.style.height=sz.h+"px";}
@@ -91,21 +77,26 @@
if(!position){position="relative";}
OpenLayers.Util.modifyDOMElement(image,id,px,sz,position,border,null,opacity);if(delayDisplay){image.style.display="none";OpenLayers.Event.observe(image,"load",OpenLayers.Function.bind(OpenLayers.Util.onImageLoad,image));OpenLayers.Event.observe(image,"error",OpenLayers.Function.bind(OpenLayers.Util.onImageLoadError,image));}
image.style.alt=id;image.galleryImg="no";if(imgURL){image.src=imgURL;}
-return image;};OpenLayers.Util.setOpacity=function(element,opacity){OpenLayers.Util.modifyDOMElement(element,null,null,null,null,null,null,opacity);};OpenLayers.Util.onImageLoad=function(){if(!this.viewRequestID||(this.map&&this.viewRequestID==this.map.viewRequestID)){this.style.backgroundColor=null;this.style.display="";}};OpenLayers.Util.onImageLoadErrorColor="pink";OpenLayers.IMAGE_RELOAD_ATTEMPTS=0;OpenLayers.Util.onImageLoadError=function(){this._attempts=(this._attempts)?(this._attempts+1):1;if(this._attempts<=OpenLayers.IMAGE_RELOAD_ATTEMPTS){this.src=this.src;}else{this.style.backgroundColor=OpenLayers.Util.onImageLoadErrorColor;}
+return image;};OpenLayers.Util.setOpacity=function(element,opacity){OpenLayers.Util.modifyDOMElement(element,null,null,null,null,null,null,opacity);};OpenLayers.Util.onImageLoad=function(){if(!this.viewRequestID||(this.map&&this.viewRequestID==this.map.viewRequestID)){this.style.backgroundColor=null;this.style.display="";}};OpenLayers.Util.onImageLoadErrorColor="pink";OpenLayers.IMAGE_RELOAD_ATTEMPTS=0;OpenLayers.Util.onImageLoadError=function(){this._attempts=(this._attempts)?(this._attempts+1):1;if(this._attempts<=OpenLayers.IMAGE_RELOAD_ATTEMPTS){var urls=this.urls;if(urls&&urls instanceof Array&&urls.length>1){var src=this.src.toString();var current_url,k;for(k=0;current_url=urls[k];k++){if(src.indexOf(current_url)!=-1){break;}}
+var guess=Math.floor(urls.length*Math.random())
+var new_url=urls[guess];k=0;while(new_url==current_url&&k++<4){guess=Math.floor(urls.length*Math.random())
+new_url=urls[guess];}
+this.src=src.replace(current_url,new_url);}else{this.src=this.src;}}else{this.style.backgroundColor=OpenLayers.Util.onImageLoadErrorColor;}
this.style.display="";};OpenLayers.Util.alphaHack=function(){var arVersion=navigator.appVersion.split("MSIE");var version=parseFloat(arVersion[1]);var filter=false;try{filter=!!(document.body.filters);}catch(e){}
-return(filter&&(version>=5.5)&&(version<7));};OpenLayers.Util.modifyAlphaImageDiv=function(div,id,px,sz,imgURL,position,border,sizing,opacity){OpenLayers.Util.modifyDOMElement(div,id,px,sz,null,null,null,opacity);var img=div.childNodes[0];if(imgURL){img.src=imgURL;}
-OpenLayers.Util.modifyDOMElement(img,div.id+"_innerImage",null,sz,"relative",border);if(OpenLayers.Util.alphaHack()){div.style.display="inline-block";if(sizing==null){sizing="scale";}
+return(filter&&(version>=5.5)&&(version<7));};OpenLayers.Util.modifyAlphaImageDiv=function(div,id,px,sz,imgURL,position,border,sizing,opacity){OpenLayers.Util.modifyDOMElement(div,id,px,sz,position,null,null,opacity);var img=div.childNodes[0];if(imgURL){img.src=imgURL;}
+OpenLayers.Util.modifyDOMElement(img,div.id+"_innerImage",null,sz,"relative",border);if(OpenLayers.Util.alphaHack()){if(div.style.display!="none"){div.style.display="inline-block";}
+if(sizing==null){sizing="scale";}
div.style.filter="progid:DXImageTransform.Microsoft"+".AlphaImageLoader(src='"+img.src+"', "+"sizingMethod='"+sizing+"')";if(parseFloat(div.style.opacity)>=0.0&&parseFloat(div.style.opacity)<1.0){div.style.filter+=" alpha(opacity="+div.style.opacity*100+")";}
img.style.filter="alpha(opacity=0)";}};OpenLayers.Util.createAlphaImageDiv=function(id,px,sz,imgURL,position,border,sizing,opacity,delayDisplay){var div=OpenLayers.Util.createDiv();var img=OpenLayers.Util.createImage(null,null,null,null,null,null,null,false);div.appendChild(img);if(delayDisplay){img.style.display="none";OpenLayers.Event.observe(img,"load",OpenLayers.Function.bind(OpenLayers.Util.onImageLoad,div));OpenLayers.Event.observe(img,"error",OpenLayers.Function.bind(OpenLayers.Util.onImageLoadError,div));}
OpenLayers.Util.modifyAlphaImageDiv(div,id,px,sz,imgURL,position,border,sizing,opacity);return div;};OpenLayers.Util.upperCaseObject=function(object){var uObject={};for(var key in object){uObject[key.toUpperCase()]=object[key];}
-return uObject;};OpenLayers.Util.applyDefaults=function(to,from){var fromIsEvt=typeof window.Event=="function"&&from instanceof window.Event;for(var key in from){if(to[key]===undefined||(!fromIsEvt&&from.hasOwnProperty&&from.hasOwnProperty(key)&&!to.hasOwnProperty(key))){to[key]=from[key];}}
+return uObject;};OpenLayers.Util.applyDefaults=function(to,from){to=to||{};var fromIsEvt=typeof window.Event=="function"&&from instanceof window.Event;for(var key in from){if(to[key]===undefined||(!fromIsEvt&&from.hasOwnProperty&&from.hasOwnProperty(key)&&!to.hasOwnProperty(key))){to[key]=from[key];}}
if(!fromIsEvt&&from.hasOwnProperty&&from.hasOwnProperty('toString')&&!to.hasOwnProperty('toString')){to.toString=from.toString;}
-return to;};OpenLayers.Util.getParameterString=function(params){var paramsArray=[];for(var key in params){var value=params[key];if((value!=null)&&(typeof value!='function')){var encodedValue;if(typeof value=='object'&&value.constructor==Array){var encodedItemArray=[];for(var itemIndex=0;itemIndex<value.length;itemIndex++){encodedItemArray.push(encodeURIComponent(value[itemIndex]));}
+return to;};OpenLayers.Util.getParameterString=function(params){var paramsArray=[];for(var key in params){var value=params[key];if((value!=null)&&(typeof value!='function')){var encodedValue;if(typeof value=='object'&&value.constructor==Array){var encodedItemArray=[];for(var itemIndex=0,len=value.length;itemIndex<len;itemIndex++){encodedItemArray.push(encodeURIComponent(value[itemIndex]));}
encodedValue=encodedItemArray.join(",");}
else{encodedValue=encodeURIComponent(value);}
paramsArray.push(encodeURIComponent(key)+"="+encodedValue);}}
-return paramsArray.join("&");};OpenLayers.ImgPath='';OpenLayers.Util.getImagesLocation=function(){return OpenLayers.ImgPath||(OpenLayers._getScriptLocation()+"img/");};OpenLayers.Util.Try=function(){var returnValue=null;for(var i=0;i<arguments.length;i++){var lambda=arguments[i];try{returnValue=lambda();break;}catch(e){}}
-return returnValue;};OpenLayers.Util.getNodes=function(p,tagName){var nodes=OpenLayers.Util.Try(function(){return OpenLayers.Util._getNodes(p.documentElement.childNodes,tagName);},function(){return OpenLayers.Util._getNodes(p.childNodes,tagName);});return nodes;};OpenLayers.Util._getNodes=function(nodes,tagName){var retArray=[];for(var i=0;i<nodes.length;i++){if(nodes[i].nodeName==tagName){retArray.push(nodes[i]);}}
+return paramsArray.join("&");};OpenLayers.ImgPath='';OpenLayers.Util.getImagesLocation=function(){return OpenLayers.ImgPath||(OpenLayers._getScriptLocation()+"img/");};OpenLayers.Util.Try=function(){var returnValue=null;for(var i=0,len=arguments.length;i<len;i++){var lambda=arguments[i];try{returnValue=lambda();break;}catch(e){}}
+return returnValue;};OpenLayers.Util.getNodes=function(p,tagName){var nodes=OpenLayers.Util.Try(function(){return OpenLayers.Util._getNodes(p.documentElement.childNodes,tagName);},function(){return OpenLayers.Util._getNodes(p.childNodes,tagName);});return nodes;};OpenLayers.Util._getNodes=function(nodes,tagName){var retArray=[];for(var i=0,len=nodes.length;i<len;i++){if(nodes[i].nodeName==tagName){retArray.push(nodes[i]);}}
return retArray;};OpenLayers.Util.getTagText=function(parent,item,index){var result=OpenLayers.Util.getNodes(parent,item);if(result&&(result.length>0))
{if(!index){index=0;}
if(result[index].childNodes.length>1){return result.childNodes[1].nodeValue;}
@@ -117,16 +108,16 @@
if(iterLimit==0){return NaN;}
var uSq=cosSqAlpha*(a*a-b*b)/(b*b);var A=1+uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq)));var B=uSq/1024*(256+uSq*(-128+uSq*(74-47*uSq)));var deltaSigma=B*sinSigma*(cos2SigmaM+B/4*(cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)-
B/6*cos2SigmaM*(-3+4*sinSigma*sinSigma)*(-3+4*cos2SigmaM*cos2SigmaM)));var s=b*A*(sigma-deltaSigma);var d=s.toFixed(3)/1000;return d;};OpenLayers.Util.getParameters=function(url){url=url||window.location.href;var paramsString="";if(OpenLayers.String.contains(url,'?')){var start=url.indexOf('?')+1;var end=OpenLayers.String.contains(url,"#")?url.indexOf('#'):url.length;paramsString=url.substring(start,end);}
-var parameters={};var pairs=paramsString.split(/[&;]/);for(var i=0;i<pairs.length;++i){var keyValue=pairs[i].split('=');if(keyValue[0]){var key=decodeURIComponent(keyValue[0]);var value=keyValue[1]||'';value=value.split(",");for(var j=0;j<value.length;j++){value[j]=decodeURIComponent(value[j]);}
+var parameters={};var pairs=paramsString.split(/[&;]/);for(var i=0,len=pairs.length;i<len;++i){var keyValue=pairs[i].split('=');if(keyValue[0]){var key=decodeURIComponent(keyValue[0]);var value=keyValue[1]||'';value=value.split(",");for(var j=0,jlen=value.length;j<jlen;j++){value[j]=decodeURIComponent(value[j]);}
if(value.length==1){value=value[0];}
parameters[key]=value;}}
return parameters;};OpenLayers.Util.getArgs=function(url){OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",{'newMethod':'OpenLayers.Util.getParameters'}));return OpenLayers.Util.getParameters(url);};OpenLayers.Util.lastSeqID=0;OpenLayers.Util.createUniqueID=function(prefix){if(prefix==null){prefix="id_";}
OpenLayers.Util.lastSeqID+=1;return prefix+OpenLayers.Util.lastSeqID;};OpenLayers.INCHES_PER_UNIT={'inches':1.0,'ft':12.0,'mi':63360.0,'m':39.3701,'km':39370.1,'dd':4374754,'yd':36};OpenLayers.INCHES_PER_UNIT["in"]=OpenLayers.INCHES_PER_UNIT.inches;OpenLayers.INCHES_PER_UNIT["degrees"]=OpenLayers.INCHES_PER_UNIT.dd;OpenLayers.INCHES_PER_UNIT["nmi"]=1852*OpenLayers.INCHES_PER_UNIT.m;OpenLayers.DOTS_PER_INCH=72;OpenLayers.Util.normalizeScale=function(scale){var normScale=(scale>1.0)?(1.0/scale):scale;return normScale;};OpenLayers.Util.getResolutionFromScale=function(scale,units){if(units==null){units="degrees";}
var normScale=OpenLayers.Util.normalizeScale(scale);var resolution=1/(normScale*OpenLayers.INCHES_PER_UNIT[units]*OpenLayers.DOTS_PER_INCH);return resolution;};OpenLayers.Util.getScaleFromResolution=function(resolution,units){if(units==null){units="degrees";}
-var scale=resolution*OpenLayers.INCHES_PER_UNIT[units]*OpenLayers.DOTS_PER_INCH;return scale;};OpenLayers.Util.safeStopPropagation=function(evt){OpenLayers.Event.stop(evt,true);};OpenLayers.Util.pagePosition=function(forElement){var valueT=0,valueL=0;var element=forElement;var child=forElement;while(element){if(element==document.body){if(child&&child.style&&OpenLayers.Element.getStyle(child,'position')=='absolute'){break;}}
+var scale=resolution*OpenLayers.INCHES_PER_UNIT[units]*OpenLayers.DOTS_PER_INCH;return scale;};OpenLayers.Util.safeStopPropagation=function(evt){OpenLayers.Event.stop(evt,true);};OpenLayers.Util.pagePosition=function(forElement){var valueT=0,valueL=0;var element=forElement;var child=forElement;while(element){if(element==document.body){if(OpenLayers.Element.getStyle(child,'position')=='absolute'){break;}}
valueT+=element.offsetTop||0;valueL+=element.offsetLeft||0;child=element;try{element=element.offsetParent;}catch(e){OpenLayers.Console.error(OpenLayers.i18n("pagePositionFailed",{'elemId':element.id}));break;}}
element=forElement;while(element){valueT-=element.scrollTop||0;valueL-=element.scrollLeft||0;element=element.parentNode;}
-return[valueL,valueT];};OpenLayers.Util.isEquivalentUrl=function(url1,url2,options){options=options||{};OpenLayers.Util.applyDefaults(options,{ignoreCase:true,ignorePort80:true,ignoreHash:true});var urlObj1=OpenLayers.Util.createUrlObject(url1,options);var urlObj2=OpenLayers.Util.createUrlObject(url2,options);for(var key in urlObj1){if(options.test){alert(key+"\n1:"+urlObj1[key]+"\n2:"+urlObj2[key]);}
+return[valueL,valueT];};OpenLayers.Util.isEquivalentUrl=function(url1,url2,options){options=options||{};OpenLayers.Util.applyDefaults(options,{ignoreCase:true,ignorePort80:true,ignoreHash:true});var urlObj1=OpenLayers.Util.createUrlObject(url1,options);var urlObj2=OpenLayers.Util.createUrlObject(url2,options);for(var key in urlObj1){if(options.test){OpenLayers.Console.userError(key+"\n1:"+urlObj1[key]+"\n2:"+urlObj2[key]);}
var val1=urlObj1[key];var val2=urlObj2[key];switch(key){case"args":break;case"host":case"port":case"protocol":if((val1=="")||(val2=="")){break;}
default:if((key!="args")&&(urlObj1[key]!=urlObj2[key])){return false;}
break;}}
@@ -143,28 +134,36 @@
if((urlObject.protocol=="file:")||(urlObject.protocol=="")){urlObject.host="localhost";}
return urlObject;};OpenLayers.Util.removeTail=function(url){var head=null;var qMark=url.indexOf("?");var hashMark=url.indexOf("#");if(qMark==-1){head=(hashMark!=-1)?url.substr(0,hashMark):url;}else{head=(hashMark!=-1)?url.substr(0,Math.min(qMark,hashMark)):url.substr(0,qMark);}
return head;};OpenLayers.Util.getBrowserName=function(){var browserName="";var ua=navigator.userAgent.toLowerCase();if(ua.indexOf("opera")!=-1){browserName="opera";}else if(ua.indexOf("msie")!=-1){browserName="msie";}else if(ua.indexOf("safari")!=-1){browserName="safari";}else if(ua.indexOf("mozilla")!=-1){if(ua.indexOf("firefox")!=-1){browserName="firefox";}else{browserName="mozilla";}}
-return browserName;};OpenLayers.Util.getRenderedDimensions=function(contentHTML,size){var w=h=null;var container=document.createElement("div");container.style.overflow="";container.style.position="absolute";container.style.left="-9999px";if(size){if(size.w){w=container.style.width=size.w;}else if(size.h){h=container.style.height=size.h;}}
+return browserName;};OpenLayers.Util.getRenderedDimensions=function(contentHTML,size,options){var w,h;var container=document.createElement("div");container.style.overflow="";container.style.position="absolute";container.style.left="-9999px";if(size){if(size.w){w=size.w;container.style.width=w+"px";}else if(size.h){h=size.h
+container.style.height=h+"px";}}
+if(options&&options.displayClass){container.className=options.displayClass;}
var content=document.createElement("div");content.innerHTML=contentHTML;container.appendChild(content);document.body.appendChild(container);if(!w){w=parseInt(content.scrollWidth);container.style.width=w+"px";}
if(!h){h=parseInt(content.scrollHeight);}
container.removeChild(content);document.body.removeChild(container);return new OpenLayers.Size(w,h);};OpenLayers.Util.getScrollbarWidth=function(){var scrollbarWidth=OpenLayers.Util._scrollbarWidth;if(scrollbarWidth==null){var scr=null;var inn=null;var wNoScroll=0;var wScroll=0;scr=document.createElement('div');scr.style.position='absolute';scr.style.top='-1000px';scr.style.left='-1000px';scr.style.width='100px';scr.style.height='50px';scr.style.overflow='hidden';inn=document.createElement('div');inn.style.width='100%';inn.style.height='200px';scr.appendChild(inn);document.body.appendChild(scr);wNoScroll=inn.offsetWidth;scr.style.overflow='scroll';wScroll=inn.offsetWidth;document.body.removeChild(document.body.lastChild);OpenLayers.Util._scrollbarWidth=(wNoScroll-wScroll);scrollbarWidth=OpenLayers.Util._scrollbarWidth;}
-return scrollbarWidth;};OpenLayers.ProxyHost="";OpenLayers.nullHandler=function(request){alert(OpenLayers.i18n("unhandledRequest",{'statusText':request.statusText}));};OpenLayers.loadURL=function(uri,params,caller,onComplete,onFailure){var success=(onComplete)?OpenLayers.Function.bind(onComplete,caller):OpenLayers.nullHandler;var failure=(onFailure)?OpenLayers.Function.bind(onFailure,caller):OpenLayers.nullHandler;var request=new OpenLayers.Ajax.Request(uri,{method:'get',parameters:params,onComplete:success,onFailure:failure});return request.transport;};OpenLayers.parseXMLString=function(text){var index=text.indexOf('<');if(index>0){text=text.substring(index);}
-var ajaxResponse=OpenLayers.Util.Try(function(){var xmldom=new ActiveXObject('Microsoft.XMLDOM');xmldom.loadXML(text);return xmldom;},function(){return new DOMParser().parseFromString(text,'text/xml');},function(){var req=new XMLHttpRequest();req.open("GET","data:"+"text/xml"+";charset=utf-8,"+encodeURIComponent(text),false);if(req.overrideMimeType){req.overrideMimeType("text/xml");}
-req.send(null);return req.responseXML;});return ajaxResponse;};OpenLayers.Ajax={emptyFunction:function(){},getTransport:function(){return OpenLayers.Util.Try(function(){return new XMLHttpRequest();},function(){return new ActiveXObject('Msxml2.XMLHTTP');},function(){return new ActiveXObject('Microsoft.XMLHTTP');})||false;},activeRequestCount:0};OpenLayers.Ajax.Responders={responders:[],register:function(responderToAdd){for(var i=0;i<this.responders.length;i++){if(responderToAdd==this.responders[i]){return;}}
-this.responders.push(responderToAdd);},unregister:function(responderToRemove){OpenLayers.Util.removeItem(this.reponders,responderToRemove);},dispatch:function(callback,request,transport){var responder;for(var i=0;i<this.responders.length;i++){responder=this.responders[i];if(responder[callback]&&typeof responder[callback]=='function'){try{responder[callback].apply(responder,[request,transport]);}catch(e){}}}}};OpenLayers.Ajax.Responders.register({onCreate:function(){OpenLayers.Ajax.activeRequestCount++;},onComplete:function(){OpenLayers.Ajax.activeRequestCount--;}});OpenLayers.Ajax.Base=OpenLayers.Class({initialize:function(options){this.options={method:'post',asynchronous:true,contentType:'application/xml',parameters:''};OpenLayers.Util.extend(this.options,options||{});this.options.method=this.options.method.toLowerCase();if(typeof this.options.parameters=='string'){this.options.parameters=OpenLayers.Util.getParameters(this.options.parameters);}}});OpenLayers.Ajax.Request=OpenLayers.Class(OpenLayers.Ajax.Base,{_complete:false,initialize:function(url,options){OpenLayers.Ajax.Base.prototype.initialize.apply(this,[options]);if(OpenLayers.ProxyHost&&OpenLayers.String.startsWith(url,"http")){url=OpenLayers.ProxyHost+encodeURIComponent(url);}
-this.transport=OpenLayers.Ajax.getTransport();this.request(url);},request:function(url){this.url=url;this.method=this.options.method;var params=OpenLayers.Util.extend({},this.options.parameters);if(this.method!='get'&&this.method!='post'){params['_method']=this.method;this.method='post';}
-this.parameters=params;if(params=OpenLayers.Util.getParameterString(params)){if(this.method=='get'){this.url+=((this.url.indexOf('?')>-1)?'&':'?')+params;}else if(/Konqueror|Safari|KHTML/.test(navigator.userAgent)){params+='&_=';}}
-try{var response=new OpenLayers.Ajax.Response(this);if(this.options.onCreate){this.options.onCreate(response);}
-OpenLayers.Ajax.Responders.dispatch('onCreate',this,response);this.transport.open(this.method.toUpperCase(),this.url,this.options.asynchronous);if(this.options.asynchronous){window.setTimeout(OpenLayers.Function.bind(this.respondToReadyState,this,1),10);}
-this.transport.onreadystatechange=OpenLayers.Function.bind(this.onStateChange,this);this.setRequestHeaders();this.body=this.method=='post'?(this.options.postBody||params):null;this.transport.send(this.body);if(!this.options.asynchronous&&this.transport.overrideMimeType){this.onStateChange();}}catch(e){this.dispatchException(e);}},onStateChange:function(){var readyState=this.transport.readyState;if(readyState>1&&!((readyState==4)&&this._complete)){this.respondToReadyState(this.transport.readyState);}},setRequestHeaders:function(){var headers={'X-Requested-With':'XMLHttpRequest','Accept':'text/javascript, text/html, application/xml, text/xml, */*','OpenLayers':true};if(this.method=='post'){headers['Content-type']=this.options.contentType+
-(this.options.encoding?'; charset='+this.options.encoding:'');if(this.transport.overrideMimeType&&(navigator.userAgent.match(/Gecko\/(\d{4})/)||[0,2005])[1]<2005){headers['Connection']='close';}}
-if(typeof this.options.requestHeaders=='object'){var extras=this.options.requestHeaders;if(typeof extras.push=='function'){for(var i=0,length=extras.length;i<length;i+=2){headers[extras[i]]=extras[i+1];}}else{for(var i in extras){headers[i]=pair[i];}}}
-for(var name in headers){this.transport.setRequestHeader(name,headers[name]);}},success:function(){var status=this.getStatus();return!status||(status>=200&&status<300);},getStatus:function(){try{return this.transport.status||0;}catch(e){return 0;}},respondToReadyState:function(readyState){var state=OpenLayers.Ajax.Request.Events[readyState];var response=new OpenLayers.Ajax.Response(this);if(state=='Complete'){try{this._complete=true;(this.options['on'+response.status]||this.options['on'+(this.success()?'Success':'Failure')]||OpenLayers.Ajax.emptyFunction)(response);}catch(e){this.dispatchException(e);}
-var contentType=response.getHeader('Content-type');}
-try{(this.options['on'+state]||OpenLayers.Ajax.emptyFunction)(response);OpenLayers.Ajax.Responders.dispatch('on'+state,this,response);}catch(e){this.dispatchException(e);}
-if(state=='Complete'){this.transport.onreadystatechange=OpenLayers.Ajax.emptyFunction;}},getHeader:function(name){try{return this.transport.getResponseHeader(name);}catch(e){return null;}},dispatchException:function(exception){var handler=this.options.onException;if(handler){handler(this,exception);OpenLayers.Ajax.Responders.dispatch('onException',this,exception);}else{var listener=false;var responders=OpenLayers.Ajax.Responders.responders;for(var i=0;i<responders.length;i++){if(responders[i].onException){listener=true;break;}}
-if(listener){OpenLayers.Ajax.Responders.dispatch('onException',this,exception);}else{throw exception;}}}});OpenLayers.Ajax.Request.Events=['Uninitialized','Loading','Loaded','Interactive','Complete'];OpenLayers.Ajax.Response=OpenLayers.Class({status:0,statusText:'',initialize:function(request){this.request=request;var transport=this.transport=request.transport,readyState=this.readyState=transport.readyState;if((readyState>2&&!(!!(window.attachEvent&&!window.opera)))||readyState==4){this.status=this.getStatus();this.statusText=this.getStatusText();this.responseText=transport.responseText==null?'':String(transport.responseText);}
-if(readyState==4){var xml=transport.responseXML;this.responseXML=xml===undefined?null:xml;}},getStatus:OpenLayers.Ajax.Request.prototype.getStatus,getStatusText:function(){try{return this.transport.statusText||'';}catch(e){return'';}},getHeader:OpenLayers.Ajax.Request.prototype.getHeader,getResponseHeader:function(name){return this.transport.getResponseHeader(name);}});OpenLayers.Ajax.getElementsByTagNameNS=function(parentnode,nsuri,nsprefix,tagname){var elem=null;if(parentnode.getElementsByTagNameNS){elem=parentnode.getElementsByTagNameNS(nsuri,tagname);}else{elem=parentnode.getElementsByTagName(nsprefix+':'+tagname);}
-return elem;};OpenLayers.Ajax.serializeXMLToString=function(xmldom){var serializer=new XMLSerializer();var data=serializer.serializeToString(xmldom);return data;};OpenLayers.Console={log:function(){},debug:function(){},info:function(){},warn:function(){},error:function(){},assert:function(){},dir:function(){},dirxml:function(){},trace:function(){},group:function(){},groupEnd:function(){},time:function(){},timeEnd:function(){},profile:function(){},profileEnd:function(){},count:function(){},CLASS_NAME:"OpenLayers.Console"};(function(){if(window.console){var scripts=document.getElementsByTagName("script");for(var i=0;i<scripts.length;++i){if(scripts[i].src.indexOf("firebug.js")!=-1){OpenLayers.Util.extend(OpenLayers.Console,console);break;}}}})();OpenLayers.Size=OpenLayers.Class({w:0.0,h:0.0,initialize:function(w,h){this.w=parseFloat(w);this.h=parseFloat(h);},toString:function(){return("w="+this.w+",h="+this.h);},clone:function(){return new OpenLayers.Size(this.w,this.h);},equals:function(sz){var equals=false;if(sz!=null){equals=((this.w==sz.w&&this.h==sz.h)||(isNaN(this.w)&&isNaN(this.h)&&isNaN(sz.w)&&isNaN(sz.h)));}
+return scrollbarWidth;};OpenLayers.Console={log:function(){},debug:function(){},info:function(){},warn:function(){},error:function(){},userError:function(error){alert(error);},assert:function(){},dir:function(){},dirxml:function(){},trace:function(){},group:function(){},groupEnd:function(){},time:function(){},timeEnd:function(){},profile:function(){},profileEnd:function(){},count:function(){},CLASS_NAME:"OpenLayers.Console"};(function(){if(window.console){var scripts=document.getElementsByTagName("script");for(var i=0,len=scripts.length;i<len;++i){if(scripts[i].src.indexOf("firebug.js")!=-1){OpenLayers.Util.extend(OpenLayers.Console,console);break;}}}})();OpenLayers.String={startsWith:function(str,sub){return(str.indexOf(sub)==0);},contains:function(str,sub){return(str.indexOf(sub)!=-1);},trim:function(str){return str.replace(/^\s*(.*?)\s*$/,"$1");},camelize:function(str){var oStringList=str.split('-');var camelizedString=oStringList[0];for(var i=1,len=oStringList.length;i<len;i++){var s=oStringList[i];camelizedString+=s.charAt(0).toUpperCase()+s.substring(1);}
+return camelizedString;},format:function(template,context,args){if(!context){context=window;}
+var tokens=template.split("${");var item,last,replacement;for(var i=1,len=tokens.length;i<len;i++){item=tokens[i];last=item.indexOf("}");if(last>0){replacement=context[item.substring(0,last)];if(typeof replacement=="function"){replacement=args?replacement.apply(null,args):replacement();}
+tokens[i]=replacement+item.substring(++last);}else{tokens[i]="${"+item;}}
+return tokens.join("");},numberRegEx:/^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/,isNumeric:function(value){return OpenLayers.String.numberRegEx.test(value);}};if(!String.prototype.startsWith){String.prototype.startsWith=function(sStart){OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",{'newMethod':'OpenLayers.String.startsWith'}));return OpenLayers.String.startsWith(this,sStart);};}
+if(!String.prototype.contains){String.prototype.contains=function(str){OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",{'newMethod':'OpenLayers.String.contains'}));return OpenLayers.String.contains(this,str);};}
+if(!String.prototype.trim){String.prototype.trim=function(){OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",{'newMethod':'OpenLayers.String.trim'}));return OpenLayers.String.trim(this);};}
+if(!String.prototype.camelize){String.prototype.camelize=function(){OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",{'newMethod':'OpenLayers.String.camelize'}));return OpenLayers.String.camelize(this);};}
+OpenLayers.Number={decimalSeparator:".",thousandsSeparator:",",limitSigDigs:function(num,sig){var fig=0;if(sig>0){fig=parseFloat(num.toPrecision(sig));}
+return fig;},format:function(num,dec,tsep,dsep){dec=(typeof dec!="undefined")?dec:0;tsep=(typeof tsep!="undefined")?tsep:OpenLayers.Number.thousandsSeparator;dsep=(typeof dsep!="undefined")?dsep:OpenLayers.Number.decimalSeparator;if(dec!=null){num=parseFloat(num.toFixed(dec));}
+var parts=num.toString().split(".");if(parts.length==1&&dec==null){dec=0;}
+var integer=parts[0];if(tsep){var thousands=/(-?[0-9]+)([0-9]{3})/;while(thousands.test(integer)){integer=integer.replace(thousands,"$1"+tsep+"$2");}}
+var str;if(dec==0){str=integer;}else{var rem=parts.length>1?parts[1]:"0";if(dec!=null){rem=rem+new Array(dec-rem.length+1).join("0");}
+str=integer+dsep+rem;}
+return str;}};if(!Number.prototype.limitSigDigs){Number.prototype.limitSigDigs=function(sig){OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",{'newMethod':'OpenLayers.String.limitSigDigs'}));return OpenLayers.Number.limitSigDigs(this,sig);};}
+OpenLayers.Function={bind:function(func,object){var args=Array.prototype.slice.apply(arguments,[2]);return function(){var newArgs=args.concat(Array.prototype.slice.apply(arguments,[0]));return func.apply(object,newArgs);};},bindAsEventListener:function(func,object){return function(event){return func.call(object,event||window.event);};}};if(!Function.prototype.bind){Function.prototype.bind=function(){OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",{'newMethod':'OpenLayers.String.bind'}));Array.prototype.unshift.apply(arguments,[this]);return OpenLayers.Function.bind.apply(null,arguments);};}
+if(!Function.prototype.bindAsEventListener){Function.prototype.bindAsEventListener=function(object){OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",{'newMethod':'OpenLayers.String.bindAsEventListener'}));return OpenLayers.Function.bindAsEventListener(this,object);};}
+OpenLayers.Array={filter:function(array,callback,caller){var selected=[];if(Array.prototype.filter){selected=array.filter(callback,caller);}else{var len=array.length;if(typeof callback!="function"){throw new TypeError();}
+for(var i=0;i<len;i++){if(i in array){var val=array[i];if(callback.call(caller,val,i,array)){selected.push(val);}}}}
+return selected;}};OpenLayers.Class=function(){var Class=function(){if(arguments&&arguments[0]!=OpenLayers.Class.isPrototype){this.initialize.apply(this,arguments);}};var extended={};var parent;for(var i=0,len=arguments.length;i<len;++i){if(typeof arguments[i]=="function"){parent=arguments[i].prototype;}else{parent=arguments[i];}
+OpenLayers.Util.extend(extended,parent);}
+Class.prototype=extended;return Class;};OpenLayers.Class.isPrototype=function(){};OpenLayers.Class.create=function(){return function(){if(arguments&&arguments[0]!=OpenLayers.Class.isPrototype){this.initialize.apply(this,arguments);}};};OpenLayers.Class.inherit=function(){var superClass=arguments[0];var proto=new superClass(OpenLayers.Class.isPrototype);for(var i=1,len=arguments.length;i<len;i++){if(typeof arguments[i]=="function"){var mixin=arguments[i];arguments[i]=new mixin(OpenLayers.Class.isPrototype);}
+OpenLayers.Util.extend(proto,arguments[i]);}
+return proto;};OpenLayers.Size=OpenLayers.Class({w:0.0,h:0.0,initialize:function(w,h){this.w=parseFloat(w);this.h=parseFloat(h);},toString:function(){return("w="+this.w+",h="+this.h);},clone:function(){return new OpenLayers.Size(this.w,this.h);},equals:function(sz){var equals=false;if(sz!=null){equals=((this.w==sz.w&&this.h==sz.h)||(isNaN(this.w)&&isNaN(this.h)&&isNaN(sz.w)&&isNaN(sz.h)));}
return equals;},CLASS_NAME:"OpenLayers.Size"});OpenLayers.Bounds=OpenLayers.Class({left:null,bottom:null,right:null,top:null,initialize:function(left,bottom,right,top){if(left!=null){this.left=parseFloat(left);}
if(bottom!=null){this.bottom=parseFloat(bottom);}
if(right!=null){this.right=parseFloat(right);}
@@ -174,7 +173,9 @@
var mult=Math.pow(10,decimal);var bbox=Math.round(this.left*mult)/mult+","+
Math.round(this.bottom*mult)/mult+","+
Math.round(this.right*mult)/mult+","+
-Math.round(this.top*mult)/mult;return bbox;},toGeometry:function(){return new OpenLayers.Geometry.Polygon([new OpenLayers.Geometry.LinearRing([new OpenLayers.Geometry.Point(this.left,this.bottom),new OpenLayers.Geometry.Point(this.right,this.bottom),new OpenLayers.Geometry.Point(this.right,this.top),new OpenLayers.Geometry.Point(this.left,this.top)])]);},getWidth:function(){return(this.right-this.left);},getHeight:function(){return(this.top-this.bottom);},getSize:function(){return new OpenLayers.Size(this.getWidth(),this.getHeight());},getCenterPixel:function(){return new OpenLayers.Pixel((this.left+this.right)/2,(this.bottom+this.top)/2);},getCenterLonLat:function(){return new OpenLayers.LonLat((this.left+this.right)/2,(this.bottom+this.top)/2);},add:function(x,y){if((x==null)||(y==null)){var msg=OpenLayers.i18n("boundsAddError");OpenLayers.Console.error(msg);return null;}
+Math.round(this.top*mult)/mult;return bbox;},toGeometry:function(){return new OpenLayers.Geometry.Polygon([new OpenLayers.Geometry.LinearRing([new OpenLayers.Geometry.Point(this.left,this.bottom),new OpenLayers.Geometry.Point(this.right,this.bottom),new OpenLayers.Geometry.Point(this.right,this.top),new OpenLayers.Geometry.Point(this.left,this.top)])]);},getWidth:function(){return(this.right-this.left);},getHeight:function(){return(this.top-this.bottom);},getSize:function(){return new OpenLayers.Size(this.getWidth(),this.getHeight());},getCenterPixel:function(){return new OpenLayers.Pixel((this.left+this.right)/2,(this.bottom+this.top)/2);},getCenterLonLat:function(){return new OpenLayers.LonLat((this.left+this.right)/2,(this.bottom+this.top)/2);},scale:function(ratio,origin){if(origin==null){origin=this.getCenterLonLat();}
+var bounds=[];var origx,origy;if(origin.CLASS_NAME=="OpenLayers.LonLat"){origx=origin.lon;origy=origin.lat;}else{origx=origin.x;origy=origin.y;}
+var left=(this.left-origx)*ratio+origx;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);},add:function(x,y){if((x==null)||(y==null)){var msg=OpenLayers.i18n("boundsAddError");OpenLayers.Console.error(msg);return null;}
return new OpenLayers.Bounds(this.left+x,this.bottom+y,this.right+x,this.top+y);},extend:function(object){var bounds=null;if(object){switch(object.CLASS_NAME){case"OpenLayers.LonLat":bounds=new OpenLayers.Bounds(object.lon,object.lat,object.lon,object.lat);break;case"OpenLayers.Geometry.Point":bounds=new OpenLayers.Bounds(object.x,object.y,object.x,object.y);break;case"OpenLayers.Bounds":bounds=object;break;}
if(bounds){if((this.left==null)||(bounds.left<this.left)){this.left=bounds.left;}
if((this.bottom==null)||(bounds.bottom<this.bottom)){this.bottom=bounds.bottom;}
@@ -187,9 +188,12 @@
var inLeft;var inTop;var inRight;var inBottom;if(inclusive){inLeft=(bounds.left>=this.left)&&(bounds.left<=this.right);inTop=(bounds.top>=this.bottom)&&(bounds.top<=this.top);inRight=(bounds.right>=this.left)&&(bounds.right<=this.right);inBottom=(bounds.bottom>=this.bottom)&&(bounds.bottom<=this.top);}else{inLeft=(bounds.left>this.left)&&(bounds.left<this.right);inTop=(bounds.top>this.bottom)&&(bounds.top<this.top);inRight=(bounds.right>this.left)&&(bounds.right<this.right);inBottom=(bounds.bottom>this.bottom)&&(bounds.bottom<this.top);}
return(partial)?(inTop||inBottom)&&(inLeft||inRight):(inTop&&inLeft&&inBottom&&inRight);},determineQuadrant:function(lonlat){var quadrant="";var center=this.getCenterLonLat();quadrant+=(lonlat.lat<center.lat)?"b":"t";quadrant+=(lonlat.lon<center.lon)?"l":"r";return quadrant;},transform:function(source,dest){var ll=OpenLayers.Projection.transform({'x':this.left,'y':this.bottom},source,dest);var lr=OpenLayers.Projection.transform({'x':this.right,'y':this.bottom},source,dest);var ul=OpenLayers.Projection.transform({'x':this.left,'y':this.top},source,dest);var ur=OpenLayers.Projection.transform({'x':this.right,'y':this.top},source,dest);this.left=Math.min(ll.x,ul.x);this.bottom=Math.min(ll.y,lr.y);this.right=Math.max(lr.x,ur.x);this.top=Math.max(ul.y,ur.y);return this;},wrapDateLine:function(maxExtent,options){options=options||{};var leftTolerance=options.leftTolerance||0;var rightTolerance=options.rightTolerance||0;var newBounds=this.clone();if(maxExtent){while(newBounds.left<maxExtent.left&&(newBounds.right-rightTolerance)<=maxExtent.left){newBounds=newBounds.add(maxExtent.getWidth(),0);}
while((newBounds.left+leftTolerance)>=maxExtent.right&&newBounds.right>maxExtent.right){newBounds=newBounds.add(-maxExtent.getWidth(),0);}}
-return newBounds;},CLASS_NAME:"OpenLayers.Bounds"});OpenLayers.Bounds.fromString=function(str){var bounds=str.split(",");return OpenLayers.Bounds.fromArray(bounds);};OpenLayers.Bounds.fromArray=function(bbox){return new OpenLayers.Bounds(parseFloat(bbox[0]),parseFloat(bbox[1]),parseFloat(bbox[2]),parseFloat(bbox[3]));};OpenLayers.Bounds.fromSize=function(size){return new OpenLayers.Bounds(0,size.h,size.w,0);};OpenLayers.Bounds.oppositeQuadrant=function(quadrant){var opp="";opp+=(quadrant.charAt(0)=='t')?'b':'t';opp+=(quadrant.charAt(1)=='l')?'r':'l';return opp;};OpenLayers.Element={visible:function(element){return OpenLayers.Util.getElement(element).style.display!='none';},toggle:function(){for(var i=0;i<arguments.length;i++){var element=OpenLayers.Util.getElement(arguments[i]);var display=OpenLayers.Element.visible(element)?'hide':'show';OpenLayers.Element[display](element);}},hide:function(){for(var i=0;i<arguments.length;i++){var element=OpenLayers.Util.getElement(arguments[i]);element.style.display='none';}},show:function(){for(var i=0;i<arguments.length;i++){var element=OpenLayers.Util.getElement(arguments[i]);element.style.display='';}},remove:function(element){element=OpenLayers.Util.getElement(element);element.parentNode.removeChild(element);},getHeight:function(element){element=OpenLayers.Util.getElement(element);return element.offsetHeight;},getDimensions:function(element){element=OpenLayers.Util.getElement(element);if(OpenLayers.Element.getStyle(element,'display')!='none'){return{width:element.offsetWidth,height:element.offsetHeight};}
-var els=element.style;var originalVisibility=els.visibility;var originalPosition=els.position;els.visibility='hidden';els.position='absolute';els.display='';var originalWidth=element.clientWidth;var originalHeight=element.clientHeight;els.display='none';els.position=originalPosition;els.visibility=originalVisibility;return{width:originalWidth,height:originalHeight};},getStyle:function(element,style){element=OpenLayers.Util.getElement(element);var value=element.style[OpenLayers.String.camelize(style)];if(!value){if(document.defaultView&&document.defaultView.getComputedStyle){var css=document.defaultView.getComputedStyle(element,null);value=css?css.getPropertyValue(style):null;}else if(element.currentStyle){value=element.currentStyle[OpenLayers.String.camelize(style)];}}
-var positions=['left','top','right','bottom'];if(window.opera&&(OpenLayers.Util.indexOf(positions,style)!=-1)&&(OpenLayers.Element.getStyle(element,'position')=='static')){value='auto';}
+return newBounds;},CLASS_NAME:"OpenLayers.Bounds"});OpenLayers.Bounds.fromString=function(str){var bounds=str.split(",");return OpenLayers.Bounds.fromArray(bounds);};OpenLayers.Bounds.fromArray=function(bbox){return new OpenLayers.Bounds(parseFloat(bbox[0]),parseFloat(bbox[1]),parseFloat(bbox[2]),parseFloat(bbox[3]));};OpenLayers.Bounds.fromSize=function(size){return new OpenLayers.Bounds(0,size.h,size.w,0);};OpenLayers.Bounds.oppositeQuadrant=function(quadrant){var opp="";opp+=(quadrant.charAt(0)=='t')?'b':'t';opp+=(quadrant.charAt(1)=='l')?'r':'l';return opp;};OpenLayers.Element={visible:function(element){return OpenLayers.Util.getElement(element).style.display!='none';},toggle:function(){for(var i=0,len=arguments.length;i<len;i++){var element=OpenLayers.Util.getElement(arguments[i]);var display=OpenLayers.Element.visible(element)?'hide':'show';OpenLayers.Element[display](element);}},hide:function(){for(var i=0,len=arguments.length;i<len;i++){var element=OpenLayers.Util.getElement(arguments[i]);element.style.display='none';}},show:function(){for(var i=0,len=arguments.length;i<len;i++){var element=OpenLayers.Util.getElement(arguments[i]);element.style.display='';}},remove:function(element){element=OpenLayers.Util.getElement(element);element.parentNode.removeChild(element);},getHeight:function(element){element=OpenLayers.Util.getElement(element);return element.offsetHeight;},getDimensions:function(element){element=OpenLayers.Util.getElement(element);if(OpenLayers.Element.getStyle(element,'display')!='none'){return{width:element.offsetWidth,height:element.offsetHeight};}
+var els=element.style;var originalVisibility=els.visibility;var originalPosition=els.position;els.visibility='hidden';els.position='absolute';els.display='';var originalWidth=element.clientWidth;var originalHeight=element.clientHeight;els.display='none';els.position=originalPosition;els.visibility=originalVisibility;return{width:originalWidth,height:originalHeight};},hasClass:function(element,name){var names=element.className;return(!!names&&new RegExp("(^|\\s)"+name+"(\\s|$)").test(names));},addClass:function(element,name){if(!OpenLayers.Element.hasClass(element,name)){element.className+=(element.className?" ":"")+name;}
+return element;},removeClass:function(element,name){var names=element.className;if(names){element.className=OpenLayers.String.trim(names.replace(new RegExp("(^|\\s+)"+name+"(\\s+|$)")," "));}
+return element;},toggleClass:function(element,name){if(OpenLayers.Element.hasClass(element,name)){OpenLayers.Element.removeClass(element,name);}else{OpenLayers.Element.addClass(element,name);}
+return element;},getStyle:function(element,style){element=OpenLayers.Util.getElement(element);var value=null;if(element&&element.style){value=element.style[OpenLayers.String.camelize(style)];if(!value){if(document.defaultView&&document.defaultView.getComputedStyle){var css=document.defaultView.getComputedStyle(element,null);value=css?css.getPropertyValue(style):null;}else if(element.currentStyle){value=element.currentStyle[OpenLayers.String.camelize(style)];}}
+var positions=['left','top','right','bottom'];if(window.opera&&(OpenLayers.Util.indexOf(positions,style)!=-1)&&(OpenLayers.Element.getStyle(element,'position')=='static')){value='auto';}}
return value=='auto'?null:value;}};OpenLayers.LonLat=OpenLayers.Class({lon:0.0,lat:0.0,initialize:function(lon,lat){this.lon=parseFloat(lon);this.lat=parseFloat(lat);},toString:function(){return("lon="+this.lon+",lat="+this.lat);},toShortString:function(){return(this.lon+", "+this.lat);},clone:function(){return new OpenLayers.LonLat(this.lon,this.lat);},add:function(lon,lat){if((lon==null)||(lat==null)){var msg=OpenLayers.i18n("lonlatAddError");OpenLayers.Console.error(msg);return null;}
return new OpenLayers.LonLat(this.lon+lon,this.lat+lat);},equals:function(ll){var equals=false;if(ll!=null){equals=((this.lon==ll.lon&&this.lat==ll.lat)||(isNaN(this.lon)&&isNaN(this.lat)&&isNaN(ll.lon)&&isNaN(ll.lat)));}
return equals;},transform:function(source,dest){var point=OpenLayers.Projection.transform({'x':this.lon,'y':this.lat},source,dest);this.lon=point.x;this.lat=point.y;return this;},wrapDateLine:function(maxExtent){var newLonLat=this.clone();if(maxExtent){while(newLonLat.lon<maxExtent.left){newLonLat.lon+=maxExtent.getWidth();}
@@ -198,7 +202,7 @@
return equals;},add:function(x,y){if((x==null)||(y==null)){var msg=OpenLayers.i18n("pixelAddError");OpenLayers.Console.error(msg);return null;}
return new OpenLayers.Pixel(this.x+x,this.y+y);},offset:function(px){var newPx=this.clone();if(px){newPx=this.add(px.x,px.y);}
return newPx;},CLASS_NAME:"OpenLayers.Pixel"});OpenLayers.Control=OpenLayers.Class({id:null,map:null,div:null,type:null,allowSelection:false,displayClass:"",title:"",active:null,handler:null,eventListeners:null,events:null,EVENT_TYPES:["activate","deactivate"],initialize:function(options){this.displayClass=this.CLASS_NAME.replace("OpenLayers.","ol").replace(/\./g,"");OpenLayers.Util.extend(this,options);this.events=new OpenLayers.Events(this,null,this.EVENT_TYPES);if(this.eventListeners instanceof Object){this.events.on(this.eventListeners);}
-this.id=OpenLayers.Util.createUniqueID(this.CLASS_NAME+"_");},destroy:function(){if(this.events){if(this.eventListeners){this.events.un(this.eventListeners);}
+if(this.id==null){this.id=OpenLayers.Util.createUniqueID(this.CLASS_NAME+"_");}},destroy:function(){if(this.events){if(this.eventListeners){this.events.un(this.eventListeners);}
this.events.destroy();this.events=null;}
this.eventListeners=null;if(this.handler){this.handler.destroy();this.handler=null;}
if(this.handlers){for(var key in this.handlers){if(this.handlers.hasOwnProperty(key)&&typeof this.handlers[key].destroy=="function"){this.handlers[key].destroy();}}
@@ -221,7 +225,54 @@
if(!lang){OpenLayers.Console.warn('Failed to find OpenLayers.Lang.'+parts.join("-")+' dictionary, falling back to default language');lang=OpenLayers.Lang.defaultCode;}
OpenLayers.Lang.code=lang;},translate:function(key,context){var dictionary=OpenLayers.Lang[OpenLayers.Lang.getCode()];var message=dictionary[key];if(!message){message=key;}
if(context){message=OpenLayers.String.format(message,context);}
-return message;}};OpenLayers.i18n=OpenLayers.Lang.translate;OpenLayers.Tween=OpenLayers.Class({INTERVAL:10,easing:null,begin:null,finish:null,duration:null,callbacks:null,time:null,interval:null,playing:false,initialize:function(easing){this.easing=(easing)?easing:OpenLayers.Easing.Expo.easeOut;},start:function(begin,finish,duration,options){this.playing=true;this.begin=begin;this.finish=finish;this.duration=duration;this.callbacks=options.callbacks;this.time=0;if(this.interval){window.clearInterval(this.interval);this.interval=null;}
+return message;}};OpenLayers.i18n=OpenLayers.Lang.translate;OpenLayers.Popup=OpenLayers.Class({events:null,id:"",lonlat:null,div:null,contentSize:null,size:null,contentHTML:null,backgroundColor:"",opacity:"",border:"",contentDiv:null,groupDiv:null,closeDiv:null,autoSize:false,minSize:null,maxSize:null,displayClass:"olPopup",contentDisplayClass:"olPopupContent",padding:0,fixPadding:function(){if(typeof this.padding=="number"){this.padding=new OpenLayers.Bounds(this.padding,this.padding,this.padding,this.padding);}},panMapIfOutOfView:false,map:null,initialize:function(id,lonlat,contentSize,contentHTML,closeBox,closeBoxCallback){if(id==null){id=OpenLayers.Util.createUniqueID(this.CLASS_NAME+"_");}
+this.id=id;this.lonlat=lonlat;this.contentSize=(contentSize!=null)?contentSize:new OpenLayers.Size(OpenLayers.Popup.WIDTH,OpenLayers.Popup.HEIGHT);if(contentHTML!=null){this.contentHTML=contentHTML;}
+this.backgroundColor=OpenLayers.Popup.COLOR;this.opacity=OpenLayers.Popup.OPACITY;this.border=OpenLayers.Popup.BORDER;this.div=OpenLayers.Util.createDiv(this.id,null,null,null,null,null,"hidden");this.div.className=this.displayClass;var groupDivId=this.id+"_GroupDiv";this.groupDiv=OpenLayers.Util.createDiv(groupDivId,null,null,null,"relative",null,"hidden");var id=this.div.id+"_contentDiv";this.contentDiv=OpenLayers.Util.createDiv(id,null,this.contentSize.clone(),null,"relative");this.contentDiv.className=this.contentDisplayClass;this.groupDiv.appendChild(this.contentDiv);this.div.appendChild(this.groupDiv);if(closeBox){this.addCloseBox(closeBoxCallback);}
+this.registerEvents();},destroy:function(){this.id=null;this.lonlat=null;this.size=null;this.contentHTML=null;this.backgroundColor=null;this.opacity=null;this.border=null;this.events.destroy();this.events=null;if(this.closeDiv){OpenLayers.Event.stopObservingElement(this.closeDiv);this.groupDiv.removeChild(this.closeDiv);}
+this.closeDiv=null;this.div.removeChild(this.groupDiv);this.groupDiv=null;if(this.map!=null){this.map.removePopup(this);}
+this.map=null;this.div=null;this.autoSize=null;this.minSize=null;this.maxSize=null;this.padding=null;this.panMapIfOutOfView=null;},draw:function(px){if(px==null){if((this.lonlat!=null)&&(this.map!=null)){px=this.map.getLayerPxFromLonLat(this.lonlat);}}
+if(OpenLayers.Util.getBrowserName()=='firefox'){this.map.events.register("movestart",this,function(){var style=document.defaultView.getComputedStyle(this.contentDiv,null);var currentOverflow=style.getPropertyValue("overflow");if(currentOverflow!="hidden"){this.contentDiv._oldOverflow=currentOverflow;this.contentDiv.style.overflow="hidden";}});this.map.events.register("moveend",this,function(){var oldOverflow=this.contentDiv._oldOverflow;if(oldOverflow){this.contentDiv.style.overflow=oldOverflow;this.contentDiv._oldOverflow=null;}});}
+this.moveTo(px);if(!this.autoSize&&!this.size){this.setSize(this.contentSize);}
+this.setBackgroundColor();this.setOpacity();this.setBorder();this.setContentHTML();if(this.panMapIfOutOfView){this.panIntoView();}
+return this.div;},updatePosition:function(){if((this.lonlat)&&(this.map)){var px=this.map.getLayerPxFromLonLat(this.lonlat);if(px){this.moveTo(px);}}},moveTo:function(px){if((px!=null)&&(this.div!=null)){this.div.style.left=px.x+"px";this.div.style.top=px.y+"px";}},visible:function(){return OpenLayers.Element.visible(this.div);},toggle:function(){if(this.visible()){this.hide();}else{this.show();}},show:function(){OpenLayers.Element.show(this.div);if(this.panMapIfOutOfView){this.panIntoView();}},hide:function(){OpenLayers.Element.hide(this.div);},setSize:function(contentSize){this.size=contentSize.clone();var contentDivPadding=this.getContentDivPadding();var wPadding=contentDivPadding.left+contentDivPadding.right;var hPadding=contentDivPadding.top+contentDivPadding.bottom;this.fixPadding();wPadding+=this.padding.left+this.padding.right;hPadding+=this.padding.top+this.padding.bottom;if(this.closeDiv){var closeDivWidth=parseInt(this.closeDiv.style.width);wPadding+=closeDivWidth+contentDivPadding.right;}
+this.size.w+=wPadding;this.size.h+=hPadding;if(OpenLayers.Util.getBrowserName()=="msie"){this.contentSize.w+=contentDivPadding.left+contentDivPadding.right;this.contentSize.h+=contentDivPadding.bottom+contentDivPadding.top;}
+if(this.div!=null){this.div.style.width=this.size.w+"px";this.div.style.height=this.size.h+"px";}
+if(this.contentDiv!=null){this.contentDiv.style.width=contentSize.w+"px";this.contentDiv.style.height=contentSize.h+"px";}},updateSize:function(){var preparedHTML="<div class='"+this.contentDisplayClass+"'>"+
+this.contentDiv.innerHTML+"<div>";var realSize=OpenLayers.Util.getRenderedDimensions(preparedHTML,null,{displayClass:this.displayClass});var safeSize=this.getSafeContentSize(realSize);var newSize=null;if(safeSize.equals(realSize)){newSize=realSize;}else{var fixedSize=new OpenLayers.Size();fixedSize.w=(safeSize.w<realSize.w)?safeSize.w:null;fixedSize.h=(safeSize.h<realSize.h)?safeSize.h:null;if(fixedSize.w&&fixedSize.h){newSize=safeSize;}else{var clippedSize=OpenLayers.Util.getRenderedDimensions(preparedHTML,fixedSize,{displayClass:this.contentDisplayClass});var currentOverflow=OpenLayers.Element.getStyle(this.contentDiv,"overflow");if((currentOverflow!="hidden")&&(clippedSize.equals(safeSize))){var scrollBar=OpenLayers.Util.getScrollbarWidth();if(fixedSize.w){clippedSize.h+=scrollBar;}else{clippedSize.w+=scrollBar;}}
+newSize=this.getSafeContentSize(clippedSize);}}
+this.setSize(newSize);},setBackgroundColor:function(color){if(color!=undefined){this.backgroundColor=color;}
+if(this.div!=null){this.div.style.backgroundColor=this.backgroundColor;}},setOpacity:function(opacity){if(opacity!=undefined){this.opacity=opacity;}
+if(this.div!=null){this.div.style.opacity=this.opacity;this.div.style.filter='alpha(opacity='+this.opacity*100+')';}},setBorder:function(border){if(border!=undefined){this.border=border;}
+if(this.div!=null){this.div.style.border=this.border;}},setContentHTML:function(contentHTML){if(contentHTML!=null){this.contentHTML=contentHTML;}
+if((this.contentDiv!=null)&&(this.contentHTML!=null)&&(this.contentHTML!=this.contentDiv.innerHTML)){this.contentDiv.innerHTML=this.contentHTML;if(this.autoSize){this.registerImageListeners();this.updateSize();}}},registerImageListeners:function(){var onImgLoad=function(){this.popup.updateSize();if(this.popup.visible()&&this.popup.panMapIfOutOfView){this.popup.panIntoView();}
+OpenLayers.Event.stopObserving(this.img,"load",this.img._onImageLoad);};var images=this.contentDiv.getElementsByTagName("img");for(var i=0,len=images.length;i<len;i++){var img=images[i];if(img.width==0||img.height==0){var context={'popup':this,'img':img};img._onImgLoad=OpenLayers.Function.bind(onImgLoad,context);OpenLayers.Event.observe(img,'load',img._onImgLoad);}}},getSafeContentSize:function(size){var safeContentSize=size.clone();var contentDivPadding=this.getContentDivPadding();var wPadding=contentDivPadding.left+contentDivPadding.right;var hPadding=contentDivPadding.top+contentDivPadding.bottom;this.fixPadding();wPadding+=this.padding.left+this.padding.right;hPadding+=this.padding.top+this.padding.bottom;if(this.closeDiv){var closeDivWidth=parseInt(this.closeDiv.style.width);wPadding+=closeDivWidth+contentDivPadding.right;}
+if(this.minSize){safeContentSize.w=Math.max(safeContentSize.w,(this.minSize.w-wPadding));safeContentSize.h=Math.max(safeContentSize.h,(this.minSize.h-hPadding));}
+if(this.maxSize){safeContentSize.w=Math.min(safeContentSize.w,(this.maxSize.w-wPadding));safeContentSize.h=Math.min(safeContentSize.h,(this.maxSize.h-hPadding));}
+if(this.map&&this.map.size){var maxY=this.map.size.h-
+this.map.paddingForPopups.top-
+this.map.paddingForPopups.bottom-
+hPadding;var maxX=this.map.size.w-
+this.map.paddingForPopups.left-
+this.map.paddingForPopups.right-
+wPadding;safeContentSize.w=Math.min(safeContentSize.w,maxX);safeContentSize.h=Math.min(safeContentSize.h,maxY);}
+return safeContentSize;},getContentDivPadding:function(){var contentDivPadding=this._contentDivPadding;if(!contentDivPadding){this.div.style.display="none";document.body.appendChild(this.div);contentDivPadding=new OpenLayers.Bounds(OpenLayers.Element.getStyle(this.contentDiv,"padding-left"),OpenLayers.Element.getStyle(this.contentDiv,"padding-bottom"),OpenLayers.Element.getStyle(this.contentDiv,"padding-right"),OpenLayers.Element.getStyle(this.contentDiv,"padding-top"));this._contentDivPadding=contentDivPadding;document.body.removeChild(this.div);this.div.style.display="";}
+return contentDivPadding;},addCloseBox:function(callback){this.closeDiv=OpenLayers.Util.createDiv(this.id+"_close",null,new OpenLayers.Size(17,17));this.closeDiv.className="olPopupCloseBox";var contentDivPadding=this.getContentDivPadding();this.closeDiv.style.right=contentDivPadding.right+"px";this.closeDiv.style.top=contentDivPadding.top+"px";this.groupDiv.appendChild(this.closeDiv);var closePopup=callback||function(e){this.hide();OpenLayers.Event.stop(e);};OpenLayers.Event.observe(this.closeDiv,"click",OpenLayers.Function.bindAsEventListener(closePopup,this));},panIntoView:function(){var mapSize=this.map.getSize();var origTL=this.map.getViewPortPxFromLayerPx(new OpenLayers.Pixel(parseInt(this.div.style.left),parseInt(this.div.style.top)));var newTL=origTL.clone();if(origTL.x<this.map.paddingForPopups.left){newTL.x=this.map.paddingForPopups.left;}else
+if((origTL.x+this.size.w)>(mapSize.w-this.map.paddingForPopups.right)){newTL.x=mapSize.w-this.map.paddingForPopups.right-this.size.w;}
+if(origTL.y<this.map.paddingForPopups.top){newTL.y=this.map.paddingForPopups.top;}else
+if((origTL.y+this.size.h)>(mapSize.h-this.map.paddingForPopups.bottom)){newTL.y=mapSize.h-this.map.paddingForPopups.bottom-this.size.h;}
+var dx=origTL.x-newTL.x;var dy=origTL.y-newTL.y;this.map.pan(dx,dy);},registerEvents:function(){this.events=new OpenLayers.Events(this,this.div,null,true);this.events.on({"mousedown":this.onmousedown,"mousemove":this.onmousemove,"mouseup":this.onmouseup,"click":this.onclick,"mouseout":this.onmouseout,"dblclick":this.ondblclick,scope:this});},onmousedown:function(evt){this.mousedown=true;OpenLayers.Event.stop(evt,true);},onmousemove:function(evt){if(this.mousedown){OpenLayers.Event.stop(evt,true);}},onmouseup:function(evt){if(this.mousedown){this.mousedown=false;OpenLayers.Event.stop(evt,true);}},onclick:function(evt){OpenLayers.Event.stop(evt,true);},onmouseout:function(evt){this.mousedown=false;},ondblclick:function(evt){OpenLayers.Event.stop(evt,true);},CLASS_NAME:"OpenLayers.Popup"});OpenLayers.Popup.WIDTH=200;OpenLayers.Popup.HEIGHT=200;OpenLayers.Popup.COLOR="white";OpenLayers.Popup.OPACITY=1;OpenLayers.Popup.BORDER="0px";OpenLayers.Renderer=OpenLayers.Class({container:null,extent:null,locked:false,size:null,resolution:null,map:null,initialize:function(containerID,options){this.container=OpenLayers.Util.getElement(containerID);},destroy:function(){this.container=null;this.extent=null;this.size=null;this.resolution=null;this.map=null;},supported:function(){return false;},setExtent:function(extent,resolutionChanged){this.extent=extent.clone();if(resolutionChanged){this.resolution=null;}},setSize:function(size){this.size=size.clone();this.resolution=null;},getResolution:function(){this.resolution=this.resolution||this.map.getResolution();return this.resolution;},drawFeature:function(feature,style){if(style==null){style=feature.style;}
+if(feature.geometry){if(!feature.geometry.getBounds().intersectsBounds(this.extent)){style={display:"none"};}
+return this.drawGeometry(feature.geometry,style,feature.id);}},drawGeometry:function(geometry,style,featureId){},clear:function(){},getFeatureIdFromEvent:function(evt){},eraseFeatures:function(features){if(!(features instanceof Array)){features=[features];}
+for(var i=0,len=features.length;i<len;++i){this.eraseGeometry(features[i].geometry);}},eraseGeometry:function(geometry){},CLASS_NAME:"OpenLayers.Renderer"});OpenLayers.Request={DEFAULT_CONFIG:{method:"GET",url:window.location.href,async:true,user:undefined,password:undefined,params:null,proxy:OpenLayers.ProxyHost,headers:{},data:null,callback:function(){},success:null,failure:null,scope:null},issue:function(config){var defaultConfig=OpenLayers.Util.extend(this.DEFAULT_CONFIG,{proxy:OpenLayers.ProxyHost});config=OpenLayers.Util.applyDefaults(config,defaultConfig);var request=new OpenLayers.Request.XMLHttpRequest();var url=config.url;if(config.params){url+="?"+OpenLayers.Util.getParameterString(config.params);}
+if(config.proxy&&(url.indexOf("http")==0)){url=config.proxy+encodeURIComponent(url);}
+request.open(config.method,url,config.async,config.user,config.password);for(var header in config.headers){request.setRequestHeader(header,config.headers[header]);}
+var complete=(config.scope)?OpenLayers.Function.bind(config.callback,config.scope):config.callback;var success;if(config.success){success=(config.scope)?OpenLayers.Function.bind(config.success,config.scope):config.success;}
+var failure;if(config.failure){failure=(config.scope)?OpenLayers.Function.bind(config.failure,config.scope):config.failure;}
+request.onreadystatechange=function(){if(request.readyState==OpenLayers.Request.XMLHttpRequest.DONE){complete(request);if(success&&(!request.status||(request.status>=200&&request.status<300))){success(request);}
+if(failure&&(request.status&&(request.status<200||request.status>=300))){failure(request);}}}
+request.send(config.data);return request;},GET:function(config){config=OpenLayers.Util.extend(config,{method:"GET"});return OpenLayers.Request.issue(config);},POST:function(config){config=OpenLayers.Util.extend(config,{method:"POST"});config.headers=config.headers?config.headers:{};if(!("CONTENT-TYPE"in OpenLayers.Util.upperCaseObject(config.headers))){config.headers["Content-Type"]="application/xml";}
+return OpenLayers.Request.issue(config);},PUT:function(config){config=OpenLayers.Util.extend(config,{method:"PUT"});config.headers=config.headers?config.headers:{};if(!("CONTENT-TYPE"in OpenLayers.Util.upperCaseObject(config.headers))){config.headers["Content-Type"]="application/xml";}
+return OpenLayers.Request.issue(config);},DELETE:function(config){config=OpenLayers.Util.extend(config,{method:"DELETE"});return OpenLayers.Request.issue(config);},HEAD:function(config){config=OpenLayers.Util.extend(config,{method:"HEAD"});return OpenLayers.Request.issue(config);},OPTIONS:function(config){config=OpenLayers.Util.extend(config,{method:"OPTIONS"});return OpenLayers.Request.issue(config);}};OpenLayers.Tween=OpenLayers.Class({INTERVAL:10,easing:null,begin:null,finish:null,duration:null,callbacks:null,time:null,interval:null,playing:false,initialize:function(easing){this.easing=(easing)?easing:OpenLayers.Easing.Expo.easeOut;},start:function(begin,finish,duration,options){this.playing=true;this.begin=begin;this.finish=finish;this.duration=duration;this.callbacks=options.callbacks;this.time=0;if(this.interval){window.clearInterval(this.interval);this.interval=null;}
if(this.callbacks&&this.callbacks.start){this.callbacks.start.call(this,this.begin);}
this.interval=window.setInterval(OpenLayers.Function.bind(this.play,this),this.INTERVAL);},stop:function(){if(!this.playing){return;}
if(this.callbacks&&this.callbacks.done){this.callbacks.done.call(this,this.finish);}
@@ -229,31 +280,15 @@
var c=f-b;value[i]=this.easing.apply(this,[this.time,b,c,this.duration]);}
this.time++;if(this.callbacks&&this.callbacks.eachStep){this.callbacks.eachStep.call(this,value);}
if(this.time>this.duration){if(this.callbacks&&this.callbacks.done){this.callbacks.done.call(this,this.finish);this.playing=false;}
-window.clearInterval(this.interval);this.interval=null;}},CLASS_NAME:"OpenLayers.Tween"});OpenLayers.Easing={CLASS_NAME:"OpenLayers.Easing"};OpenLayers.Easing.Linear={easeIn:function(t,b,c,d){return c*t/d+b;},easeOut:function(t,b,c,d){return c*t/d+b;},easeInOut:function(t,b,c,d){return c*t/d+b;},CLASS_NAME:"OpenLayers.Easing.Linear"};OpenLayers.Easing.Expo={easeIn:function(t,b,c,d){return(t==0)?b:c*Math.pow(2,10*(t/d-1))+b;},easeOut:function(t,b,c,d){return(t==d)?b+c:c*(-Math.pow(2,-10*t/d)+1)+b;},easeInOut:function(t,b,c,d){if(t==0)return b;if(t==d)return b+c;if((t/=d/2)<1)return c/2*Math.pow(2,10*(t-1))+b;return c/2*(-Math.pow(2,-10*--t)+2)+b;},CLASS_NAME:"OpenLayers.Easing.Expo"};OpenLayers.Easing.Quad={easeIn:function(t,b,c,d){return c*(t/=d)*t+b;},easeOut:function(t,b,c,d){return-c*(t/=d)*(t-2)+b;},easeInOut:function(t,b,c,d){if((t/=d/2)<1)return c/2*t*t+b;return-c/2*((--t)*(t-2)-1)+b;},CLASS_NAME:"OpenLayers.Easing.Quad"};OpenLayers.Control.ArgParser=OpenLayers.Class(OpenLayers.Control,{center:null,zoom:null,layers:null,displayProjection:null,initialize:function(options){OpenLayers.Control.prototype.initialize.apply(this,arguments);},setMap:function(map){OpenLayers.Control.prototype.setMap.apply(this,arguments);for(var i=0;i<this.map.controls.length;i++){var control=this.map.controls[i];if((control!=this)&&(control.CLASS_NAME=="OpenLayers.Control.ArgParser")){if(control.displayProjection!=this.displayProjection){this.displayProjection=control.displayProjection;}
+window.clearInterval(this.interval);this.interval=null;}},CLASS_NAME:"OpenLayers.Tween"});OpenLayers.Easing={CLASS_NAME:"OpenLayers.Easing"};OpenLayers.Easing.Linear={easeIn:function(t,b,c,d){return c*t/d+b;},easeOut:function(t,b,c,d){return c*t/d+b;},easeInOut:function(t,b,c,d){return c*t/d+b;},CLASS_NAME:"OpenLayers.Easing.Linear"};OpenLayers.Easing.Expo={easeIn:function(t,b,c,d){return(t==0)?b:c*Math.pow(2,10*(t/d-1))+b;},easeOut:function(t,b,c,d){return(t==d)?b+c:c*(-Math.pow(2,-10*t/d)+1)+b;},easeInOut:function(t,b,c,d){if(t==0)return b;if(t==d)return b+c;if((t/=d/2)<1)return c/2*Math.pow(2,10*(t-1))+b;return c/2*(-Math.pow(2,-10*--t)+2)+b;},CLASS_NAME:"OpenLayers.Easing.Expo"};OpenLayers.Easing.Quad={easeIn:function(t,b,c,d){return c*(t/=d)*t+b;},easeOut:function(t,b,c,d){return-c*(t/=d)*(t-2)+b;},easeInOut:function(t,b,c,d){if((t/=d/2)<1)return c/2*t*t+b;return-c/2*((--t)*(t-2)-1)+b;},CLASS_NAME:"OpenLayers.Easing.Quad"};OpenLayers.Control.ArgParser=OpenLayers.Class(OpenLayers.Control,{center:null,zoom:null,layers:null,displayProjection:null,initialize:function(options){OpenLayers.Control.prototype.initialize.apply(this,arguments);},setMap:function(map){OpenLayers.Control.prototype.setMap.apply(this,arguments);for(var i=0,len=this.map.controls.length;i<len;i++){var control=this.map.controls[i];if((control!=this)&&(control.CLASS_NAME=="OpenLayers.Control.ArgParser")){if(control.displayProjection!=this.displayProjection){this.displayProjection=control.displayProjection;}
break;}}
if(i==this.map.controls.length){var args=OpenLayers.Util.getParameters();if(args.layers){this.layers=args.layers;this.map.events.register('addlayer',this,this.configureLayers);this.configureLayers();}
if(args.lat&&args.lon){this.center=new OpenLayers.LonLat(parseFloat(args.lon),parseFloat(args.lat));if(args.zoom){this.zoom=parseInt(args.zoom);}
this.map.events.register('changebaselayer',this,this.setCenter);this.setCenter();}}},setCenter:function(){if(this.map.baseLayer){this.map.events.unregister('changebaselayer',this,this.setCenter);if(this.displayProjection){this.center.transform(this.displayProjection,this.map.getProjectionObject());}
-this.map.setCenter(this.center,this.zoom);}},configureLayers:function(){if(this.layers.length==this.map.layers.length){this.map.events.unregister('addlayer',this,this.configureLayers);for(var i=0;i<this.layers.length;i++){var layer=this.map.layers[i];var c=this.layers.charAt(i);if(c=="B"){this.map.setBaseLayer(layer);}else if((c=="T")||(c=="F")){layer.setVisibility(c=="T");}}}},CLASS_NAME:"OpenLayers.Control.ArgParser"});OpenLayers.Control.MousePosition=OpenLayers.Class(OpenLayers.Control,{element:null,prefix:'',separator:', ',suffix:'',numdigits:5,granularity:10,lastXy:null,displayProjection:null,initialize:function(options){OpenLayers.Control.prototype.initialize.apply(this,arguments);},destroy:function(){if(this.map){this.map.events.unregister('mousemove',this,this.redraw);}
-OpenLayers.Control.prototype.destroy.apply(this,arguments);},draw:function(){OpenLayers.Control.prototype.draw.apply(this,arguments);if(!this.element){this.div.left="";this.div.top="";this.element=this.div;}
-this.redraw();return this.div;},redraw:function(evt){var lonLat;if(evt==null){lonLat=new OpenLayers.LonLat(0,0);}else{if(this.lastXy==null||Math.abs(evt.xy.x-this.lastXy.x)>this.granularity||Math.abs(evt.xy.y-this.lastXy.y)>this.granularity)
-{this.lastXy=evt.xy;return;}
-lonLat=this.map.getLonLatFromPixel(evt.xy);if(!lonLat){return;}
-if(this.displayProjection){lonLat.transform(this.map.getProjectionObject(),this.displayProjection);}
-this.lastXy=evt.xy;}
-var newHtml=this.formatOutput(lonLat);if(newHtml!=this.element.innerHTML){this.element.innerHTML=newHtml;}},formatOutput:function(lonLat){var digits=parseInt(this.numdigits);var newHtml=this.prefix+
-lonLat.lon.toFixed(digits)+
-this.separator+
-lonLat.lat.toFixed(digits)+
-this.suffix;return newHtml;},setMap:function(){OpenLayers.Control.prototype.setMap.apply(this,arguments);this.map.events.register('mousemove',this,this.redraw);},CLASS_NAME:"OpenLayers.Control.MousePosition"});OpenLayers.Control.PanZoom=OpenLayers.Class(OpenLayers.Control,{slideFactor:50,buttons:null,position:null,initialize:function(options){this.position=new OpenLayers.Pixel(OpenLayers.Control.PanZoom.X,OpenLayers.Control.PanZoom.Y);OpenLayers.Control.prototype.initialize.apply(this,arguments);},destroy:function(){OpenLayers.Control.prototype.destroy.apply(this,arguments);while(this.buttons.length){var btn=this.buttons.shift();btn.map=null;OpenLayers.Event.stopObservingElement(btn);}
-this.buttons=null;this.position=null;},draw:function(px){OpenLayers.Control.prototype.draw.apply(this,arguments);px=this.position;this.buttons=[];var sz=new OpenLayers.Size(18,18);var centered=new OpenLayers.Pixel(px.x+sz.w/2,px.y);this._addButton("panup","north-mini.png",centered,sz);px.y=centered.y+sz.h;this._addButton("panleft","west-mini.png",px,sz);this._addButton("panright","east-mini.png",px.add(sz.w,0),sz);this._addButton("pandown","south-mini.png",centered.add(0,sz.h*2),sz);this._addButton("zoomin","zoom-plus-mini.png",centered.add(0,sz.h*3+5),sz);this._addButton("zoomworld","zoom-world-mini.png",centered.add(0,sz.h*4+5),sz);this._addButton("zoomout","zoom-minus-mini.png",centered.add(0,sz.h*5+5),sz);return this.div;},_addButton:function(id,img,xy,sz){var imgLocation=OpenLayers.Util.getImagesLocation()+img;var btn=OpenLayers.Util.createAlphaImageDiv("OpenLayers_Control_PanZoom_"+id,xy,sz,imgLocation,"absolute");this.div.appendChild(btn);OpenLayers.Event.observe(btn,"mousedown",OpenLayers.Function.bindAsEventListener(this.buttonDown,btn));OpenLayers.Event.observe(btn,"dblclick",OpenLayers.Function.bindAsEventListener(this.doubleClick,btn));OpenLayers.Event.observe(btn,"click",OpenLayers.Function.bindAsEventListener(this.doubleClick,btn));btn.action=id;btn.map=this.map;btn.slideFactor=this.slideFactor;this.buttons.push(btn);return btn;},doubleClick:function(evt){OpenLayers.Event.stop(evt);return false;},buttonDown:function(evt){if(!OpenLayers.Event.isLeftClick(evt)){return;}
+this.map.setCenter(this.center,this.zoom);}},configureLayers:function(){if(this.layers.length==this.map.layers.length){this.map.events.unregister('addlayer',this,this.configureLayers);for(var i=0,len=this.layers.length;i<len;i++){var layer=this.map.layers[i];var c=this.layers.charAt(i);if(c=="B"){this.map.setBaseLayer(layer);}else if((c=="T")||(c=="F")){layer.setVisibility(c=="T");}}}},CLASS_NAME:"OpenLayers.Control.ArgParser"});OpenLayers.Control.PanZoom=OpenLayers.Class(OpenLayers.Control,{slideFactor:50,buttons:null,position:null,initialize:function(options){this.position=new OpenLayers.Pixel(OpenLayers.Control.PanZoom.X,OpenLayers.Control.PanZoom.Y);OpenLayers.Control.prototype.initialize.apply(this,arguments);},destroy:function(){OpenLayers.Control.prototype.destroy.apply(this,arguments);while(this.buttons.length){var btn=this.buttons.shift();btn.map=null;OpenLayers.Event.stopObservingElement(btn);}
+this.buttons=null;this.position=null;},draw:function(px){OpenLayers.Control.prototype.draw.apply(this,arguments);px=this.position;this.buttons=[];var sz=new OpenLayers.Size(18,18);var centered=new OpenLayers.Pixel(px.x+sz.w/2,px.y);this._addButton("panup","north-mini.png",centered,sz);px.y=centered.y+sz.h;this._addButton("panleft","west-mini.png",px,sz);this._addButton("panright","east-mini.png",px.add(sz.w,0),sz);this._addButton("pandown","south-mini.png",centered.add(0,sz.h*2),sz);this._addButton("zoomin","zoom-plus-mini.png",centered.add(0,sz.h*3+5),sz);this._addButton("zoomworld","zoom-world-mini.png",centered.add(0,sz.h*4+5),sz);this._addButton("zoomout","zoom-minus-mini.png",centered.add(0,sz.h*5+5),sz);return this.div;},_addButton:function(id,img,xy,sz){var imgLocation=OpenLayers.Util.getImagesLocation()+img;var btn=OpenLayers.Util.createAlphaImageDiv(this.id+"_"+id,xy,sz,imgLocation,"absolute");this.div.appendChild(btn);OpenLayers.Event.observe(btn,"mousedown",OpenLayers.Function.bindAsEventListener(this.buttonDown,btn));OpenLayers.Event.observe(btn,"dblclick",OpenLayers.Function.bindAsEventListener(this.doubleClick,btn));OpenLayers.Event.observe(btn,"click",OpenLayers.Function.bindAsEventListener(this.doubleClick,btn));btn.action=id;btn.map=this.map;btn.slideFactor=this.slideFactor;this.buttons.push(btn);return btn;},doubleClick:function(evt){OpenLayers.Event.stop(evt);return false;},buttonDown:function(evt){if(!OpenLayers.Event.isLeftClick(evt)){return;}
switch(this.action){case"panup":this.map.pan(0,-this.slideFactor);break;case"pandown":this.map.pan(0,this.slideFactor);break;case"panleft":this.map.pan(-this.slideFactor,0);break;case"panright":this.map.pan(this.slideFactor,0);break;case"zoomin":this.map.zoomIn();break;case"zoomout":this.map.zoomOut();break;case"zoomworld":this.map.zoomToMaxExtent();break;}
-OpenLayers.Event.stop(evt);},CLASS_NAME:"OpenLayers.Control.PanZoom"});OpenLayers.Control.PanZoom.X=4;OpenLayers.Control.PanZoom.Y=4;OpenLayers.Control.ScaleLine=OpenLayers.Class(OpenLayers.Control,{maxWidth:100,topOutUnits:"km",topInUnits:"m",bottomOutUnits:"mi",bottomInUnits:"ft",eTop:null,eBottom:null,initialize:function(options){OpenLayers.Control.prototype.initialize.apply(this,[options]);},draw:function(){OpenLayers.Control.prototype.draw.apply(this,arguments);if(!this.eTop){this.div.style.display="block";this.div.style.position="absolute";this.eTop=document.createElement("div");this.eTop.className=this.displayClass+"Top";var theLen=this.topInUnits.length;this.div.appendChild(this.eTop);if((this.topOutUnits=="")||(this.topInUnits=="")){this.eTop.style.visibility="hidden";}else{this.eTop.style.visibility="visible";}
-this.eBottom=document.createElement("div");this.eBottom.className=this.displayClass+"Bottom";this.div.appendChild(this.eBottom);if((this.bottomOutUnits=="")||(this.bottomInUnits=="")){this.eBottom.style.visibility="hidden";}else{this.eBottom.style.visibility="visible";}}
-this.map.events.register('moveend',this,this.update);this.update();return this.div;},getBarLen:function(maxLen){var digits=parseInt(Math.log(maxLen)/Math.log(10));var pow10=Math.pow(10,digits);var firstChar=parseInt(maxLen/pow10);var barLen;if(firstChar>5){barLen=5;}else if(firstChar>2){barLen=2;}else{barLen=1;}
-return barLen*pow10;},update:function(){var res=this.map.getResolution();if(!res){return;}
-var curMapUnits=this.map.units;var inches=OpenLayers.INCHES_PER_UNIT;var maxSizeData=this.maxWidth*res*inches[curMapUnits];var topUnits;var bottomUnits;if(maxSizeData>100000){topUnits=this.topOutUnits;bottomUnits=this.bottomOutUnits;}else{topUnits=this.topInUnits;bottomUnits=this.bottomInUnits;}
-var topMax=maxSizeData/inches[topUnits];var bottomMax=maxSizeData/inches[bottomUnits];var topRounded=this.getBarLen(topMax);var bottomRounded=this.getBarLen(bottomMax);topMax=topRounded/inches[curMapUnits]*inches[topUnits];bottomMax=bottomRounded/inches[curMapUnits]*inches[bottomUnits];var topPx=topMax/res;var bottomPx=bottomMax/res;this.eTop.style.width=Math.round(topPx)+"px";this.eBottom.style.width=Math.round(bottomPx)+"px";this.eTop.innerHTML=topRounded+" "+topUnits;this.eBottom.innerHTML=bottomRounded+" "+bottomUnits;},CLASS_NAME:"OpenLayers.Control.ScaleLine"});OpenLayers.Event={observers:false,KEY_BACKSPACE:8,KEY_TAB:9,KEY_RETURN:13,KEY_ESC:27,KEY_LEFT:37,KEY_UP:38,KEY_RIGHT:39,KEY_DOWN:40,KEY_DELETE:46,element:function(event){return event.target||event.srcElement;},isLeftClick:function(event){return(((event.which)&&(event.which==1))||((event.button)&&(event.button==1)));},stop:function(event,allowDefault){if(!allowDefault){if(event.preventDefault){event.preventDefault();}else{event.returnValue=false;}}
+OpenLayers.Event.stop(evt);},CLASS_NAME:"OpenLayers.Control.PanZoom"});OpenLayers.Control.PanZoom.X=4;OpenLayers.Control.PanZoom.Y=4;OpenLayers.Event={observers:false,KEY_BACKSPACE:8,KEY_TAB:9,KEY_RETURN:13,KEY_ESC:27,KEY_LEFT:37,KEY_UP:38,KEY_RIGHT:39,KEY_DOWN:40,KEY_DELETE:46,element:function(event){return event.target||event.srcElement;},isLeftClick:function(event){return(((event.which)&&(event.which==1))||((event.button)&&(event.button==1)));},isRightClick:function(event){return(((event.which)&&(event.which==3))||((event.button)&&(event.button==2)));},stop:function(event,allowDefault){if(!allowDefault){if(event.preventDefault){event.preventDefault();}else{event.returnValue=false;}}
if(event.stopPropagation){event.stopPropagation();}else{event.cancelBubble=true;}},findElement:function(event,tagName){var element=OpenLayers.Event.element(event);while(element.parentNode&&(!element.tagName||(element.tagName.toUpperCase()!=tagName.toUpperCase()))){element=element.parentNode;}
return element;},observe:function(elementParam,name,observer,useCapture){var element=OpenLayers.Util.getElement(elementParam);useCapture=useCapture||false;if(name=='keypress'&&(navigator.appVersion.match(/Konqueror|Safari|KHTML/)||element.attachEvent)){name='keydown';}
if(!this.observers){this.observers={};}
@@ -267,39 +302,84 @@
if(foundEntry){if(element.removeEventListener){element.removeEventListener(name,observer,useCapture);}else if(element&&element.detachEvent){element.detachEvent('on'+name,observer);}}
return foundEntry;},unloadCache:function(){if(OpenLayers.Event&&OpenLayers.Event.observers){for(var cacheID in OpenLayers.Event.observers){var elementObservers=OpenLayers.Event.observers[cacheID];OpenLayers.Event._removeElementObservers.apply(this,[elementObservers]);}
OpenLayers.Event.observers=false;}},CLASS_NAME:"OpenLayers.Event"};OpenLayers.Event.observe(window,'unload',OpenLayers.Event.unloadCache,false);if(window.Event){OpenLayers.Util.applyDefaults(window.Event,OpenLayers.Event);}else{var Event=OpenLayers.Event;}
-OpenLayers.Events=OpenLayers.Class({BROWSER_EVENTS:["mouseover","mouseout","mousedown","mouseup","mousemove","click","dblclick","resize","focus","blur"],listeners:null,object:null,element:null,eventTypes:null,eventHandler:null,fallThrough:null,initialize:function(object,element,eventTypes,fallThrough){this.object=object;this.element=element;this.eventTypes=eventTypes;this.fallThrough=fallThrough;this.listeners={};this.eventHandler=OpenLayers.Function.bindAsEventListener(this.handleBrowserEvent,this);if(this.eventTypes!=null){for(var i=0;i<this.eventTypes.length;i++){this.addEventType(this.eventTypes[i]);}}
+OpenLayers.Events=OpenLayers.Class({BROWSER_EVENTS:["mouseover","mouseout","mousedown","mouseup","mousemove","click","dblclick","rightclick","dblrightclick","resize","focus","blur"],listeners:null,object:null,element:null,eventTypes:null,eventHandler:null,fallThrough:null,includeXY:false,initialize:function(object,element,eventTypes,fallThrough,options){OpenLayers.Util.extend(this,options);this.object=object;this.element=element;this.fallThrough=fallThrough;this.listeners={};this.eventHandler=OpenLayers.Function.bindAsEventListener(this.handleBrowserEvent,this);this.eventTypes=[];if(eventTypes!=null){for(var i=0,len=eventTypes.length;i<len;i++){this.addEventType(eventTypes[i]);}}
if(this.element!=null){this.attachToElement(element);}},destroy:function(){if(this.element){OpenLayers.Event.stopObservingElement(this.element);}
-this.element=null;this.listeners=null;this.object=null;this.eventTypes=null;this.fallThrough=null;this.eventHandler=null;},addEventType:function(eventName){if(!this.listeners[eventName]){this.listeners[eventName]=[];}},attachToElement:function(element){for(var i=0;i<this.BROWSER_EVENTS.length;i++){var eventType=this.BROWSER_EVENTS[i];this.addEventType(eventType);OpenLayers.Event.observe(element,eventType,this.eventHandler);}
-OpenLayers.Event.observe(element,"dragstart",OpenLayers.Event.stop);},on:function(object){for(var type in object){if(type!="scope"){this.register(type,object.scope,object[type]);}}},register:function(type,obj,func){if(func!=null&&((this.eventTypes&&OpenLayers.Util.indexOf(this.eventTypes,type)!=-1)||OpenLayers.Util.indexOf(this.BROWSER_EVENTS,type)!=-1)){if(obj==null){obj=this.object;}
-var listeners=this.listeners[type];if(listeners!=null){listeners.push({obj:obj,func:func});}}},registerPriority:function(type,obj,func){if(func!=null){if(obj==null){obj=this.object;}
+this.element=null;this.listeners=null;this.object=null;this.eventTypes=null;this.fallThrough=null;this.eventHandler=null;},addEventType:function(eventName){if(!this.listeners[eventName]){this.eventTypes.push(eventName);this.listeners[eventName]=[];}},attachToElement:function(element){for(var i=0,len=this.BROWSER_EVENTS.length;i<len;i++){var eventType=this.BROWSER_EVENTS[i];this.addEventType(eventType);OpenLayers.Event.observe(element,eventType,this.eventHandler);}
+OpenLayers.Event.observe(element,"dragstart",OpenLayers.Event.stop);},on:function(object){for(var type in object){if(type!="scope"){this.register(type,object.scope,object[type]);}}},register:function(type,obj,func){if((func!=null)&&(OpenLayers.Util.indexOf(this.eventTypes,type)!=-1)){if(obj==null){obj=this.object;}
+var listeners=this.listeners[type];listeners.push({obj:obj,func:func});}},registerPriority:function(type,obj,func){if(func!=null){if(obj==null){obj=this.object;}
var listeners=this.listeners[type];if(listeners!=null){listeners.unshift({obj:obj,func:func});}}},un:function(object){for(var type in object){if(type!="scope"){this.unregister(type,object.scope,object[type]);}}},unregister:function(type,obj,func){if(obj==null){obj=this.object;}
-var listeners=this.listeners[type];if(listeners!=null){for(var i=0;i<listeners.length;i++){if(listeners[i].obj==obj&&listeners[i].func==func){listeners.splice(i,1);break;}}}},remove:function(type){if(this.listeners[type]!=null){this.listeners[type]=[];}},triggerEvent:function(type,evt){if(evt==null){evt={};}
+var listeners=this.listeners[type];if(listeners!=null){for(var i=0,len=listeners.length;i<len;i++){if(listeners[i].obj==obj&&listeners[i].func==func){listeners.splice(i,1);break;}}}},remove:function(type){if(this.listeners[type]!=null){this.listeners[type]=[];}},triggerEvent:function(type,evt){if(evt==null){evt={};}
evt.object=this.object;evt.element=this.element;if(!evt.type){evt.type=type;}
-var listeners=(this.listeners[type])?this.listeners[type].slice():null;if((listeners!=null)&&(listeners.length>0)){var continueChain;for(var i=0;i<listeners.length;i++){var callback=listeners[i];continueChain=callback.func.apply(callback.obj,[evt]);if((continueChain!=undefined)&&(continueChain==false)){break;}}
+var listeners=(this.listeners[type])?this.listeners[type].slice():null;if((listeners!=null)&&(listeners.length>0)){var continueChain;for(var i=0,len=listeners.length;i<len;i++){var callback=listeners[i];continueChain=callback.func.apply(callback.obj,[evt]);if((continueChain!=undefined)&&(continueChain==false)){break;}}
if(!this.fallThrough){OpenLayers.Event.stop(evt,true);}}
-return continueChain;},handleBrowserEvent:function(evt){evt.xy=this.getMousePosition(evt);this.triggerEvent(evt.type,evt);},getMousePosition:function(evt){if(!this.element.offsets){this.element.offsets=OpenLayers.Util.pagePosition(this.element);this.element.offsets[0]+=(document.documentElement.scrollLeft||document.body.scrollLeft);this.element.offsets[1]+=(document.documentElement.scrollTop||document.body.scrollTop);}
-return new OpenLayers.Pixel((evt.clientX+(document.documentElement.scrollLeft||document.body.scrollLeft))-this.element.offsets[0]
--(document.documentElement.clientLeft||0),(evt.clientY+(document.documentElement.scrollTop||document.body.scrollTop))-this.element.offsets[1]
--(document.documentElement.clientTop||0));},CLASS_NAME:"OpenLayers.Events"});OpenLayers.Lang.en={'unhandledRequest':"Unhandled request return ${statusText}",'permalink':"Permalink",'overlays':"Overlays",'baseLayer':"Base Layer",'sameProjection':"The overview map only works when it is in the same projection as the main map",'readNotImplemented':"Read not implemented.",'writeNotImplemented':"Write not implemented.",'noFID':"Can't update a feature for which there is no FID.",'errorLoadingGML':"Error in loading GML file ${url}",'browserNotSupported':"Your browser does not support vector rendering. Currently supported renderers are:\n${renderers}",'componentShouldBe':"addFeatures : component should be an ${geomType}",'getFeatureError':"getFeatureFromEvent called on layer with no renderer. This usually means you "+"destroyed a layer, but not some handler which is associated with it.",'minZoomLevelError':"The minZoomLevel property is only intended for use "+"with the FixedZoomLevels-descendent layers. That this "+"wfs layer checks for minZoomLevel is a relic of the"+"past. We cannot, however, remove it without possibly "+"breaking OL based applications that may depend on it."+" Therefore we are deprecating it -- the minZoomLevel "+"check below will be removed at 3.0. Please instead "+"use min/max resolution setting as described here: "+"http://trac.openlayers.org/wiki/SettingZoomLevels",'commitSuccess':"WFS Transaction: SUCCESS ${response}",'commitFailed':"WFS Transaction: FAILED ${response}",'googleWarning':"The Google Layer was unable to load correctly.<br><br>"+"To get rid of this message, select a new BaseLayer "+"in the layer switcher in the upper-right corner.<br><br>"+"Most likely, this is because the Google Maps library "+"script was either not included, or does not contain the "+"correct API key for your site.<br><br>"+"Developers: For help getting this working correctly, "+"<a href='http://trac.openlayers.org/wiki/Google' "+"target='_blank'>click here</a>",'getLayerWarning':"The ${layerType} Layer was unable to load correctly.<br><br>"+"To get rid of this message, select a new BaseLayer "+"in the layer switcher in the upper-right corner.<br><br>"+"Most likely, this is because the ${layerLib} library "+"script was either not correctly included.<br><br>"+"Developers: For help getting this working correctly, "+"<a href='http://trac.openlayers.org/wiki/${layerLib}' "+"target='_blank'>click here</a>",'scale':"Scale = 1 : ${scaleDenom}",'layerAlreadyAdded':"You tried to add the layer: ${layerName} to the map, but it has already been added",'reprojectDeprecated':"You are using the 'reproject' option "+"on the ${layerName} layer. This option is deprecated: "+"its use was designed to support displaying data over commercial "+"basemaps, but that functionality should now be achieved by using "+"Spherical Mercator support. More information is available from "+"http://trac.openlayers.org/wiki/SphericalMercator.",'methodDeprecated':"This method has been deprecated and will be removed in 3.0. "+"Please use ${newMethod} instead.",'boundsAddError':"You must pass both x and y values to the add function.",'lonlatAddError':"You must pass both lon and lat values to the add function.",'pixelAddError':"You must pass both x and y values to the add function.",'unsupportedGeometryType':"Unsupported geometry type: ${geomType}",'pagePositionFailed':"OpenLayers.Util.pagePosition failed: element with id ${elemId} may be misplaced.",'end':''};OpenLayers.Projection=OpenLayers.Class({proj:null,projCode:null,initialize:function(projCode,options){OpenLayers.Util.extend(this,options);this.projCode=projCode;if(window.Proj4js){this.proj=new Proj4js.Proj(projCode);}},getCode:function(){return this.proj?this.proj.srsCode:this.projCode;},getUnits:function(){return this.proj?this.proj.units:null;},toString:function(){return this.getCode();},equals:function(projection){if(projection&&projection.getCode){return this.getCode()==projection.getCode();}else{return false;}},destroy:function(){delete this.proj;delete this.projCode;},CLASS_NAME:"OpenLayers.Projection"});OpenLayers.Projection.transforms={};OpenLayers.Projection.addTransform=function(from,to,method){if(!OpenLayers.Projection.transforms[from]){OpenLayers.Projection.transforms[from]={};}
+return continueChain;},handleBrowserEvent:function(evt){if(this.includeXY){evt.xy=this.getMousePosition(evt);}
+this.triggerEvent(evt.type,evt);},clearMouseCache:function(){this.element.scrolls=null;this.element.lefttop=null;this.element.offsets=null;},getMousePosition:function(evt){if(!this.includeXY){this.clearMouseCache();}else if(!this.element.hasScrollEvent){OpenLayers.Event.observe(window,'scroll',OpenLayers.Function.bind(this.clearMouseCache,this));this.element.hasScrollEvent=true;}
+if(!this.element.scrolls){this.element.scrolls=[];this.element.scrolls[0]=(document.documentElement.scrollLeft||document.body.scrollLeft);this.element.scrolls[1]=(document.documentElement.scrollTop||document.body.scrollTop);}
+if(!this.element.lefttop){this.element.lefttop=[];this.element.lefttop[0]=(document.documentElement.clientLeft||0);this.element.lefttop[1]=(document.documentElement.clientTop||0);}
+if(!this.element.offsets){this.element.offsets=OpenLayers.Util.pagePosition(this.element);this.element.offsets[0]+=this.element.scrolls[0];this.element.offsets[1]+=this.element.scrolls[1];}
+return new OpenLayers.Pixel((evt.clientX+this.element.scrolls[0])-this.element.offsets[0]
+-this.element.lefttop[0],(evt.clientY+this.element.scrolls[1])-this.element.offsets[1]
+-this.element.lefttop[1]);},CLASS_NAME:"OpenLayers.Events"});OpenLayers.Format=OpenLayers.Class({externalProjection:null,internalProjection:null,initialize:function(options){OpenLayers.Util.extend(this,options);},read:function(data){OpenLayers.Console.userError(OpenLayers.i18n("readNotImplemented"));},write:function(object){OpenLayers.Console.userError(OpenLayers.i18n("writeNotImplemented"));},CLASS_NAME:"OpenLayers.Format"});OpenLayers.Lang.en={'unhandledRequest':"Unhandled request return ${statusText}",'permalink':"Permalink",'overlays':"Overlays",'baseLayer':"Base Layer",'sameProjection':"The overview map only works when it is in the same projection as the main map",'readNotImplemented':"Read not implemented.",'writeNotImplemented':"Write not implemented.",'noFID':"Can't update a feature for which there is no FID.",'errorLoadingGML':"Error in loading GML file ${url}",'browserNotSupported':"Your browser does not support vector rendering. Currently supported renderers are:\n${renderers}",'componentShouldBe':"addFeatures : component should be an ${geomType}",'getFeatureError':"getFeatureFromEvent called on layer with no renderer. This usually means you "+"destroyed a layer, but not some handler which is associated with it.",'minZoomLevelError':"The minZoomLevel property is only intended for use "+"with the FixedZoomLevels-descendent layers. That this "+"wfs layer checks for minZoomLevel is a relic of the"+"past. We cannot, however, remove it without possibly "+"breaking OL based applications that may depend on it."+" Therefore we are deprecating it -- the minZoomLevel "+"check below will be removed at 3.0. Please instead "+"use min/max resolution setting as described here: "+"http://trac.openlayers.org/wiki/SettingZoomLevels",'commitSuccess':"WFS Transaction: SUCCESS ${response}",'commitFailed':"WFS Transaction: FAILED ${response}",'googleWarning':"The Google Layer was unable to load correctly.<br><br>"+"To get rid of this message, select a new BaseLayer "+"in the layer switcher in the upper-right corner.<br><br>"+"Most likely, this is because the Google Maps library "+"script was either not included, or does not contain the "+"correct API key for your site.<br><br>"+"Developers: For help getting this working correctly, "+"<a href='http://trac.openlayers.org/wiki/Google' "+"target='_blank'>click here</a>",'getLayerWarning':"The ${layerType} Layer was unable to load correctly.<br><br>"+"To get rid of this message, select a new BaseLayer "+"in the layer switcher in the upper-right corner.<br><br>"+"Most likely, this is because the ${layerLib} library "+"script was not correctly included.<br><br>"+"Developers: For help getting this working correctly, "+"<a href='http://trac.openlayers.org/wiki/${layerLib}' "+"target='_blank'>click here</a>",'scale':"Scale = 1 : ${scaleDenom}",'layerAlreadyAdded':"You tried to add the layer: ${layerName} to the map, but it has already been added",'reprojectDeprecated':"You are using the 'reproject' option "+"on the ${layerName} layer. This option is deprecated: "+"its use was designed to support displaying data over commercial "+"basemaps, but that functionality should now be achieved by using "+"Spherical Mercator support. More information is available from "+"http://trac.openlayers.org/wiki/SphericalMercator.",'methodDeprecated':"This method has been deprecated and will be removed in 3.0. "+"Please use ${newMethod} instead.",'boundsAddError':"You must pass both x and y values to the add function.",'lonlatAddError':"You must pass both lon and lat values to the add function.",'pixelAddError':"You must pass both x and y values to the add function.",'unsupportedGeometryType':"Unsupported geometry type: ${geomType}",'pagePositionFailed':"OpenLayers.Util.pagePosition failed: element with id ${elemId} may be misplaced.",'end':''};OpenLayers.Popup.Anchored=OpenLayers.Class(OpenLayers.Popup,{relativePosition:null,anchor:null,initialize:function(id,lonlat,contentSize,contentHTML,anchor,closeBox,closeBoxCallback){var newArguments=[id,lonlat,contentSize,contentHTML,closeBox,closeBoxCallback];OpenLayers.Popup.prototype.initialize.apply(this,newArguments);this.anchor=(anchor!=null)?anchor:{size:new OpenLayers.Size(0,0),offset:new OpenLayers.Pixel(0,0)};},destroy:function(){this.anchor=null;this.relativePosition=null;OpenLayers.Popup.prototype.destroy.apply(this,arguments);},show:function(){this.updatePosition();OpenLayers.Popup.prototype.show.apply(this,arguments);},moveTo:function(px){var oldRelativePosition=this.relativePosition;this.relativePosition=this.calculateRelativePosition(px);var newPx=this.calculateNewPx(px);var newArguments=new Array(newPx);OpenLayers.Popup.prototype.moveTo.apply(this,newArguments);if(this.relativePosition!=oldRelativePosition){this.updateRelativePosition();}},setSize:function(contentSize){OpenLayers.Popup.prototype.setSize.apply(this,arguments);if((this.lonlat)&&(this.map)){var px=this.map.getLayerPxFromLonLat(this.lonlat);this.moveTo(px);}},calculateRelativePosition:function(px){var lonlat=this.map.getLonLatFromLayerPx(px);var extent=this.map.getExtent();var quadrant=extent.determineQuadrant(lonlat);return OpenLayers.Bounds.oppositeQuadrant(quadrant);},updateRelativePosition:function(){},calculateNewPx:function(px){var newPx=px.offset(this.anchor.offset);var size=this.size||this.contentSize;var top=(this.relativePosition.charAt(0)=='t');newPx.y+=(top)?-size.h:this.anchor.size.h;var left=(this.relativePosition.charAt(1)=='l');newPx.x+=(left)?-size.w:this.anchor.size.w;return newPx;},CLASS_NAME:"OpenLayers.Popup.Anchored"});OpenLayers.Projection=OpenLayers.Class({proj:null,projCode:null,initialize:function(projCode,options){OpenLayers.Util.extend(this,options);this.projCode=projCode;if(window.Proj4js){this.proj=new Proj4js.Proj(projCode);}},getCode:function(){return this.proj?this.proj.srsCode:this.projCode;},getUnits:function(){return this.proj?this.proj.units:null;},toString:function(){return this.getCode();},equals:function(projection){if(projection&&projection.getCode){return this.getCode()==projection.getCode();}else{return false;}},destroy:function(){delete this.proj;delete this.projCode;},CLASS_NAME:"OpenLayers.Projection"});OpenLayers.Projection.transforms={};OpenLayers.Projection.addTransform=function(from,to,method){if(!OpenLayers.Projection.transforms[from]){OpenLayers.Projection.transforms[from]={};}
OpenLayers.Projection.transforms[from][to]=method;};OpenLayers.Projection.transform=function(point,source,dest){if(source.proj&&dest.proj){point=Proj4js.transform(source.proj,dest.proj,point);}else if(source&&dest&&OpenLayers.Projection.transforms[source.getCode()]&&OpenLayers.Projection.transforms[source.getCode()][dest.getCode()]){OpenLayers.Projection.transforms[source.getCode()][dest.getCode()](point);}
-return point;};OpenLayers.Tile=OpenLayers.Class({EVENT_TYPES:["loadstart","loadend","reload","unload"],events:null,id:null,layer:null,url:null,bounds:null,size:null,position:null,isLoading:false,isBackBuffer:false,lastRatio:1,isFirstDraw:true,backBufferTile:null,initialize:function(layer,position,bounds,url,size){this.layer=layer;this.position=position.clone();this.bounds=bounds.clone();this.url=url;this.size=size.clone();this.id=OpenLayers.Util.createUniqueID("Tile_");this.events=new OpenLayers.Events(this,null,this.EVENT_TYPES);},unload:function(){if(this.isLoading){this.isLoading=false;this.events.triggerEvent("unload");}},destroy:function(){if(OpenLayers.Util.indexOf(this.layer.SUPPORTED_TRANSITIONS,this.layer.transitionEffect)!=-1){this.layer.events.unregister("loadend",this,this.resetBackBuffer);this.events.unregister('loadend',this,this.resetBackBuffer);}else{this.events.unregister('loadend',this,this.showTile);}
-this.layer=null;this.bounds=null;this.size=null;this.position=null;this.events.destroy();this.events=null;if(this.backBufferTile){this.backBufferTile.destroy();this.backBufferTile=null;}},clone:function(obj){if(obj==null){obj=new OpenLayers.Tile(this.layer,this.position,this.bounds,this.url,this.size);}
-OpenLayers.Util.applyDefaults(obj,this);return obj;},draw:function(){var maxExtent=this.layer.maxExtent;var withinMaxExtent=(maxExtent&&this.bounds.intersectsBounds(maxExtent,false));var drawTile=(withinMaxExtent||this.layer.displayOutsideMaxExtent);if(OpenLayers.Util.indexOf(this.layer.SUPPORTED_TRANSITIONS,this.layer.transitionEffect)!=-1){if(drawTile){if(!this.backBufferTile){this.backBufferTile=this.clone();this.backBufferTile.hide();this.backBufferTile.isBackBuffer=true;this.events.register('loadend',this,this.resetBackBuffer);this.layer.events.register("loadend",this,this.resetBackBuffer);}
-this.startTransition();}else{if(this.backBufferTile){this.backBufferTile.clear();}}}else{if(drawTile&&this.isFirstDraw){this.events.register('loadend',this,this.showTile);this.isFirstDraw=false;}}
-this.shouldDraw=drawTile;this.clear();return drawTile;},moveTo:function(bounds,position,redraw){if(redraw==null){redraw=true;}
+return point;};(function(){var oXMLHttpRequest=window.XMLHttpRequest;var bGecko=!!window.controllers,bIE=window.document.all&&!window.opera;function cXMLHttpRequest(){this._object=oXMLHttpRequest?new oXMLHttpRequest:new window.ActiveXObject('Microsoft.XMLHTTP');};if(bGecko&&oXMLHttpRequest.wrapped)
+cXMLHttpRequest.wrapped=oXMLHttpRequest.wrapped;cXMLHttpRequest.UNSENT=0;cXMLHttpRequest.OPENED=1;cXMLHttpRequest.HEADERS_RECEIVED=2;cXMLHttpRequest.LOADING=3;cXMLHttpRequest.DONE=4;cXMLHttpRequest.prototype.readyState=cXMLHttpRequest.UNSENT;cXMLHttpRequest.prototype.responseText="";cXMLHttpRequest.prototype.responseXML=null;cXMLHttpRequest.prototype.status=0;cXMLHttpRequest.prototype.statusText="";cXMLHttpRequest.prototype.onreadystatechange=null;cXMLHttpRequest.onreadystatechange=null;cXMLHttpRequest.onopen=null;cXMLHttpRequest.onsend=null;cXMLHttpRequest.onabort=null;cXMLHttpRequest.prototype.open=function(sMethod,sUrl,bAsync,sUser,sPassword){this._async=bAsync;var oRequest=this,nState=this.readyState;if(bIE){var fOnUnload=function(){if(oRequest._object.readyState!=cXMLHttpRequest.DONE)
+fCleanTransport(oRequest);};if(bAsync)
+window.attachEvent("onunload",fOnUnload);}
+this._object.onreadystatechange=function(){if(bGecko&&!bAsync)
+return;oRequest.readyState=oRequest._object.readyState;fSynchronizeValues(oRequest);if(oRequest._aborted){oRequest.readyState=cXMLHttpRequest.UNSENT;return;}
+if(oRequest.readyState==cXMLHttpRequest.DONE){fCleanTransport(oRequest);if(bIE&&bAsync)
+window.detachEvent("onunload",fOnUnload);}
+if(nState!=oRequest.readyState)
+fReadyStateChange(oRequest);nState=oRequest.readyState;};if(cXMLHttpRequest.onopen)
+cXMLHttpRequest.onopen.apply(this,arguments);this._object.open(sMethod,sUrl,bAsync,sUser,sPassword);if(!bAsync&&bGecko){this.readyState=cXMLHttpRequest.OPENED;fReadyStateChange(this);}};cXMLHttpRequest.prototype.send=function(vData){if(cXMLHttpRequest.onsend)
+cXMLHttpRequest.onsend.apply(this,arguments);if(vData&&vData.nodeType){vData=window.XMLSerializer?new window.XMLSerializer().serializeToString(vData):vData.xml;if(!this._headers["Content-Type"])
+this._object.setRequestHeader("Content-Type","application/xml");}
+this._object.send(vData);if(bGecko&&!this._async){this.readyState=cXMLHttpRequest.OPENED;fSynchronizeValues(this);while(this.readyState<cXMLHttpRequest.DONE){this.readyState++;fReadyStateChange(this);if(this._aborted)
+return;}}};cXMLHttpRequest.prototype.abort=function(){if(cXMLHttpRequest.onabort)
+cXMLHttpRequest.onabort.apply(this,arguments);if(this.readyState>cXMLHttpRequest.UNSENT)
+this._aborted=true;this._object.abort();fCleanTransport(this);};cXMLHttpRequest.prototype.getAllResponseHeaders=function(){return this._object.getAllResponseHeaders();};cXMLHttpRequest.prototype.getResponseHeader=function(sName){return this._object.getResponseHeader(sName);};cXMLHttpRequest.prototype.setRequestHeader=function(sName,sValue){if(!this._headers)
+this._headers={};this._headers[sName]=sValue;return this._object.setRequestHeader(sName,sValue);};cXMLHttpRequest.prototype.toString=function(){return'['+"object"+' '+"XMLHttpRequest"+']';};cXMLHttpRequest.toString=function(){return'['+"XMLHttpRequest"+']';};function fReadyStateChange(oRequest){if(oRequest.onreadystatechange)
+oRequest.onreadystatechange.apply(oRequest);if(cXMLHttpRequest.onreadystatechange)
+cXMLHttpRequest.onreadystatechange.apply(oRequest);};function fGetDocument(oRequest){var oDocument=oRequest.responseXML;if(bIE&&oDocument&&!oDocument.documentElement&&oRequest.getResponseHeader("Content-Type").match(/[^\/]+\/[^\+]+\+xml/)){oDocument=new ActiveXObject('Microsoft.XMLDOM');oDocument.loadXML(oRequest.responseText);}
+if(oDocument)
+if((bIE&&oDocument.parseError!=0)||(oDocument.documentElement&&oDocument.documentElement.tagName=="parsererror"))
+return null;return oDocument;};function fSynchronizeValues(oRequest){try{oRequest.responseText=oRequest._object.responseText;}catch(e){}
+try{oRequest.responseXML=fGetDocument(oRequest._object);}catch(e){}
+try{oRequest.status=oRequest._object.status;}catch(e){}
+try{oRequest.statusText=oRequest._object.statusText;}catch(e){}};function fCleanTransport(oRequest){oRequest._object.onreadystatechange=new window.Function;delete oRequest._headers;};if(!window.Function.prototype.apply){window.Function.prototype.apply=function(oRequest,oArguments){if(!oArguments)
+oArguments=[];oRequest.__func=this;oRequest.__func(oArguments[0],oArguments[1],oArguments[2],oArguments[3],oArguments[4]);delete oRequest.__func;};};OpenLayers.Request.XMLHttpRequest=cXMLHttpRequest;})();OpenLayers.Tile=OpenLayers.Class({EVENT_TYPES:["loadstart","loadend","reload","unload"],events:null,id:null,layer:null,url:null,bounds:null,size:null,position:null,isLoading:false,initialize:function(layer,position,bounds,url,size){this.layer=layer;this.position=position.clone();this.bounds=bounds.clone();this.url=url;this.size=size.clone();this.id=OpenLayers.Util.createUniqueID("Tile_");this.events=new OpenLayers.Events(this,null,this.EVENT_TYPES);},unload:function(){if(this.isLoading){this.isLoading=false;this.events.triggerEvent("unload");}},destroy:function(){this.layer=null;this.bounds=null;this.size=null;this.position=null;this.events.destroy();this.events=null;},clone:function(obj){if(obj==null){obj=new OpenLayers.Tile(this.layer,this.position,this.bounds,this.url,this.size);}
+OpenLayers.Util.applyDefaults(obj,this);return obj;},draw:function(){var maxExtent=this.layer.maxExtent;var withinMaxExtent=(maxExtent&&this.bounds.intersectsBounds(maxExtent,false));this.shouldDraw=(withinMaxExtent||this.layer.displayOutsideMaxExtent);this.clear();return this.shouldDraw;},moveTo:function(bounds,position,redraw){if(redraw==null){redraw=true;}
this.bounds=bounds.clone();this.position=position.clone();if(redraw){this.draw();}},clear:function(){},getBoundsFromBaseLayer:function(position){var msg=OpenLayers.i18n('reprojectDeprecated',{'layerName':this.layer.name});OpenLayers.Console.warn(msg);var topLeft=this.layer.map.getLonLatFromLayerPx(position);var bottomRightPx=position.clone();bottomRightPx.x+=this.size.w;bottomRightPx.y+=this.size.h;var bottomRight=this.layer.map.getLonLatFromLayerPx(bottomRightPx);if(topLeft.lon>bottomRight.lon){if(topLeft.lon<0){topLeft.lon=-180-(topLeft.lon+180);}else{bottomRight.lon=180+bottomRight.lon+180;}}
-var bounds=new OpenLayers.Bounds(topLeft.lon,bottomRight.lat,bottomRight.lon,topLeft.lat);return bounds;},startTransition:function(){},resetBackBuffer:function(){this.showTile();if(this.backBufferTile&&(this.isFirstDraw||!this.layer.numLoadingTiles)){this.isFirstDraw=false;var maxExtent=this.layer.maxExtent;var withinMaxExtent=(maxExtent&&this.bounds.intersectsBounds(maxExtent,false));if(withinMaxExtent){this.backBufferTile.position=this.position;this.backBufferTile.bounds=this.bounds;this.backBufferTile.size=this.size;this.backBufferTile.imageSize=this.layer.imageSize||this.size;this.backBufferTile.imageOffset=this.layer.imageOffset;this.backBufferTile.resolution=this.layer.getResolution();this.backBufferTile.renderTile();}}},showTile:function(){if(this.shouldDraw){this.show();}},show:function(){},hide:function(){},CLASS_NAME:"OpenLayers.Tile"});OpenLayers.Handler=OpenLayers.Class({id:null,control:null,map:null,keyMask:null,active:false,evt:null,initialize:function(control,callbacks,options){OpenLayers.Util.extend(this,options);this.control=control;this.callbacks=callbacks;if(control.map){this.setMap(control.map);}
+var bounds=new OpenLayers.Bounds(topLeft.lon,bottomRight.lat,bottomRight.lon,topLeft.lat);return bounds;},showTile:function(){if(this.shouldDraw){this.show();}},show:function(){},hide:function(){},CLASS_NAME:"OpenLayers.Tile"});OpenLayers.ProxyHost="";OpenLayers.nullHandler=function(request){OpenLayers.Console.userError(OpenLayers.i18n("unhandledRequest",{'statusText':request.statusText}));};OpenLayers.loadURL=function(uri,params,caller,onComplete,onFailure){if(typeof params=='string'){params=OpenLayers.Util.getParameters(params);}
+var success=(onComplete)?onComplete:OpenLayers.nullHandler;var failure=(onFailure)?onFailure:OpenLayers.nullHandler;return OpenLayers.Request.GET({url:uri,params:params,success:success,failure:failure,scope:caller});};OpenLayers.parseXMLString=function(text){var index=text.indexOf('<');if(index>0){text=text.substring(index);}
+var ajaxResponse=OpenLayers.Util.Try(function(){var xmldom=new ActiveXObject('Microsoft.XMLDOM');xmldom.loadXML(text);return xmldom;},function(){return new DOMParser().parseFromString(text,'text/xml');},function(){var req=new XMLHttpRequest();req.open("GET","data:"+"text/xml"+";charset=utf-8,"+encodeURIComponent(text),false);if(req.overrideMimeType){req.overrideMimeType("text/xml");}
+req.send(null);return req.responseXML;});return ajaxResponse;};OpenLayers.Ajax={emptyFunction:function(){},getTransport:function(){return OpenLayers.Util.Try(function(){return new XMLHttpRequest();},function(){return new ActiveXObject('Msxml2.XMLHTTP');},function(){return new ActiveXObject('Microsoft.XMLHTTP');})||false;},activeRequestCount:0};OpenLayers.Ajax.Responders={responders:[],register:function(responderToAdd){for(var i=0;i<this.responders.length;i++){if(responderToAdd==this.responders[i]){return;}}
+this.responders.push(responderToAdd);},unregister:function(responderToRemove){OpenLayers.Util.removeItem(this.reponders,responderToRemove);},dispatch:function(callback,request,transport){var responder;for(var i=0;i<this.responders.length;i++){responder=this.responders[i];if(responder[callback]&&typeof responder[callback]=='function'){try{responder[callback].apply(responder,[request,transport]);}catch(e){}}}}};OpenLayers.Ajax.Responders.register({onCreate:function(){OpenLayers.Ajax.activeRequestCount++;},onComplete:function(){OpenLayers.Ajax.activeRequestCount--;}});OpenLayers.Ajax.Base=OpenLayers.Class({initialize:function(options){this.options={method:'post',asynchronous:true,contentType:'application/xml',parameters:''};OpenLayers.Util.extend(this.options,options||{});this.options.method=this.options.method.toLowerCase();if(typeof this.options.parameters=='string'){this.options.parameters=OpenLayers.Util.getParameters(this.options.parameters);}}});OpenLayers.Ajax.Request=OpenLayers.Class(OpenLayers.Ajax.Base,{_complete:false,initialize:function(url,options){OpenLayers.Ajax.Base.prototype.initialize.apply(this,[options]);if(OpenLayers.ProxyHost&&OpenLayers.String.startsWith(url,"http")){url=OpenLayers.ProxyHost+encodeURIComponent(url);}
+this.transport=OpenLayers.Ajax.getTransport();this.request(url);},request:function(url){this.url=url;this.method=this.options.method;var params=OpenLayers.Util.extend({},this.options.parameters);if(this.method!='get'&&this.method!='post'){params['_method']=this.method;this.method='post';}
+this.parameters=params;if(params=OpenLayers.Util.getParameterString(params)){if(this.method=='get'){this.url+=((this.url.indexOf('?')>-1)?'&':'?')+params;}else if(/Konqueror|Safari|KHTML/.test(navigator.userAgent)){params+='&_=';}}
+try{var response=new OpenLayers.Ajax.Response(this);if(this.options.onCreate){this.options.onCreate(response);}
+OpenLayers.Ajax.Responders.dispatch('onCreate',this,response);this.transport.open(this.method.toUpperCase(),this.url,this.options.asynchronous);if(this.options.asynchronous){window.setTimeout(OpenLayers.Function.bind(this.respondToReadyState,this,1),10);}
+this.transport.onreadystatechange=OpenLayers.Function.bind(this.onStateChange,this);this.setRequestHeaders();this.body=this.method=='post'?(this.options.postBody||params):null;this.transport.send(this.body);if(!this.options.asynchronous&&this.transport.overrideMimeType){this.onStateChange();}}catch(e){this.dispatchException(e);}},onStateChange:function(){var readyState=this.transport.readyState;if(readyState>1&&!((readyState==4)&&this._complete)){this.respondToReadyState(this.transport.readyState);}},setRequestHeaders:function(){var headers={'X-Requested-With':'XMLHttpRequest','Accept':'text/javascript, text/html, application/xml, text/xml, */*','OpenLayers':true};if(this.method=='post'){headers['Content-type']=this.options.contentType+
+(this.options.encoding?'; charset='+this.options.encoding:'');if(this.transport.overrideMimeType&&(navigator.userAgent.match(/Gecko\/(\d{4})/)||[0,2005])[1]<2005){headers['Connection']='close';}}
+if(typeof this.options.requestHeaders=='object'){var extras=this.options.requestHeaders;if(typeof extras.push=='function'){for(var i=0,length=extras.length;i<length;i+=2){headers[extras[i]]=extras[i+1];}}else{for(var i in extras){headers[i]=extras[i];}}}
+for(var name in headers){this.transport.setRequestHeader(name,headers[name]);}},success:function(){var status=this.getStatus();return!status||(status>=200&&status<300);},getStatus:function(){try{return this.transport.status||0;}catch(e){return 0;}},respondToReadyState:function(readyState){var state=OpenLayers.Ajax.Request.Events[readyState];var response=new OpenLayers.Ajax.Response(this);if(state=='Complete'){try{this._complete=true;(this.options['on'+response.status]||this.options['on'+(this.success()?'Success':'Failure')]||OpenLayers.Ajax.emptyFunction)(response);}catch(e){this.dispatchException(e);}
+var contentType=response.getHeader('Content-type');}
+try{(this.options['on'+state]||OpenLayers.Ajax.emptyFunction)(response);OpenLayers.Ajax.Responders.dispatch('on'+state,this,response);}catch(e){this.dispatchException(e);}
+if(state=='Complete'){this.transport.onreadystatechange=OpenLayers.Ajax.emptyFunction;}},getHeader:function(name){try{return this.transport.getResponseHeader(name);}catch(e){return null;}},dispatchException:function(exception){var handler=this.options.onException;if(handler){handler(this,exception);OpenLayers.Ajax.Responders.dispatch('onException',this,exception);}else{var listener=false;var responders=OpenLayers.Ajax.Responders.responders;for(var i=0;i<responders.length;i++){if(responders[i].onException){listener=true;break;}}
+if(listener){OpenLayers.Ajax.Responders.dispatch('onException',this,exception);}else{throw exception;}}}});OpenLayers.Ajax.Request.Events=['Uninitialized','Loading','Loaded','Interactive','Complete'];OpenLayers.Ajax.Response=OpenLayers.Class({status:0,statusText:'',initialize:function(request){this.request=request;var transport=this.transport=request.transport,readyState=this.readyState=transport.readyState;if((readyState>2&&!(!!(window.attachEvent&&!window.opera)))||readyState==4){this.status=this.getStatus();this.statusText=this.getStatusText();this.responseText=transport.responseText==null?'':String(transport.responseText);}
+if(readyState==4){var xml=transport.responseXML;this.responseXML=xml===undefined?null:xml;}},getStatus:OpenLayers.Ajax.Request.prototype.getStatus,getStatusText:function(){try{return this.transport.statusText||'';}catch(e){return'';}},getHeader:OpenLayers.Ajax.Request.prototype.getHeader,getResponseHeader:function(name){return this.transport.getResponseHeader(name);}});OpenLayers.Ajax.getElementsByTagNameNS=function(parentnode,nsuri,nsprefix,tagname){var elem=null;if(parentnode.getElementsByTagNameNS){elem=parentnode.getElementsByTagNameNS(nsuri,tagname);}else{elem=parentnode.getElementsByTagName(nsprefix+':'+tagname);}
+return elem;};OpenLayers.Ajax.serializeXMLToString=function(xmldom){var serializer=new XMLSerializer();var data=serializer.serializeToString(xmldom);return data;};OpenLayers.Handler=OpenLayers.Class({id:null,control:null,map:null,keyMask:null,active:false,evt:null,initialize:function(control,callbacks,options){OpenLayers.Util.extend(this,options);this.control=control;this.callbacks=callbacks;if(control.map){this.setMap(control.map);}
OpenLayers.Util.extend(this,options);this.id=OpenLayers.Util.createUniqueID(this.CLASS_NAME+"_");},setMap:function(map){this.map=map;},checkModifiers:function(evt){if(this.keyMask==null){return true;}
var keyModifiers=(evt.shiftKey?OpenLayers.Handler.MOD_SHIFT:0)|(evt.ctrlKey?OpenLayers.Handler.MOD_CTRL:0)|(evt.altKey?OpenLayers.Handler.MOD_ALT:0);return(keyModifiers==this.keyMask);},activate:function(){if(this.active){return false;}
-var events=OpenLayers.Events.prototype.BROWSER_EVENTS;for(var i=0;i<events.length;i++){if(this[events[i]]){this.register(events[i],this[events[i]]);}}
+var events=OpenLayers.Events.prototype.BROWSER_EVENTS;for(var i=0,len=events.length;i<len;i++){if(this[events[i]]){this.register(events[i],this[events[i]]);}}
this.active=true;return true;},deactivate:function(){if(!this.active){return false;}
-var events=OpenLayers.Events.prototype.BROWSER_EVENTS;for(var i=0;i<events.length;i++){if(this[events[i]]){this.unregister(events[i],this[events[i]]);}}
-this.active=false;return true;},callback:function(name,args){if(name&&this.callbacks[name]){this.callbacks[name].apply(this.control,args);}},register:function(name,method){this.map.events.registerPriority(name,this,method);this.map.events.registerPriority(name,this,this.setEvent);},unregister:function(name,method){this.map.events.unregister(name,this,method);this.map.events.unregister(name,this,this.setEvent);},setEvent:function(evt){this.evt=evt;return true;},destroy:function(){this.deactivate();this.control=this.map=null;},CLASS_NAME:"OpenLayers.Handler"});OpenLayers.Handler.MOD_NONE=0;OpenLayers.Handler.MOD_SHIFT=1;OpenLayers.Handler.MOD_CTRL=2;OpenLayers.Handler.MOD_ALT=4;OpenLayers.Map=OpenLayers.Class({Z_INDEX_BASE:{BaseLayer:100,Overlay:325,Popup:750,Control:1000},EVENT_TYPES:["preaddlayer","addlayer","removelayer","changelayer","movestart","move","moveend","zoomend","popupopen","popupclose","addmarker","removemarker","clearmarkers","mouseover","mouseout","mousemove","dragstart","drag","dragend","changebaselayer"],id:null,fractionalZoom:false,events:null,div:null,dragging:false,size:null,viewPortDiv:null,layerContainerOrigin:null,layerContainerDiv:null,layers:null,controls:null,popups:null,baseLayer:null,center:null,resolution:null,zoom:0,viewRequestID:0,tileSize:null,projection:"EPSG:4326",units:'degrees',resolutions:null,maxResolution:1.40625,minResolution:null,maxScale:null,minScale:null,maxExtent:null,minExtent:null,restrictedExtent:null,numZoomLevels:16,theme:null,displayProjection:null,fallThrough:true,panTween:null,eventListeners:null,panMethod:OpenLayers.Easing.Expo.easeOut,paddingForPopups:null,initialize:function(div,options){this.tileSize=new OpenLayers.Size(OpenLayers.Map.TILE_WIDTH,OpenLayers.Map.TILE_HEIGHT);this.maxExtent=new OpenLayers.Bounds(-180,-90,180,90);this.paddingForPopups=new OpenLayers.Bounds(15,15,15,15);this.theme=OpenLayers._getScriptLocation()+'theme/default/style.css';OpenLayers.Util.extend(this,options);this.id=OpenLayers.Util.createUniqueID("OpenLayers.Map_");this.div=OpenLayers.Util.getElement(div);var id=this.div.id+"_OpenLayers_ViewPort";this.viewPortDiv=OpenLayers.Util.createDiv(id,null,null,null,"relative",null,"hidden");this.viewPortDiv.style.width="100%";this.viewPortDiv.style.height="100%";this.viewPortDiv.className="olMapViewport";this.div.appendChild(this.viewPortDiv);id=this.div.id+"_OpenLayers_Container";this.layerContainerDiv=OpenLayers.Util.createDiv(id);this.layerContainerDiv.style.zIndex=this.Z_INDEX_BASE['Popup']-1;this.viewPortDiv.appendChild(this.layerContainerDiv);this.events=new OpenLayers.Events(this,this.div,this.EVENT_TYPES,this.fallThrough);this.updateSize();if(this.eventListeners instanceof Object){this.events.on(this.eventListeners);}
+var events=OpenLayers.Events.prototype.BROWSER_EVENTS;for(var i=0,len=events.length;i<len;i++){if(this[events[i]]){this.unregister(events[i],this[events[i]]);}}
+this.active=false;return true;},callback:function(name,args){if(name&&this.callbacks[name]){this.callbacks[name].apply(this.control,args);}},register:function(name,method){this.map.events.registerPriority(name,this,method);this.map.events.registerPriority(name,this,this.setEvent);},unregister:function(name,method){this.map.events.unregister(name,this,method);this.map.events.unregister(name,this,this.setEvent);},setEvent:function(evt){this.evt=evt;return true;},destroy:function(){this.deactivate();this.control=this.map=null;},CLASS_NAME:"OpenLayers.Handler"});OpenLayers.Handler.MOD_NONE=0;OpenLayers.Handler.MOD_SHIFT=1;OpenLayers.Handler.MOD_CTRL=2;OpenLayers.Handler.MOD_ALT=4;OpenLayers.Map=OpenLayers.Class({Z_INDEX_BASE:{BaseLayer:100,Overlay:325,Feature:725,Popup:750,Control:1000},EVENT_TYPES:["preaddlayer","addlayer","removelayer","changelayer","movestart","move","moveend","zoomend","popupopen","popupclose","addmarker","removemarker","clearmarkers","mouseover","mouseout","mousemove","dragstart","drag","dragend","changebaselayer"],id:null,fractionalZoom:false,events:null,div:null,dragging:false,size:null,viewPortDiv:null,layerContainerOrigin:null,layerContainerDiv:null,layers:null,controls:null,popups:null,baseLayer:null,center:null,resolution:null,zoom:0,panRatio:1.5,viewRequestID:0,tileSize:null,projection:"EPSG:4326",units:'degrees',resolutions:null,maxResolution:1.40625,minResolution:null,maxScale:null,minScale:null,maxExtent:null,minExtent:null,restrictedExtent:null,numZoomLevels:16,theme:null,displayProjection:null,fallThrough:true,panTween:null,eventListeners:null,panMethod:OpenLayers.Easing.Expo.easeOut,paddingForPopups:null,initialize:function(div,options){this.tileSize=new OpenLayers.Size(OpenLayers.Map.TILE_WIDTH,OpenLayers.Map.TILE_HEIGHT);this.maxExtent=new OpenLayers.Bounds(-180,-90,180,90);this.paddingForPopups=new OpenLayers.Bounds(15,15,15,15);this.theme=OpenLayers._getScriptLocation()+'theme/default/style.css';OpenLayers.Util.extend(this,options);this.id=OpenLayers.Util.createUniqueID("OpenLayers.Map_");this.div=OpenLayers.Util.getElement(div);OpenLayers.Element.addClass(this.div,'olMap');var id=this.div.id+"_OpenLayers_ViewPort";this.viewPortDiv=OpenLayers.Util.createDiv(id,null,null,null,"relative",null,"hidden");this.viewPortDiv.style.width="100%";this.viewPortDiv.style.height="100%";this.viewPortDiv.className="olMapViewport";this.div.appendChild(this.viewPortDiv);id=this.div.id+"_OpenLayers_Container";this.layerContainerDiv=OpenLayers.Util.createDiv(id);this.layerContainerDiv.style.zIndex=this.Z_INDEX_BASE['Popup']-1;this.viewPortDiv.appendChild(this.layerContainerDiv);this.events=new OpenLayers.Events(this,this.div,this.EVENT_TYPES,this.fallThrough,{includeXY:true});this.updateSize();if(this.eventListeners instanceof Object){this.events.on(this.eventListeners);}
this.events.register("movestart",this,this.updateSize);if(OpenLayers.String.contains(navigator.appName,"Microsoft")){this.events.register("resize",this,this.updateSize);}else{this.updateSizeDestroy=OpenLayers.Function.bind(this.updateSize,this);OpenLayers.Event.observe(window,'resize',this.updateSizeDestroy);}
-if(this.theme){var addNode=true;var nodes=document.getElementsByTagName('link');for(var i=0;i<nodes.length;++i){if(OpenLayers.Util.isEquivalentUrl(nodes.item(i).href,this.theme)){addNode=false;break;}}
+if(this.theme){var addNode=true;var nodes=document.getElementsByTagName('link');for(var i=0,len=nodes.length;i<len;++i){if(OpenLayers.Util.isEquivalentUrl(nodes.item(i).href,this.theme)){addNode=false;break;}}
if(addNode){var cssNode=document.createElement('link');cssNode.setAttribute('rel','stylesheet');cssNode.setAttribute('type','text/css');cssNode.setAttribute('href',this.theme);document.getElementsByTagName('head')[0].appendChild(cssNode);}}
this.layers=[];if(this.controls==null){if(OpenLayers.Control!=null){this.controls=[new OpenLayers.Control.Navigation(),new OpenLayers.Control.PanZoom(),new OpenLayers.Control.ArgParser(),new OpenLayers.Control.Attribution()];}else{this.controls=[];}}
-for(var i=0;i<this.controls.length;i++){this.addControlToMap(this.controls[i]);}
+for(var i=0,len=this.controls.length;i<len;i++){this.addControlToMap(this.controls[i]);}
this.popups=[];this.unloadDestroy=OpenLayers.Function.bind(this.destroy,this);OpenLayers.Event.observe(window,'unload',this.unloadDestroy);},unloadDestroy:null,updateSizeDestroy:null,destroy:function(){if(!this.unloadDestroy){return false;}
OpenLayers.Event.stopObserving(window,'unload',this.unloadDestroy);this.unloadDestroy=null;if(this.updateSizeDestroy){OpenLayers.Event.stopObserving(window,'resize',this.updateSizeDestroy);}else{this.events.unregister("resize",this,this.updateSize);}
this.paddingForPopups=null;if(this.controls!=null){for(var i=this.controls.length-1;i>=0;--i){this.controls[i].destroy();}
@@ -308,38 +388,38 @@
this.layers=null;}
if(this.viewPortDiv){this.div.removeChild(this.viewPortDiv);}
this.viewPortDiv=null;if(this.eventListeners){this.events.un(this.eventListeners);this.eventListeners=null;}
-this.events.destroy();this.events=null;},setOptions:function(options){OpenLayers.Util.extend(this,options);},getTileSize:function(){return this.tileSize;},getBy:function(array,property,match){var test=(typeof match.test=="function");var found=OpenLayers.Array.filter(this[array],function(item){return item[property]==match||(test&&match.test(item[property]));});return found;},getLayersBy:function(property,match){return this.getBy("layers",property,match);},getLayersByName:function(match){return this.getLayersBy("name",match);},getLayersByClass:function(match){return this.getLayersBy("CLASS_NAME",match);},getControlsBy:function(property,match){return this.getBy("controls",property,match);},getControlsByClass:function(match){return this.getControlsBy("CLASS_NAME",match);},getLayer:function(id){var foundLayer=null;for(var i=0;i<this.layers.length;i++){var layer=this.layers[i];if(layer.id==id){foundLayer=layer;break;}}
+this.events.destroy();this.events=null;},setOptions:function(options){OpenLayers.Util.extend(this,options);},getTileSize:function(){return this.tileSize;},getBy:function(array,property,match){var test=(typeof match.test=="function");var found=OpenLayers.Array.filter(this[array],function(item){return item[property]==match||(test&&match.test(item[property]));});return found;},getLayersBy:function(property,match){return this.getBy("layers",property,match);},getLayersByName:function(match){return this.getLayersBy("name",match);},getLayersByClass:function(match){return this.getLayersBy("CLASS_NAME",match);},getControlsBy:function(property,match){return this.getBy("controls",property,match);},getControlsByClass:function(match){return this.getControlsBy("CLASS_NAME",match);},getLayer:function(id){var foundLayer=null;for(var i=0,len=this.layers.length;i<len;i++){var layer=this.layers[i];if(layer.id==id){foundLayer=layer;break;}}
return foundLayer;},setLayerZIndex:function(layer,zIdx){layer.setZIndex(this.Z_INDEX_BASE[layer.isBaseLayer?'BaseLayer':'Overlay']
-+zIdx*5);},resetLayersZIndex:function(){for(var i=0;i<this.layers.length;i++){var layer=this.layers[i];this.setLayerZIndex(layer,i);}},addLayer:function(layer){for(var i=0;i<this.layers.length;i++){if(this.layers[i]==layer){var msg=OpenLayers.i18n('layerAlreadyAdded',{'layerName':layer.name});OpenLayers.Console.warn(msg);return false;}}
++zIdx*5);},resetLayersZIndex:function(){for(var i=0,len=this.layers.length;i<len;i++){var layer=this.layers[i];this.setLayerZIndex(layer,i);}},addLayer:function(layer){for(var i=0,len=this.layers.length;i<len;i++){if(this.layers[i]==layer){var msg=OpenLayers.i18n('layerAlreadyAdded',{'layerName':layer.name});OpenLayers.Console.warn(msg);return false;}}
this.events.triggerEvent("preaddlayer",{layer:layer});layer.div.className="olLayerDiv";layer.div.style.overflow="";this.setLayerZIndex(layer,this.layers.length);if(layer.isFixed){this.viewPortDiv.appendChild(layer.div);}else{this.layerContainerDiv.appendChild(layer.div);}
this.layers.push(layer);layer.setMap(this);if(layer.isBaseLayer){if(this.baseLayer==null){this.setBaseLayer(layer);}else{layer.setVisibility(false);}}else{layer.redraw();}
-this.events.triggerEvent("addlayer",{layer:layer});},addLayers:function(layers){for(var i=0;i<layers.length;i++){this.addLayer(layers[i]);}},removeLayer:function(layer,setNewBaseLayer){if(setNewBaseLayer==null){setNewBaseLayer=true;}
+this.events.triggerEvent("addlayer",{layer:layer});},addLayers:function(layers){for(var i=0,len=layers.length;i<len;i++){this.addLayer(layers[i]);}},removeLayer:function(layer,setNewBaseLayer){if(setNewBaseLayer==null){setNewBaseLayer=true;}
if(layer.isFixed){this.viewPortDiv.removeChild(layer.div);}else{this.layerContainerDiv.removeChild(layer.div);}
-OpenLayers.Util.removeItem(this.layers,layer);layer.removeMap(this);layer.map=null;if(this.baseLayer==layer){this.baseLayer=null;if(setNewBaseLayer){for(var i=0;i<this.layers.length;i++){var iLayer=this.layers[i];if(iLayer.isBaseLayer){this.setBaseLayer(iLayer);break;}}}}
+OpenLayers.Util.removeItem(this.layers,layer);layer.removeMap(this);layer.map=null;if(this.baseLayer==layer){this.baseLayer=null;if(setNewBaseLayer){for(var i=0,len=this.layers.length;i<len;i++){var iLayer=this.layers[i];if(iLayer.isBaseLayer){this.setBaseLayer(iLayer);break;}}}}
this.resetLayersZIndex();this.events.triggerEvent("removelayer",{layer:layer});},getNumLayers:function(){return this.layers.length;},getLayerIndex:function(layer){return OpenLayers.Util.indexOf(this.layers,layer);},setLayerIndex:function(layer,idx){var base=this.getLayerIndex(layer);if(idx<0){idx=0;}else if(idx>this.layers.length){idx=this.layers.length;}
-if(base!=idx){this.layers.splice(base,1);this.layers.splice(idx,0,layer);for(var i=0;i<this.layers.length;i++){this.setLayerZIndex(this.layers[i],i);}
+if(base!=idx){this.layers.splice(base,1);this.layers.splice(idx,0,layer);for(var i=0,len=this.layers.length;i<len;i++){this.setLayerZIndex(this.layers[i],i);}
this.events.triggerEvent("changelayer",{layer:layer,property:"order"});}},raiseLayer:function(layer,delta){var idx=this.getLayerIndex(layer)+delta;this.setLayerIndex(layer,idx);},setBaseLayer:function(newBaseLayer){var oldExtent=null;if(this.baseLayer){oldExtent=this.baseLayer.getExtent();}
if(newBaseLayer!=this.baseLayer){if(OpenLayers.Util.indexOf(this.layers,newBaseLayer)!=-1){if(this.baseLayer!=null){this.baseLayer.setVisibility(false);}
this.baseLayer=newBaseLayer;this.viewRequestID++;this.baseLayer.visibility=true;var center=this.getCenter();if(center!=null){var newCenter=(oldExtent)?oldExtent.getCenterLonLat():center;var newZoom=(oldExtent)?this.getZoomForExtent(oldExtent,true):this.getZoomForResolution(this.resolution,true);this.setCenter(newCenter,newZoom,false,true);}
this.events.triggerEvent("changebaselayer",{layer:this.baseLayer});}}},addControl:function(control,px){this.controls.push(control);this.addControlToMap(control,px);},addControlToMap:function(control,px){control.outsideViewport=(control.div!=null);if(this.displayProjection&&!control.displayProjection){control.displayProjection=this.displayProjection;}
control.setMap(this);var div=control.draw(px);if(div){if(!control.outsideViewport){div.style.zIndex=this.Z_INDEX_BASE['Control']+
-this.controls.length;this.viewPortDiv.appendChild(div);}}},getControl:function(id){var returnControl=null;for(var i=0;i<this.controls.length;i++){var control=this.controls[i];if(control.id==id){returnControl=control;break;}}
+this.controls.length;this.viewPortDiv.appendChild(div);}}},getControl:function(id){var returnControl=null;for(var i=0,len=this.controls.length;i<len;i++){var control=this.controls[i];if(control.id==id){returnControl=control;break;}}
return returnControl;},removeControl:function(control){if((control)&&(control==this.getControl(control.id))){if(control.div&&(control.div.parentNode==this.viewPortDiv)){this.viewPortDiv.removeChild(control.div);}
OpenLayers.Util.removeItem(this.controls,control);}},addPopup:function(popup,exclusive){if(exclusive){for(var i=this.popups.length-1;i>=0;--i){this.removePopup(this.popups[i]);}}
popup.map=this;this.popups.push(popup);var popupDiv=popup.draw();if(popupDiv){popupDiv.style.zIndex=this.Z_INDEX_BASE['Popup']+
this.popups.length;this.layerContainerDiv.appendChild(popupDiv);}},removePopup:function(popup){OpenLayers.Util.removeItem(this.popups,popup);if(popup.div){try{this.layerContainerDiv.removeChild(popup.div);}
catch(e){}}
popup.map=null;},getSize:function(){var size=null;if(this.size!=null){size=this.size.clone();}
-return size;},updateSize:function(){this.events.element.offsets=null;var newSize=this.getCurrentSize();var oldSize=this.getSize();if(oldSize==null){this.size=oldSize=newSize;}
-if(!newSize.equals(oldSize)){this.size=newSize;for(var i=0;i<this.layers.length;i++){this.layers[i].onMapResize();}
+return size;},updateSize:function(){this.events.clearMouseCache();var newSize=this.getCurrentSize();var oldSize=this.getSize();if(oldSize==null){this.size=oldSize=newSize;}
+if(!newSize.equals(oldSize)){this.size=newSize;for(var i=0,len=this.layers.length;i<len;i++){this.layers[i].onMapResize();}
if(this.baseLayer!=null){var center=new OpenLayers.Pixel(newSize.w/2,newSize.h/2);var centerLL=this.getLonLatFromViewPortPx(center);var zoom=this.getZoom();this.zoom=null;this.setCenter(this.getCenter(),zoom);}}},getCurrentSize:function(){var size=new OpenLayers.Size(this.div.clientWidth,this.div.clientHeight);if(size.w==0&&size.h==0||isNaN(size.w)&&isNaN(size.h)){var dim=OpenLayers.Element.getDimensions(this.div);size.w=dim.width;size.h=dim.height;}
if(size.w==0&&size.h==0||isNaN(size.w)&&isNaN(size.h)){size.w=parseInt(this.div.style.width);size.h=parseInt(this.div.style.height);}
return size;},calculateBounds:function(center,resolution){var extent=null;if(center==null){center=this.getCenter();}
if(resolution==null){resolution=this.getResolution();}
if((center!=null)&&(resolution!=null)){var size=this.getSize();var w_deg=size.w*resolution;var h_deg=size.h*resolution;extent=new OpenLayers.Bounds(center.lon-w_deg/2,center.lat-h_deg/2,center.lon+w_deg/2,center.lat+h_deg/2);}
-return extent;},getCenter:function(){return this.center;},getZoom:function(){return this.zoom;},pan:function(dx,dy,options){if(!options){options={};}
-OpenLayers.Util.applyDefaults(options,{animate:true,dragging:false});var centerPx=this.getViewPortPxFromLonLat(this.getCenter());var newCenterPx=centerPx.add(dx,dy);if(!options.dragging||!newCenterPx.equals(centerPx)){var newCenterLonLat=this.getLonLatFromViewPortPx(newCenterPx);if(options.animate){this.panTo(newCenterLonLat);}else{this.setCenter(newCenterLonLat,null,options.dragging);}}},panTo:function(lonlat){if(this.panMethod&&this.getExtent().containsLonLat(lonlat)){if(!this.panTween){this.panTween=new OpenLayers.Tween(this.panMethod);}
-var center=this.getCenter();var from={lon:center.lon,lat:center.lat};var to={lon:lonlat.lon,lat:lonlat.lat};this.panTween.start(from,to,50,{callbacks:{start:OpenLayers.Function.bind(function(lonlat){this.events.triggerEvent("movestart");},this),eachStep:OpenLayers.Function.bind(function(lonlat){lonlat=new OpenLayers.LonLat(lonlat.lon,lonlat.lat);this.moveTo(lonlat,this.zoom,{'dragging':true,'noEvent':true});},this),done:OpenLayers.Function.bind(function(lonlat){lonlat=new OpenLayers.LonLat(lonlat.lon,lonlat.lat);this.moveTo(lonlat,this.zoom,{'noEvent':true});this.events.triggerEvent("moveend");},this)}});}else{this.setCenter(lonlat);}},setCenter:function(lonlat,zoom,dragging,forceZoomChange){this.moveTo(lonlat,zoom,{'dragging':dragging,'forceZoomChange':forceZoomChange,'caller':'setCenter'});},moveTo:function(lonlat,zoom,options){if(!options){options={};}
+return extent;},getCenter:function(){return this.center;},getZoom:function(){return this.zoom;},pan:function(dx,dy,options){options=OpenLayers.Util.applyDefaults(options,{animate:true,dragging:false});var centerPx=this.getViewPortPxFromLonLat(this.getCenter());var newCenterPx=centerPx.add(dx,dy);if(!options.dragging||!newCenterPx.equals(centerPx)){var newCenterLonLat=this.getLonLatFromViewPortPx(newCenterPx);if(options.animate){this.panTo(newCenterLonLat);}else{this.setCenter(newCenterLonLat,null,options.dragging);}}},panTo:function(lonlat){if(this.panMethod&&this.getExtent().scale(this.panRatio).containsLonLat(lonlat)){if(!this.panTween){this.panTween=new OpenLayers.Tween(this.panMethod);}
+var center=this.getCenter();if(lonlat.lon==center.lon&&lonlat.lat==center.lat){return;}
+var from={lon:center.lon,lat:center.lat};var to={lon:lonlat.lon,lat:lonlat.lat};this.panTween.start(from,to,50,{callbacks:{start:OpenLayers.Function.bind(function(lonlat){this.events.triggerEvent("movestart");},this),eachStep:OpenLayers.Function.bind(function(lonlat){lonlat=new OpenLayers.LonLat(lonlat.lon,lonlat.lat);this.moveTo(lonlat,this.zoom,{'dragging':true,'noEvent':true});},this),done:OpenLayers.Function.bind(function(lonlat){lonlat=new OpenLayers.LonLat(lonlat.lon,lonlat.lat);this.moveTo(lonlat,this.zoom,{'noEvent':true});this.events.triggerEvent("moveend");},this)}});}else{this.setCenter(lonlat);}},setCenter:function(lonlat,zoom,dragging,forceZoomChange){this.moveTo(lonlat,zoom,{'dragging':dragging,'forceZoomChange':forceZoomChange,'caller':'setCenter'});},moveTo:function(lonlat,zoom,options){if(!options){options={};}
var dragging=options.dragging;var forceZoomChange=options.forceZoomChange;var noEvent=options.noEvent;if(this.panTween&&options.caller=="setCenter"){this.panTween.stop();}
if(!this.center&&!this.isValidLonLat(lonlat)){lonlat=this.maxExtent.getCenterLonLat();}
if(this.restrictedExtent!=null){if(lonlat==null){lonlat=this.getCenter();}
@@ -356,41 +436,48 @@
this.center=lonlat.clone();}
if((zoomChanged)||(this.layerContainerOrigin==null)){this.layerContainerOrigin=this.center.clone();this.layerContainerDiv.style.left="0px";this.layerContainerDiv.style.top="0px";}
if(zoomChanged){this.zoom=zoom;this.resolution=this.getResolutionForZoom(zoom);this.viewRequestID++;}
-var bounds=this.getExtent();this.baseLayer.moveTo(bounds,zoomChanged,dragging);bounds=this.baseLayer.getExtent();for(var i=0;i<this.layers.length;i++){var layer=this.layers[i];if(!layer.isBaseLayer){var inRange=layer.calculateInRange();if(layer.inRange!=inRange){layer.inRange=inRange;if(!inRange){layer.display(false);}
+var bounds=this.getExtent();this.baseLayer.moveTo(bounds,zoomChanged,dragging);bounds=this.baseLayer.getExtent();for(var i=0,len=this.layers.length;i<len;i++){var layer=this.layers[i];if(!layer.isBaseLayer){var inRange=layer.calculateInRange();if(layer.inRange!=inRange){layer.inRange=inRange;if(!inRange){layer.display(false);}
this.events.triggerEvent("changelayer",{layer:layer,property:"visibility"});}
-if(inRange&&layer.visibility){layer.moveTo(bounds,zoomChanged,dragging);}}}
-if(zoomChanged){for(var i=0;i<this.popups.length;i++){this.popups[i].updatePosition();}}
+if(inRange&&layer.visibility){layer.moveTo(bounds,zoomChanged,dragging);layer.events.triggerEvent("moveend",{"zoomChanged":zoomChanged});}}}
+if(zoomChanged){for(var i=0,len=this.popups.length;i<len;i++){this.popups[i].updatePosition();}}
this.events.triggerEvent("move");if(zoomChanged){this.events.triggerEvent("zoomend");}}
if(!dragging&&!noEvent){this.events.triggerEvent("moveend");}
this.dragging=!!dragging;},centerLayerContainer:function(lonlat){var originPx=this.getViewPortPxFromLonLat(this.layerContainerOrigin);var newPx=this.getViewPortPxFromLonLat(lonlat);if((originPx!=null)&&(newPx!=null)){this.layerContainerDiv.style.left=Math.round(originPx.x-newPx.x)+"px";this.layerContainerDiv.style.top=Math.round(originPx.y-newPx.y)+"px";}},isValidZoomLevel:function(zoomLevel){return((zoomLevel!=null)&&(zoomLevel>=0)&&(zoomLevel<this.getNumZoomLevels()));},isValidLonLat:function(lonlat){var valid=false;if(lonlat!=null){var maxExtent=this.getMaxExtent();valid=maxExtent.containsLonLat(lonlat);}
return valid;},getProjection:function(){var projection=this.getProjectionObject();return projection?projection.getCode():null;},getProjectionObject:function(){var projection=null;if(this.baseLayer!=null){projection=this.baseLayer.projection;}
return projection;},getMaxResolution:function(){var maxResolution=null;if(this.baseLayer!=null){maxResolution=this.baseLayer.maxResolution;}
-return maxResolution;},getMaxExtent:function(){var maxExtent=null;if(this.baseLayer!=null){maxExtent=this.baseLayer.maxExtent;}
+return maxResolution;},getMaxExtent:function(options){var maxExtent=null;if(options&&options.restricted&&this.restrictedExtent){maxExtent=this.restrictedExtent;}else if(this.baseLayer!=null){maxExtent=this.baseLayer.maxExtent;}
return maxExtent;},getNumZoomLevels:function(){var numZoomLevels=null;if(this.baseLayer!=null){numZoomLevels=this.baseLayer.numZoomLevels;}
return numZoomLevels;},getExtent:function(){var extent=null;if(this.baseLayer!=null){extent=this.baseLayer.getExtent();}
return extent;},getResolution:function(){var resolution=null;if(this.baseLayer!=null){resolution=this.baseLayer.getResolution();}
-return resolution;},getScale:function(){var scale=null;if(this.baseLayer!=null){var res=this.getResolution();var units=this.baseLayer.units;scale=OpenLayers.Util.getScaleFromResolution(res,units);}
+return resolution;},getUnits:function(){var units=null;if(this.baseLayer!=null){units=this.baseLayer.units;}
+return units;},getScale:function(){var scale=null;if(this.baseLayer!=null){var res=this.getResolution();var units=this.baseLayer.units;scale=OpenLayers.Util.getScaleFromResolution(res,units);}
return scale;},getZoomForExtent:function(bounds,closest){var zoom=null;if(this.baseLayer!=null){zoom=this.baseLayer.getZoomForExtent(bounds,closest);}
return zoom;},getResolutionForZoom:function(zoom){var resolution=null;if(this.baseLayer){resolution=this.baseLayer.getResolutionForZoom(zoom);}
return resolution;},getZoomForResolution:function(resolution,closest){var zoom=null;if(this.baseLayer!=null){zoom=this.baseLayer.getZoomForResolution(resolution,closest);}
-return zoom;},zoomTo:function(zoom){if(this.isValidZoomLevel(zoom)){this.setCenter(null,zoom);}},zoomIn:function(){this.zoomTo(this.getZoom()+1);},zoomOut:function(){this.zoomTo(this.getZoom()-1);},zoomToExtent:function(bounds){var center=bounds.getCenterLonLat();if(this.baseLayer.wrapDateLine){var maxExtent=this.getMaxExtent();bounds=bounds.clone();while(bounds.right<bounds.left){bounds.right+=maxExtent.getWidth();}
+return zoom;},zoomTo:function(zoom){if(this.isValidZoomLevel(zoom)){this.setCenter(null,zoom);}},zoomIn:function(){this.zoomTo(this.getZoom()+1);},zoomOut:function(){this.zoomTo(this.getZoom()-1);},zoomToExtent:function(bounds,closest){var center=bounds.getCenterLonLat();if(this.baseLayer.wrapDateLine){var maxExtent=this.getMaxExtent();bounds=bounds.clone();while(bounds.right<bounds.left){bounds.right+=maxExtent.getWidth();}
center=bounds.getCenterLonLat().wrapDateLine(maxExtent);}
-this.setCenter(center,this.getZoomForExtent(bounds));},zoomToMaxExtent:function(){this.zoomToExtent(this.getMaxExtent());},zoomToScale:function(scale){var res=OpenLayers.Util.getResolutionFromScale(scale,this.baseLayer.units);var size=this.getSize();var w_deg=size.w*res;var h_deg=size.h*res;var center=this.getCenter();var extent=new OpenLayers.Bounds(center.lon-w_deg/2,center.lat-h_deg/2,center.lon+w_deg/2,center.lat+h_deg/2);this.zoomToExtent(extent);},getLonLatFromViewPortPx:function(viewPortPx){var lonlat=null;if(this.baseLayer!=null){lonlat=this.baseLayer.getLonLatFromViewPortPx(viewPortPx);}
+this.setCenter(center,this.getZoomForExtent(bounds,closest));},zoomToMaxExtent:function(options){var restricted=(options)?options.restricted:true;var maxExtent=this.getMaxExtent({'restricted':restricted});this.zoomToExtent(maxExtent);},zoomToScale:function(scale,closest){var res=OpenLayers.Util.getResolutionFromScale(scale,this.baseLayer.units);var size=this.getSize();var w_deg=size.w*res;var h_deg=size.h*res;var center=this.getCenter();var extent=new OpenLayers.Bounds(center.lon-w_deg/2,center.lat-h_deg/2,center.lon+w_deg/2,center.lat+h_deg/2);this.zoomToExtent(extent,closest);},getLonLatFromViewPortPx:function(viewPortPx){var lonlat=null;if(this.baseLayer!=null){lonlat=this.baseLayer.getLonLatFromViewPortPx(viewPortPx);}
return lonlat;},getViewPortPxFromLonLat:function(lonlat){var px=null;if(this.baseLayer!=null){px=this.baseLayer.getViewPortPxFromLonLat(lonlat);}
return px;},getLonLatFromPixel:function(px){return this.getLonLatFromViewPortPx(px);},getPixelFromLonLat:function(lonlat){var px=this.getViewPortPxFromLonLat(lonlat);px.x=Math.round(px.x);px.y=Math.round(px.y);return px;},getViewPortPxFromLayerPx:function(layerPx){var viewPortPx=null;if(layerPx!=null){var dX=parseInt(this.layerContainerDiv.style.left);var dY=parseInt(this.layerContainerDiv.style.top);viewPortPx=layerPx.add(dX,dY);}
return viewPortPx;},getLayerPxFromViewPortPx:function(viewPortPx){var layerPx=null;if(viewPortPx!=null){var dX=-parseInt(this.layerContainerDiv.style.left);var dY=-parseInt(this.layerContainerDiv.style.top);layerPx=viewPortPx.add(dX,dY);if(isNaN(layerPx.x)||isNaN(layerPx.y)){layerPx=null;}}
return layerPx;},getLonLatFromLayerPx:function(px){px=this.getViewPortPxFromLayerPx(px);return this.getLonLatFromViewPortPx(px);},getLayerPxFromLonLat:function(lonlat){var px=this.getPixelFromLonLat(lonlat);return this.getLayerPxFromViewPortPx(px);},CLASS_NAME:"OpenLayers.Map"});OpenLayers.Map.TILE_WIDTH=256;OpenLayers.Map.TILE_HEIGHT=256;OpenLayers.Marker=OpenLayers.Class({icon:null,lonlat:null,events:null,map:null,initialize:function(lonlat,icon){this.lonlat=lonlat;var newIcon=(icon)?icon:OpenLayers.Marker.defaultIcon();if(this.icon==null){this.icon=newIcon;}else{this.icon.url=newIcon.url;this.icon.size=newIcon.size;this.icon.offset=newIcon.offset;this.icon.calculateOffset=newIcon.calculateOffset;}
this.events=new OpenLayers.Events(this,this.icon.imageDiv,null);},destroy:function(){this.map=null;this.events.destroy();this.events=null;if(this.icon!=null){this.icon.destroy();this.icon=null;}},draw:function(px){return this.icon.draw(px);},moveTo:function(px){if((px!=null)&&(this.icon!=null)){this.icon.moveTo(px);}
this.lonlat=this.map.getLonLatFromLayerPx(px);},onScreen:function(){var onScreen=false;if(this.map){var screenBounds=this.map.getExtent();onScreen=screenBounds.containsLonLat(this.lonlat);}
-return onScreen;},inflate:function(inflate){if(this.icon){var newSize=new OpenLayers.Size(this.icon.size.w*inflate,this.icon.size.h*inflate);this.icon.setSize(newSize);}},setOpacity:function(opacity){this.icon.setOpacity(opacity);},setUrl:function(url){this.icon.setUrl(url);},display:function(display){this.icon.display(display);},CLASS_NAME:"OpenLayers.Marker"});OpenLayers.Marker.defaultIcon=function(){var url=OpenLayers.Util.getImagesLocation()+"marker.png";var size=new OpenLayers.Size(21,25);var calculateOffset=function(size){return new OpenLayers.Pixel(-(size.w/2),-size.h);};return new OpenLayers.Icon(url,size,null,calculateOffset);};OpenLayers.Tile.Image=OpenLayers.Class(OpenLayers.Tile,{url:null,imgDiv:null,frame:null,layerAlphaHack:null,initialize:function(layer,position,bounds,url,size){OpenLayers.Tile.prototype.initialize.apply(this,arguments);this.url=url;this.frame=document.createElement('div');this.frame.style.overflow='hidden';this.frame.style.position='absolute';this.layerAlphaHack=this.layer.alpha&&OpenLayers.Util.alphaHack();},destroy:function(){if(this.imgDiv!=null){if(this.layerAlphaHack){OpenLayers.Event.stopObservingElement(this.imgDiv.childNodes[0].id);}else{OpenLayers.Event.stopObservingElement(this.imgDiv.id);}
-if(this.imgDiv.parentNode==this.frame){this.frame.removeChild(this.imgDiv);this.imgDiv.map=null;}}
+return onScreen;},inflate:function(inflate){if(this.icon){var newSize=new OpenLayers.Size(this.icon.size.w*inflate,this.icon.size.h*inflate);this.icon.setSize(newSize);}},setOpacity:function(opacity){this.icon.setOpacity(opacity);},setUrl:function(url){this.icon.setUrl(url);},display:function(display){this.icon.display(display);},CLASS_NAME:"OpenLayers.Marker"});OpenLayers.Marker.defaultIcon=function(){var url=OpenLayers.Util.getImagesLocation()+"marker.png";var size=new OpenLayers.Size(21,25);var calculateOffset=function(size){return new OpenLayers.Pixel(-(size.w/2),-size.h);};return new OpenLayers.Icon(url,size,null,calculateOffset);};OpenLayers.Popup.AnchoredBubble=OpenLayers.Class(OpenLayers.Popup.Anchored,{rounded:false,initialize:function(id,lonlat,contentSize,contentHTML,anchor,closeBox,closeBoxCallback){this.padding=new OpenLayers.Bounds(0,OpenLayers.Popup.AnchoredBubble.CORNER_SIZE,0,OpenLayers.Popup.AnchoredBubble.CORNER_SIZE);OpenLayers.Popup.Anchored.prototype.initialize.apply(this,arguments);},draw:function(px){OpenLayers.Popup.Anchored.prototype.draw.apply(this,arguments);this.setContentHTML();this.setBackgroundColor();this.setOpacity();return this.div;},updateRelativePosition:function(){this.setRicoCorners();},setSize:function(contentSize){OpenLayers.Popup.Anchored.prototype.setSize.apply(this,arguments);this.setRicoCorners();},setBackgroundColor:function(color){if(color!=undefined){this.backgroundColor=color;}
+if(this.div!=null){if(this.contentDiv!=null){this.div.style.background="transparent";OpenLayers.Rico.Corner.changeColor(this.groupDiv,this.backgroundColor);}}},setOpacity:function(opacity){OpenLayers.Popup.Anchored.prototype.setOpacity.call(this,opacity);if(this.div!=null){if(this.groupDiv!=null){OpenLayers.Rico.Corner.changeOpacity(this.groupDiv,this.opacity);}}},setBorder:function(border){this.border=0;},setRicoCorners:function(){var corners=this.getCornersToRound(this.relativePosition);var options={corners:corners,color:this.backgroundColor,bgColor:"transparent",blend:false};if(!this.rounded){OpenLayers.Rico.Corner.round(this.div,options);this.rounded=true;}else{OpenLayers.Rico.Corner.reRound(this.groupDiv,options);this.setBackgroundColor();this.setOpacity();}},getCornersToRound:function(){var corners=['tl','tr','bl','br'];var corner=OpenLayers.Bounds.oppositeQuadrant(this.relativePosition);OpenLayers.Util.removeItem(corners,corner);return corners.join(" ");},CLASS_NAME:"OpenLayers.Popup.AnchoredBubble"});OpenLayers.Popup.AnchoredBubble.CORNER_SIZE=5;OpenLayers.Tile.Image=OpenLayers.Class(OpenLayers.Tile,{url:null,imgDiv:null,frame:null,layerAlphaHack:null,isBackBuffer:false,lastRatio:1,isFirstDraw:true,backBufferTile:null,initialize:function(layer,position,bounds,url,size){OpenLayers.Tile.prototype.initialize.apply(this,arguments);this.url=url;this.frame=document.createElement('div');this.frame.style.overflow='hidden';this.frame.style.position='absolute';this.layerAlphaHack=this.layer.alpha&&OpenLayers.Util.alphaHack();},destroy:function(){if(this.imgDiv!=null){if(this.layerAlphaHack){OpenLayers.Event.stopObservingElement(this.imgDiv.childNodes[0].id);}else{OpenLayers.Event.stopObservingElement(this.imgDiv.id);}
+if(this.imgDiv.parentNode==this.frame){this.frame.removeChild(this.imgDiv);this.imgDiv.map=null;}
+this.imgDiv.urls=null;}
this.imgDiv=null;if((this.frame!=null)&&(this.frame.parentNode==this.layer.div)){this.layer.div.removeChild(this.frame);}
-this.frame=null;OpenLayers.Tile.prototype.destroy.apply(this,arguments);},clone:function(obj){if(obj==null){obj=new OpenLayers.Tile.Image(this.layer,this.position,this.bounds,this.url,this.size);}
+this.frame=null;if(this.backBufferTile){this.backBufferTile.destroy();this.backBufferTile=null;}
+this.layer.events.unregister("loadend",this,this.resetBackBuffer);OpenLayers.Tile.prototype.destroy.apply(this,arguments);},clone:function(obj){if(obj==null){obj=new OpenLayers.Tile.Image(this.layer,this.position,this.bounds,this.url,this.size);}
obj=OpenLayers.Tile.prototype.clone.apply(this,[obj]);obj.imgDiv=null;return obj;},draw:function(){if(this.layer!=this.layer.map.baseLayer&&this.layer.reproject){this.bounds=this.getBoundsFromBaseLayer(this.position);}
-if(!OpenLayers.Tile.prototype.draw.apply(this,arguments)){return false;}
+var drawTile=OpenLayers.Tile.prototype.draw.apply(this,arguments);if(OpenLayers.Util.indexOf(this.layer.SUPPORTED_TRANSITIONS,this.layer.transitionEffect)!=-1){if(drawTile){if(!this.backBufferTile){this.backBufferTile=this.clone();this.backBufferTile.hide();this.backBufferTile.isBackBuffer=true;this.events.register('loadend',this,this.resetBackBuffer);this.layer.events.register("loadend",this,this.resetBackBuffer);}
+this.startTransition();}else{if(this.backBufferTile){this.backBufferTile.clear();}}}else{if(drawTile&&this.isFirstDraw){this.events.register('loadend',this,this.showTile);this.isFirstDraw=false;}}
+if(!drawTile){return false;}
if(this.isLoading){this.events.triggerEvent("reload");}else{this.isLoading=true;this.events.triggerEvent("loadstart");}
-return this.renderTile();},renderTile:function(){if(this.imgDiv==null){this.initImgDiv();}
-this.imgDiv.viewRequestID=this.layer.map.viewRequestID;this.url=this.layer.getURL(this.bounds);OpenLayers.Util.modifyDOMElement(this.frame,null,this.position,this.size);var imageSize=this.layer.getImageSize();if(this.layerAlphaHack){OpenLayers.Util.modifyAlphaImageDiv(this.imgDiv,null,null,imageSize,this.url);}else{OpenLayers.Util.modifyDOMElement(this.imgDiv,null,null,imageSize);this.imgDiv.src=this.url;}
+return this.renderTile();},resetBackBuffer:function(){this.showTile();if(this.backBufferTile&&(this.isFirstDraw||!this.layer.numLoadingTiles)){this.isFirstDraw=false;var maxExtent=this.layer.maxExtent;var withinMaxExtent=(maxExtent&&this.bounds.intersectsBounds(maxExtent,false));if(withinMaxExtent){this.backBufferTile.position=this.position;this.backBufferTile.bounds=this.bounds;this.backBufferTile.size=this.size;this.backBufferTile.imageSize=this.layer.imageSize||this.size;this.backBufferTile.imageOffset=this.layer.imageOffset;this.backBufferTile.resolution=this.layer.getResolution();this.backBufferTile.renderTile();}}},renderTile:function(){if(this.imgDiv==null){this.initImgDiv();}
+this.imgDiv.viewRequestID=this.layer.map.viewRequestID;if(this.layer.url instanceof Array){this.imgDiv.urls=this.layer.url.slice();}
+this.url=this.layer.getURL(this.bounds);OpenLayers.Util.modifyDOMElement(this.frame,null,this.position,this.size);var imageSize=this.layer.getImageSize();if(this.layerAlphaHack){OpenLayers.Util.modifyAlphaImageDiv(this.imgDiv,null,null,imageSize,this.url);}else{OpenLayers.Util.modifyDOMElement(this.imgDiv,null,null,imageSize);this.imgDiv.src=this.url;}
return true;},clear:function(){if(this.imgDiv){this.hide();if(OpenLayers.Tile.Image.useBlankTile){this.imgDiv.src=OpenLayers.Util.getImagesLocation()+"blank.gif";}}},initImgDiv:function(){var offset=this.layer.imageOffset;var size=this.layer.getImageSize();if(this.layerAlphaHack){this.imgDiv=OpenLayers.Util.createAlphaImageDiv(null,offset,size,null,"relative",null,null,null,true);}else{this.imgDiv=OpenLayers.Util.createImage(null,offset,size,null,"relative",null,null,true);}
this.imgDiv.className='olTileImage';this.frame.style.zIndex=this.isBackBuffer?0:1;this.frame.appendChild(this.imgDiv);this.layer.div.appendChild(this.frame);if(this.layer.opacity!=null){OpenLayers.Util.modifyDOMElement(this.imgDiv,null,null,null,null,null,null,this.layer.opacity);}
this.imgDiv.map=this.layer.map;var onload=function(){if(this.isLoading){this.isLoading=false;this.events.triggerEvent("loadend");}};if(this.layerAlphaHack){OpenLayers.Event.observe(this.imgDiv.childNodes[0],'load',OpenLayers.Function.bind(onload,this));}else{OpenLayers.Event.observe(this.imgDiv,'load',OpenLayers.Function.bind(onload,this));}
@@ -398,34 +485,48 @@
var ratio=1;if(this.backBufferTile.resolution){ratio=this.backBufferTile.resolution/this.layer.getResolution();}
if(ratio!=this.lastRatio){if(this.layer.transitionEffect=='resize'){var upperLeft=new OpenLayers.LonLat(this.backBufferTile.bounds.left,this.backBufferTile.bounds.top);var size=new OpenLayers.Size(this.backBufferTile.size.w*ratio,this.backBufferTile.size.h*ratio);var px=this.layer.map.getLayerPxFromLonLat(upperLeft);OpenLayers.Util.modifyDOMElement(this.backBufferTile.frame,null,px,size);var imageSize=this.backBufferTile.imageSize;imageSize=new OpenLayers.Size(imageSize.w*ratio,imageSize.h*ratio);var imageOffset=this.backBufferTile.imageOffset;if(imageOffset){imageOffset=new OpenLayers.Pixel(imageOffset.x*ratio,imageOffset.y*ratio);}
OpenLayers.Util.modifyDOMElement(this.backBufferTile.imgDiv,null,imageOffset,imageSize);this.backBufferTile.show();}}else{if(this.layer.singleTile){this.backBufferTile.show();}else{this.backBufferTile.hide();}}
-this.lastRatio=ratio;},show:function(){this.frame.style.display='';if(OpenLayers.Util.indexOf(this.layer.SUPPORTED_TRANSITIONS,this.layer.transitionEffect)!=-1){if(navigator.userAgent.toLowerCase().indexOf("gecko")!=-1){this.frame.scrollLeft=this.frame.scrollLeft;}}},hide:function(){this.frame.style.display='none';},CLASS_NAME:"OpenLayers.Tile.Image"});OpenLayers.Tile.Image.useBlankTile=(OpenLayers.Util.getBrowserName()=="safari"||OpenLayers.Util.getBrowserName()=="opera");OpenLayers.Control.OverviewMap=OpenLayers.Class(OpenLayers.Control,{element:null,ovmap:null,size:new OpenLayers.Size(180,90),layers:null,minRectSize:15,minRectDisplayClass:"RectReplacement",minRatio:8,maxRatio:32,mapOptions:null,handlers:null,initialize:function(options){this.layers=[];this.handlers={};OpenLayers.Control.prototype.initialize.apply(this,[options]);},destroy:function(){if(!this.mapDiv){return;}
+this.lastRatio=ratio;},show:function(){this.frame.style.display='';if(OpenLayers.Util.indexOf(this.layer.SUPPORTED_TRANSITIONS,this.layer.transitionEffect)!=-1){if(navigator.userAgent.toLowerCase().indexOf("gecko")!=-1){this.frame.scrollLeft=this.frame.scrollLeft;}}},hide:function(){this.frame.style.display='none';},CLASS_NAME:"OpenLayers.Tile.Image"});OpenLayers.Tile.Image.useBlankTile=(OpenLayers.Util.getBrowserName()=="safari"||OpenLayers.Util.getBrowserName()=="opera");OpenLayers.Control.OverviewMap=OpenLayers.Class(OpenLayers.Control,{element:null,ovmap:null,size:new OpenLayers.Size(180,90),layers:null,minRectSize:15,minRectDisplayClass:"RectReplacement",minRatio:8,maxRatio:32,mapOptions:null,handlers:null,resolutionFactor:1,initialize:function(options){this.layers=[];this.handlers={};OpenLayers.Control.prototype.initialize.apply(this,[options]);},destroy:function(){if(!this.mapDiv){return;}
this.handlers.click.destroy();this.mapDiv.removeChild(this.extentRectangle);this.extentRectangle=null;this.rectEvents.destroy();this.rectEvents=null;this.ovmap.destroy();this.ovmap=null;this.element.removeChild(this.mapDiv);this.mapDiv=null;this.div.removeChild(this.element);this.element=null;if(this.maximizeDiv){OpenLayers.Event.stopObservingElement(this.maximizeDiv);this.div.removeChild(this.maximizeDiv);this.maximizeDiv=null;}
if(this.minimizeDiv){OpenLayers.Event.stopObservingElement(this.minimizeDiv);this.div.removeChild(this.minimizeDiv);this.minimizeDiv=null;}
this.map.events.un({"moveend":this.update,"changebaselayer":this.baseLayerDraw,scope:this});OpenLayers.Control.prototype.destroy.apply(this,arguments);},draw:function(){OpenLayers.Control.prototype.draw.apply(this,arguments);if(!(this.layers.length>0)){if(this.map.baseLayer){var layer=this.map.baseLayer.clone();this.layers=[layer];}else{this.map.events.register("changebaselayer",this,this.baseLayerDraw);return this.div;}}
-this.element=document.createElement('div');this.element.className=this.displayClass+'Element';this.element.style.display='none';this.mapDiv=document.createElement('div');this.mapDiv.style.width=this.size.w+'px';this.mapDiv.style.height=this.size.h+'px';this.mapDiv.style.position='relative';this.mapDiv.style.overflow='hidden';this.mapDiv.id=OpenLayers.Util.createUniqueID('overviewMap');this.extentRectangle=document.createElement('div');this.extentRectangle.style.position='absolute';this.extentRectangle.style.zIndex=1000;this.extentRectangle.className=this.displayClass+'ExtentRectangle';this.mapDiv.appendChild(this.extentRectangle);this.element.appendChild(this.mapDiv);this.div.appendChild(this.element);if(!this.outsideViewport){this.div.className+=" "+this.displayClass+'Container';var imgLocation=OpenLayers.Util.getImagesLocation();var img=imgLocation+'layer-switcher-maximize.png';this.maximizeDiv=OpenLayers.Util.createAlphaImageDiv(this.displayClass+'MaximizeButton',null,new OpenLayers.Size(18,18),img,'absolute');this.maximizeDiv.style.display='none';this.maximizeDiv.className=this.displayClass+'MaximizeButton';OpenLayers.Event.observe(this.maximizeDiv,'click',OpenLayers.Function.bindAsEventListener(this.maximizeControl,this));this.div.appendChild(this.maximizeDiv);var img=imgLocation+'layer-switcher-minimize.png';this.minimizeDiv=OpenLayers.Util.createAlphaImageDiv('OpenLayers_Control_minimizeDiv',null,new OpenLayers.Size(18,18),img,'absolute');this.minimizeDiv.style.display='none';this.minimizeDiv.className=this.displayClass+'MinimizeButton';OpenLayers.Event.observe(this.minimizeDiv,'click',OpenLayers.Function.bindAsEventListener(this.minimizeControl,this));this.div.appendChild(this.minimizeDiv);var eventsToStop=['dblclick','mousedown'];for(var i=0;i<eventsToStop.length;i++){OpenLayers.Event.observe(this.maximizeDiv,eventsToStop[i],OpenLayers.Event.stop);OpenLayers.Event.observe(this.minimizeDiv,eventsToStop[i],OpenLayers.Event.stop);}
+this.element=document.createElement('div');this.element.className=this.displayClass+'Element';this.element.style.display='none';this.mapDiv=document.createElement('div');this.mapDiv.style.width=this.size.w+'px';this.mapDiv.style.height=this.size.h+'px';this.mapDiv.style.position='relative';this.mapDiv.style.overflow='hidden';this.mapDiv.id=OpenLayers.Util.createUniqueID('overviewMap');this.extentRectangle=document.createElement('div');this.extentRectangle.style.position='absolute';this.extentRectangle.style.zIndex=1000;this.extentRectangle.className=this.displayClass+'ExtentRectangle';this.mapDiv.appendChild(this.extentRectangle);this.element.appendChild(this.mapDiv);this.div.appendChild(this.element);if(!this.outsideViewport){this.div.className+=" "+this.displayClass+'Container';var imgLocation=OpenLayers.Util.getImagesLocation();var img=imgLocation+'layer-switcher-maximize.png';this.maximizeDiv=OpenLayers.Util.createAlphaImageDiv(this.displayClass+'MaximizeButton',null,new OpenLayers.Size(18,18),img,'absolute');this.maximizeDiv.style.display='none';this.maximizeDiv.className=this.displayClass+'MaximizeButton';OpenLayers.Event.observe(this.maximizeDiv,'click',OpenLayers.Function.bindAsEventListener(this.maximizeControl,this));this.div.appendChild(this.maximizeDiv);var img=imgLocation+'layer-switcher-minimize.png';this.minimizeDiv=OpenLayers.Util.createAlphaImageDiv('OpenLayers_Control_minimizeDiv',null,new OpenLayers.Size(18,18),img,'absolute');this.minimizeDiv.style.display='none';this.minimizeDiv.className=this.displayClass+'MinimizeButton';OpenLayers.Event.observe(this.minimizeDiv,'click',OpenLayers.Function.bindAsEventListener(this.minimizeControl,this));this.div.appendChild(this.minimizeDiv);var eventsToStop=['dblclick','mousedown'];for(var i=0,len=eventsToStop.length;i<len;i++){OpenLayers.Event.observe(this.maximizeDiv,eventsToStop[i],OpenLayers.Event.stop);OpenLayers.Event.observe(this.minimizeDiv,eventsToStop[i],OpenLayers.Event.stop);}
this.minimizeControl();}else{this.element.style.display='';}
if(this.map.getExtent()){this.update();}
this.map.events.register('moveend',this,this.update);return this.div;},baseLayerDraw:function(){this.draw();this.map.events.unregister("changebaselayer",this,this.baseLayerDraw);},rectDrag:function(px){var deltaX=this.handlers.drag.last.x-px.x;var deltaY=this.handlers.drag.last.y-px.y;if(deltaX!=0||deltaY!=0){var rectTop=this.rectPxBounds.top;var rectLeft=this.rectPxBounds.left;var rectHeight=Math.abs(this.rectPxBounds.getHeight());var rectWidth=this.rectPxBounds.getWidth();var newTop=Math.max(0,(rectTop-deltaY));newTop=Math.min(newTop,this.ovmap.size.h-this.hComp-rectHeight);var newLeft=Math.max(0,(rectLeft-deltaX));newLeft=Math.min(newLeft,this.ovmap.size.w-this.wComp-rectWidth);this.setRectPxBounds(new OpenLayers.Bounds(newLeft,newTop+rectHeight,newLeft+rectWidth,newTop));}},mapDivClick:function(evt){var pxCenter=this.rectPxBounds.getCenterPixel();var deltaX=evt.xy.x-pxCenter.x;var deltaY=evt.xy.y-pxCenter.y;var top=this.rectPxBounds.top;var left=this.rectPxBounds.left;var height=Math.abs(this.rectPxBounds.getHeight());var width=this.rectPxBounds.getWidth();var newTop=Math.max(0,(top+deltaY));newTop=Math.min(newTop,this.ovmap.size.h-height);var newLeft=Math.max(0,(left+deltaX));newLeft=Math.min(newLeft,this.ovmap.size.w-width);this.setRectPxBounds(new OpenLayers.Bounds(newLeft,newTop+height,newLeft+width,newTop));this.updateMapToRect();},maximizeControl:function(e){this.element.style.display='';this.showToggle(false);if(e!=null){OpenLayers.Event.stop(e);}},minimizeControl:function(e){this.element.style.display='none';this.showToggle(true);if(e!=null){OpenLayers.Event.stop(e);}},showToggle:function(minimize){this.maximizeDiv.style.display=minimize?'':'none';this.minimizeDiv.style.display=minimize?'none':'';},update:function(){if(this.ovmap==null){this.createMap();}
if(!this.isSuitableOverview()){this.updateOverview();}
-this.updateRectToMap();},isSuitableOverview:function(){var mapExtent=this.map.getExtent();var maxExtent=this.map.maxExtent;var testExtent=new OpenLayers.Bounds(Math.max(mapExtent.left,maxExtent.left),Math.max(mapExtent.bottom,maxExtent.bottom),Math.min(mapExtent.right,maxExtent.right),Math.min(mapExtent.top,maxExtent.top));var resRatio=this.ovmap.getResolution()/this.map.getResolution();return((resRatio>this.minRatio)&&(resRatio<=this.maxRatio)&&(this.ovmap.getExtent().containsBounds(testExtent)));},updateOverview:function(){var mapRes=this.map.getResolution();var targetRes=this.ovmap.getResolution();var resRatio=targetRes/mapRes;if(resRatio>this.maxRatio){targetRes=this.minRatio*mapRes;}else if(resRatio<=this.minRatio){targetRes=this.maxRatio*mapRes;}
-this.ovmap.setCenter(this.map.center,this.ovmap.getZoomForResolution(targetRes));this.updateRectToMap();},createMap:function(){var options=OpenLayers.Util.extend({controls:[],maxResolution:'auto',fallThrough:false},this.mapOptions);this.ovmap=new OpenLayers.Map(this.mapDiv,options);OpenLayers.Event.stopObserving(window,'unload',this.ovmap.unloadDestroy);this.ovmap.addLayers(this.layers);this.ovmap.zoomToMaxExtent();this.wComp=parseInt(OpenLayers.Element.getStyle(this.extentRectangle,'border-left-width'))+
+this.updateRectToMap();},isSuitableOverview:function(){var mapExtent=this.map.getExtent();var maxExtent=this.map.maxExtent;var testExtent=new OpenLayers.Bounds(Math.max(mapExtent.left,maxExtent.left),Math.max(mapExtent.bottom,maxExtent.bottom),Math.min(mapExtent.right,maxExtent.right),Math.min(mapExtent.top,maxExtent.top));if(this.ovmap.getProjection()!=this.map.getProjection()){testExtent=testExtent.transform(this.map.getProjectionObject(),this.ovmap.getProjectionObject());}
+var resRatio=this.ovmap.getResolution()/this.map.getResolution();return((resRatio>this.minRatio)&&(resRatio<=this.maxRatio)&&(this.ovmap.getExtent().containsBounds(testExtent)));},updateOverview:function(){var mapRes=this.map.getResolution();var targetRes=this.ovmap.getResolution();var resRatio=targetRes/mapRes;if(resRatio>this.maxRatio){targetRes=this.minRatio*mapRes;}else if(resRatio<=this.minRatio){targetRes=this.maxRatio*mapRes;}
+var center;if(this.ovmap.getProjection()!=this.map.getProjection()){center=this.map.center.clone();center.transform(this.map.getProjectionObject(),this.ovmap.getProjectionObject());}else{center=this.map.center;}
+this.ovmap.setCenter(center,this.ovmap.getZoomForResolution(targetRes*this.resolutionFactor));this.updateRectToMap();},createMap:function(){var options=OpenLayers.Util.extend({controls:[],maxResolution:'auto',fallThrough:false},this.mapOptions);this.ovmap=new OpenLayers.Map(this.mapDiv,options);OpenLayers.Event.stopObserving(window,'unload',this.ovmap.unloadDestroy);this.ovmap.addLayers(this.layers);this.ovmap.zoomToMaxExtent();this.wComp=parseInt(OpenLayers.Element.getStyle(this.extentRectangle,'border-left-width'))+
parseInt(OpenLayers.Element.getStyle(this.extentRectangle,'border-right-width'));this.wComp=(this.wComp)?this.wComp:2;this.hComp=parseInt(OpenLayers.Element.getStyle(this.extentRectangle,'border-top-width'))+
-parseInt(OpenLayers.Element.getStyle(this.extentRectangle,'border-bottom-width'));this.hComp=(this.hComp)?this.hComp:2;this.handlers.drag=new OpenLayers.Handler.Drag(this,{move:this.rectDrag,done:this.updateMapToRect},{map:this.ovmap});this.handlers.click=new OpenLayers.Handler.Click(this,{"click":this.mapDivClick},{"single":true,"double":false,"stopSingle":true,"stopDouble":true,"pixelTolerance":1,map:this.ovmap});this.handlers.click.activate();this.rectEvents=new OpenLayers.Events(this,this.extentRectangle,null,true);this.rectEvents.register("mouseover",this,function(e){if(!this.handlers.drag.active&&!this.map.dragging){this.handlers.drag.activate();}});this.rectEvents.register("mouseout",this,function(e){if(!this.handlers.drag.dragging){this.handlers.drag.deactivate();}});},updateRectToMap:function(){if(this.map.units!='degrees'){if(this.ovmap.getProjection()&&(this.map.getProjection()!=this.ovmap.getProjection())){alert(OpenLayers.i18n("sameProjection"));}}
-var pxBounds=this.getRectBoundsFromMapBounds(this.map.getExtent());if(pxBounds){this.setRectPxBounds(pxBounds);}},updateMapToRect:function(){var lonLatBounds=this.getMapBoundsFromRectBounds(this.rectPxBounds);this.map.panTo(lonLatBounds.getCenterLonLat());},setRectPxBounds:function(pxBounds){var top=Math.max(pxBounds.top,0);var left=Math.max(pxBounds.left,0);var bottom=Math.min(pxBounds.top+Math.abs(pxBounds.getHeight()),this.ovmap.size.h-this.hComp);var right=Math.min(pxBounds.left+pxBounds.getWidth(),this.ovmap.size.w-this.wComp);var width=Math.max(right-left,0);var height=Math.max(bottom-top,0);if(width<this.minRectSize||height<this.minRectSize){this.extentRectangle.className=this.displayClass+
+parseInt(OpenLayers.Element.getStyle(this.extentRectangle,'border-bottom-width'));this.hComp=(this.hComp)?this.hComp:2;this.handlers.drag=new OpenLayers.Handler.Drag(this,{move:this.rectDrag,done:this.updateMapToRect},{map:this.ovmap});this.handlers.click=new OpenLayers.Handler.Click(this,{"click":this.mapDivClick},{"single":true,"double":false,"stopSingle":true,"stopDouble":true,"pixelTolerance":1,map:this.ovmap});this.handlers.click.activate();this.rectEvents=new OpenLayers.Events(this,this.extentRectangle,null,true);this.rectEvents.register("mouseover",this,function(e){if(!this.handlers.drag.active&&!this.map.dragging){this.handlers.drag.activate();}});this.rectEvents.register("mouseout",this,function(e){if(!this.handlers.drag.dragging){this.handlers.drag.deactivate();}});if(this.ovmap.getProjection()!=this.map.getProjection()){var sourceUnits=this.map.getProjectionObject().getUnits()||this.map.units||this.map.baseLayer.units;var targetUnits=this.ovmap.getProjectionObject().getUnits()||this.ovmap.units||this.ovmap.baseLayer.units;this.resolutionFactor=sourceUnits&&targetUnits?OpenLayers.INCHES_PER_UNIT[sourceUnits]/OpenLayers.INCHES_PER_UNIT[targetUnits]:1;}},updateRectToMap:function(){var bounds;if(this.ovmap.getProjection()!=this.map.getProjection()){bounds=this.map.getExtent().transform(this.map.getProjectionObject(),this.ovmap.getProjectionObject());}else{bounds=this.map.getExtent();}
+var pxBounds=this.getRectBoundsFromMapBounds(bounds);if(pxBounds){this.setRectPxBounds(pxBounds);}},updateMapToRect:function(){var lonLatBounds=this.getMapBoundsFromRectBounds(this.rectPxBounds);if(this.ovmap.getProjection()!=this.map.getProjection()){lonLatBounds=lonLatBounds.transform(this.ovmap.getProjectionObject(),this.map.getProjectionObject());}
+this.map.panTo(lonLatBounds.getCenterLonLat());},setRectPxBounds:function(pxBounds){var top=Math.max(pxBounds.top,0);var left=Math.max(pxBounds.left,0);var bottom=Math.min(pxBounds.top+Math.abs(pxBounds.getHeight()),this.ovmap.size.h-this.hComp);var right=Math.min(pxBounds.left+pxBounds.getWidth(),this.ovmap.size.w-this.wComp);var width=Math.max(right-left,0);var height=Math.max(bottom-top,0);if(width<this.minRectSize||height<this.minRectSize){this.extentRectangle.className=this.displayClass+
this.minRectDisplayClass;var rLeft=left+(width/2)-(this.minRectSize/2);var rTop=top+(height/2)-(this.minRectSize/2);this.extentRectangle.style.top=Math.round(rTop)+'px';this.extentRectangle.style.left=Math.round(rLeft)+'px';this.extentRectangle.style.height=this.minRectSize+'px';this.extentRectangle.style.width=this.minRectSize+'px';}else{this.extentRectangle.className=this.displayClass+'ExtentRectangle';this.extentRectangle.style.top=Math.round(top)+'px';this.extentRectangle.style.left=Math.round(left)+'px';this.extentRectangle.style.height=Math.round(height)+'px';this.extentRectangle.style.width=Math.round(width)+'px';}
this.rectPxBounds=new OpenLayers.Bounds(Math.round(left),Math.round(bottom),Math.round(right),Math.round(top));},getRectBoundsFromMapBounds:function(lonLatBounds){var leftBottomLonLat=new OpenLayers.LonLat(lonLatBounds.left,lonLatBounds.bottom);var rightTopLonLat=new OpenLayers.LonLat(lonLatBounds.right,lonLatBounds.top);var leftBottomPx=this.getOverviewPxFromLonLat(leftBottomLonLat);var rightTopPx=this.getOverviewPxFromLonLat(rightTopLonLat);var bounds=null;if(leftBottomPx&&rightTopPx){bounds=new OpenLayers.Bounds(leftBottomPx.x,leftBottomPx.y,rightTopPx.x,rightTopPx.y);}
return bounds;},getMapBoundsFromRectBounds:function(pxBounds){var leftBottomPx=new OpenLayers.Pixel(pxBounds.left,pxBounds.bottom);var rightTopPx=new OpenLayers.Pixel(pxBounds.right,pxBounds.top);var leftBottomLonLat=this.getLonLatFromOverviewPx(leftBottomPx);var rightTopLonLat=this.getLonLatFromOverviewPx(rightTopPx);return new OpenLayers.Bounds(leftBottomLonLat.lon,leftBottomLonLat.lat,rightTopLonLat.lon,rightTopLonLat.lat);},getLonLatFromOverviewPx:function(overviewMapPx){var size=this.ovmap.size;var res=this.ovmap.getResolution();var center=this.ovmap.getExtent().getCenterLonLat();var delta_x=overviewMapPx.x-(size.w/2);var delta_y=overviewMapPx.y-(size.h/2);return new OpenLayers.LonLat(center.lon+delta_x*res,center.lat-delta_y*res);},getOverviewPxFromLonLat:function(lonlat){var res=this.ovmap.getResolution();var extent=this.ovmap.getExtent();var px=null;if(extent){px=new OpenLayers.Pixel(Math.round(1/res*(lonlat.lon-extent.left)),Math.round(1/res*(extent.top-lonlat.lat)));}
-return px;},CLASS_NAME:'OpenLayers.Control.OverviewMap'});OpenLayers.Handler.Click=OpenLayers.Class(OpenLayers.Handler,{delay:300,single:true,'double':false,pixelTolerance:0,stopSingle:false,stopDouble:false,timerId:null,down:null,initialize:function(control,callbacks,options){OpenLayers.Handler.prototype.initialize.apply(this,arguments);if(this.pixelTolerance!=null){this.mousedown=function(evt){this.down=evt.xy;return true;};}},mousedown:null,dblclick:function(evt){if(this.passesTolerance(evt)){if(this["double"]){this.callback('dblclick',[evt]);}
+return px;},CLASS_NAME:'OpenLayers.Control.OverviewMap'});OpenLayers.Feature=OpenLayers.Class({layer:null,id:null,lonlat:null,data:null,marker:null,popupClass:OpenLayers.Popup.AnchoredBubble,popup:null,initialize:function(layer,lonlat,data){this.layer=layer;this.lonlat=lonlat;this.data=(data!=null)?data:{};this.id=OpenLayers.Util.createUniqueID(this.CLASS_NAME+"_");},destroy:function(){if((this.layer!=null)&&(this.layer.map!=null)){if(this.popup!=null){this.layer.map.removePopup(this.popup);}}
+this.layer=null;this.id=null;this.lonlat=null;this.data=null;if(this.marker!=null){this.destroyMarker(this.marker);this.marker=null;}
+if(this.popup!=null){this.destroyPopup(this.popup);this.popup=null;}},onScreen:function(){var onScreen=false;if((this.layer!=null)&&(this.layer.map!=null)){var screenBounds=this.layer.map.getExtent();onScreen=screenBounds.containsLonLat(this.lonlat);}
+return onScreen;},createMarker:function(){if(this.lonlat!=null){this.marker=new OpenLayers.Marker(this.lonlat,this.data.icon);}
+return this.marker;},destroyMarker:function(){this.marker.destroy();},createPopup:function(closeBox){if(this.lonlat!=null){var id=this.id+"_popup";var anchor=(this.marker)?this.marker.icon:null;if(!this.popup){this.popup=new this.popupClass(id,this.lonlat,this.data.popupSize,this.data.popupContentHTML,anchor,closeBox);}
+if(this.data.overflow!=null){this.popup.contentDiv.style.overflow=this.data.overflow;}
+this.popup.feature=this;}
+return this.popup;},destroyPopup:function(){if(this.popup){this.popup.feature=null;this.popup.destroy();this.popup=null;}},CLASS_NAME:"OpenLayers.Feature"});OpenLayers.Handler.Click=OpenLayers.Class(OpenLayers.Handler,{delay:300,single:true,'double':false,pixelTolerance:0,stopSingle:false,stopDouble:false,timerId:null,down:null,rightclickTimerId:null,initialize:function(control,callbacks,options){OpenLayers.Handler.prototype.initialize.apply(this,arguments);if(this.pixelTolerance!=null){this.mousedown=function(evt){this.down=evt.xy;return true;};}},mousedown:null,mouseup:function(evt){var propagate=true;if(this.checkModifiers(evt)&&this.control.handleRightClicks&&OpenLayers.Event.isRightClick(evt)){propogate=this.rightclick(evt);}
+return propagate;},rightclick:function(evt){if(this.passesTolerance(evt)){if(this.rightclickTimerId!=null){this.clearTimer();this.callback('dblrightclick',[evt]);return!this.stopDouble;}else{var clickEvent=this['double']?OpenLayers.Util.extend({},evt):this.callback('rightclick',[evt]);var delayedRightCall=OpenLayers.Function.bind(this.delayedRightCall,this,clickEvent);this.rightclickTimerId=window.setTimeout(delayedRightCall,this.delay);}}
+return!this.stopSingle;},delayedRightCall:function(evt){this.rightclickTimerId=null;if(evt){this.callback('rightclick',[evt]);}
+return!this.stopSingle;},dblclick:function(evt){if(this.passesTolerance(evt)){if(this["double"]){this.callback('dblclick',[evt]);}
this.clearTimer();}
return!this.stopDouble;},click:function(evt){if(this.passesTolerance(evt)){if(this.timerId!=null){this.clearTimer();}else{var clickEvent=this.single?OpenLayers.Util.extend({},evt):null;this.timerId=window.setTimeout(OpenLayers.Function.bind(this.delayedCall,this,clickEvent),this.delay);}}
return!this.stopSingle;},passesTolerance:function(evt){var passes=true;if(this.pixelTolerance!=null&&this.down){var dpx=Math.sqrt(Math.pow(this.down.x-evt.xy.x,2)+
Math.pow(this.down.y-evt.xy.y,2));if(dpx>this.pixelTolerance){passes=false;}}
return passes;},clearTimer:function(){if(this.timerId!=null){window.clearTimeout(this.timerId);this.timerId=null;}},delayedCall:function(evt){this.timerId=null;if(evt){this.callback('click',[evt]);}},deactivate:function(){var deactivated=false;if(OpenLayers.Handler.prototype.deactivate.apply(this,arguments)){this.clearTimer();this.down=null;deactivated=true;}
-return deactivated;},CLASS_NAME:"OpenLayers.Handler.Click"});OpenLayers.Handler.Drag=OpenLayers.Class(OpenLayers.Handler,{started:false,stopDown:true,dragging:false,last:null,start:null,oldOnselectstart:null,initialize:function(control,callbacks,options){OpenLayers.Handler.prototype.initialize.apply(this,arguments);},down:function(evt){},move:function(evt){},up:function(evt){},out:function(evt){},mousedown:function(evt){var propagate=true;this.dragging=false;if(this.checkModifiers(evt)&&OpenLayers.Event.isLeftClick(evt)){this.started=true;this.start=evt.xy;this.last=evt.xy;this.map.div.style.cursor="move";this.down(evt);this.callback("down",[evt.xy]);OpenLayers.Event.stop(evt);if(!this.oldOnselectstart){this.oldOnselectstart=(document.onselectstart)?document.onselectstart:function(){return true;};document.onselectstart=function(){return false;};}
+return deactivated;},CLASS_NAME:"OpenLayers.Handler.Click"});OpenLayers.Handler.Drag=OpenLayers.Class(OpenLayers.Handler,{started:false,stopDown:true,dragging:false,last:null,start:null,oldOnselectstart:null,interval:0,timeoutId:null,initialize:function(control,callbacks,options){OpenLayers.Handler.prototype.initialize.apply(this,arguments);},down:function(evt){},move:function(evt){},up:function(evt){},out:function(evt){},mousedown:function(evt){var propagate=true;this.dragging=false;if(this.checkModifiers(evt)&&OpenLayers.Event.isLeftClick(evt)){this.started=true;this.start=evt.xy;this.last=evt.xy;this.map.div.style.cursor="move";this.down(evt);this.callback("down",[evt.xy]);OpenLayers.Event.stop(evt);if(!this.oldOnselectstart){this.oldOnselectstart=(document.onselectstart)?document.onselectstart:function(){return true;};document.onselectstart=function(){return false;};}
propagate=!this.stopDown;}else{this.started=false;this.start=null;this.last=null;}
-return propagate;},mousemove:function(evt){if(this.started){if(evt.xy.x!=this.last.x||evt.xy.y!=this.last.y){this.dragging=true;this.move(evt);this.callback("move",[evt.xy]);if(!this.oldOnselectstart){this.oldOnselectstart=document.onselectstart;document.onselectstart=function(){return false;};}
-this.last=evt.xy;}}
-return true;},mouseup:function(evt){if(this.started){var dragged=(this.start!=this.last);this.started=false;this.dragging=false;this.map.div.style.cursor="";this.up(evt);this.callback("up",[evt.xy]);if(dragged){this.callback("done",[evt.xy]);}
+return propagate;},mousemove:function(evt){if(this.started&&!this.timeoutId&&(evt.xy.x!=this.last.x||evt.xy.y!=this.last.y)){if(this.interval>0){this.timeoutId=setTimeout(OpenLayers.Function.bind(this.removeTimeout,this),this.interval);}
+this.dragging=true;this.move(evt);this.callback("move",[evt.xy]);if(!this.oldOnselectstart){this.oldOnselectstart=document.onselectstart;document.onselectstart=function(){return false;};}
+this.last=this.evt.xy;}
+return true;},removeTimeout:function(){this.timeoutId=null;},mouseup:function(evt){if(this.started){var dragged=(this.start!=this.last);this.started=false;this.dragging=false;this.map.div.style.cursor="";this.up(evt);this.callback("up",[evt.xy]);if(dragged){this.callback("done",[evt.xy]);}
document.onselectstart=this.oldOnselectstart;}
return true;},mouseout:function(evt){if(this.started&&OpenLayers.Util.mouseLeft(evt,this.map.div)){var dragged=(this.start!=this.last);this.started=false;this.dragging=false;this.map.div.style.cursor="";this.out(evt);this.callback("out",[]);if(dragged){this.callback("done",[evt.xy]);}
if(document.onselectstart){document.onselectstart=this.oldOnselectstart;}}
@@ -434,14 +535,14 @@
return deactivated;},CLASS_NAME:"OpenLayers.Handler.Drag"});OpenLayers.Handler.MouseWheel=OpenLayers.Class(OpenLayers.Handler,{wheelListener:null,mousePosition:null,initialize:function(control,callbacks,options){OpenLayers.Handler.prototype.initialize.apply(this,arguments);this.wheelListener=OpenLayers.Function.bindAsEventListener(this.onWheelEvent,this);},destroy:function(){OpenLayers.Handler.prototype.destroy.apply(this,arguments);this.wheelListener=null;},onWheelEvent:function(e){if(!this.map||!this.checkModifiers(e)){return;}
var overScrollableDiv=false;var overLayerDiv=false;var overMapDiv=false;var elem=OpenLayers.Event.element(e);while((elem!=null)&&!overMapDiv&&!overScrollableDiv){if(!overScrollableDiv){try{if(elem.currentStyle){overflow=elem.currentStyle["overflow"];}else{var style=document.defaultView.getComputedStyle(elem,null);var overflow=style.getPropertyValue("overflow");}
overScrollableDiv=(overflow&&(overflow=="auto")||(overflow=="scroll"));}catch(err){}}
-if(!overLayerDiv){for(var i=0;i<this.map.layers.length;i++){if(elem==this.map.layers[i].div||elem==this.map.layers[i].pane){overLayerDiv=true;break;}}}
+if(!overLayerDiv){for(var i=0,len=this.map.layers.length;i<len;i++){if(elem==this.map.layers[i].div||elem==this.map.layers[i].pane){overLayerDiv=true;break;}}}
overMapDiv=(elem==this.map.div);elem=elem.parentNode;}
if(!overScrollableDiv&&overMapDiv){if(overLayerDiv){this.wheelZoom(e);}
OpenLayers.Event.stop(e);}},wheelZoom:function(e){var delta=0;if(!e){e=window.event;}
if(e.wheelDelta){delta=e.wheelDelta/120;if(window.opera&&window.opera.version()<9.2){delta=-delta;}}else if(e.detail){delta=-e.detail/3;}
if(delta){if(this.mousePosition){e.xy=this.mousePosition;}
if(!e.xy){e.xy=this.map.getPixelFromLonLat(this.map.getCenter());}
-if(delta<0){this.callback("down",[e,delta]);}else{this.callback("up",[e,delta]);}}},mousemove:function(evt){this.mousePosition=evt.xy;},activate:function(evt){if(OpenLayers.Handler.prototype.activate.apply(this,arguments)){var wheelListener=this.wheelListener;OpenLayers.Event.observe(window,"DOMMouseScroll",wheelListener);OpenLayers.Event.observe(window,"mousewheel",wheelListener);OpenLayers.Event.observe(document,"mousewheel",wheelListener);return true;}else{return false;}},deactivate:function(evt){if(OpenLayers.Handler.prototype.deactivate.apply(this,arguments)){var wheelListener=this.wheelListener;OpenLayers.Event.stopObserving(window,"DOMMouseScroll",wheelListener);OpenLayers.Event.stopObserving(window,"mousewheel",wheelListener);OpenLayers.Event.stopObserving(document,"mousewheel",wheelListener);return true;}else{return false;}},CLASS_NAME:"OpenLayers.Handler.MouseWheel"});OpenLayers.Layer=OpenLayers.Class({id:null,name:null,div:null,opacity:null,EVENT_TYPES:["loadstart","loadend","loadcancel","visibilitychanged"],events:null,map:null,isBaseLayer:false,alpha:false,displayInLayerSwitcher:true,visibility:true,attribution:null,inRange:false,imageSize:null,imageOffset:null,options:null,eventListeners:null,gutter:0,projection:null,units:null,scales:null,resolutions:null,maxExtent:null,minExtent:null,maxResolution:null,minResolution:null,numZoomLevels:null,minScale:null,maxScale:null,displayOutsideMaxExtent:false,wrapDateLine:false,transitionEffect:null,SUPPORTED_TRANSITIONS:['resize'],initialize:function(name,options){this.addOptions(options);this.name=name;if(this.id==null){this.id=OpenLayers.Util.createUniqueID(this.CLASS_NAME+"_");this.div=OpenLayers.Util.createDiv(this.id);this.div.style.width="100%";this.div.style.height="100%";this.events=new OpenLayers.Events(this,this.div,this.EVENT_TYPES);if(this.eventListeners instanceof Object){this.events.on(this.eventListeners);}}
+if(delta<0){this.callback("down",[e,delta]);}else{this.callback("up",[e,delta]);}}},mousemove:function(evt){this.mousePosition=evt.xy;},activate:function(evt){if(OpenLayers.Handler.prototype.activate.apply(this,arguments)){var wheelListener=this.wheelListener;OpenLayers.Event.observe(window,"DOMMouseScroll",wheelListener);OpenLayers.Event.observe(window,"mousewheel",wheelListener);OpenLayers.Event.observe(document,"mousewheel",wheelListener);return true;}else{return false;}},deactivate:function(evt){if(OpenLayers.Handler.prototype.deactivate.apply(this,arguments)){var wheelListener=this.wheelListener;OpenLayers.Event.stopObserving(window,"DOMMouseScroll",wheelListener);OpenLayers.Event.stopObserving(window,"mousewheel",wheelListener);OpenLayers.Event.stopObserving(document,"mousewheel",wheelListener);return true;}else{return false;}},CLASS_NAME:"OpenLayers.Handler.MouseWheel"});OpenLayers.Layer=OpenLayers.Class({id:null,name:null,div:null,opacity:null,alwaysInRange:null,EVENT_TYPES:["loadstart","loadend","loadcancel","visibilitychanged","moveend"],events:null,map:null,isBaseLayer:false,alpha:false,displayInLayerSwitcher:true,visibility:true,attribution:null,inRange:false,imageSize:null,imageOffset:null,options:null,eventListeners:null,gutter:0,projection:null,units:null,scales:null,resolutions:null,maxExtent:null,minExtent:null,maxResolution:null,minResolution:null,numZoomLevels:null,minScale:null,maxScale:null,displayOutsideMaxExtent:false,wrapDateLine:false,transitionEffect:null,SUPPORTED_TRANSITIONS:['resize'],initialize:function(name,options){this.addOptions(options);this.name=name;if(this.id==null){this.id=OpenLayers.Util.createUniqueID(this.CLASS_NAME+"_");this.div=OpenLayers.Util.createDiv(this.id);this.div.style.width="100%";this.div.style.height="100%";this.events=new OpenLayers.Events(this,this.div,this.EVENT_TYPES);if(this.eventListeners instanceof Object){this.events.on(this.eventListeners);}}
if(this.wrapDateLine){this.displayOutsideMaxExtent=true;}},destroy:function(setNewBaseLayer){if(setNewBaseLayer==null){setNewBaseLayer=true;}
if(this.map!=null){this.map.removeLayer(this,setNewBaseLayer);}
this.projection=null;this.map=null;this.name=null;this.div=null;this.options=null;if(this.events){if(this.eventListeners){this.events.un(this.eventListeners);}
@@ -453,41 +554,73 @@
this.display(display);},setMap:function(map){if(this.map==null){this.map=map;this.maxExtent=this.maxExtent||this.map.maxExtent;this.projection=this.projection||this.map.projection;if(this.projection&&typeof this.projection=="string"){this.projection=new OpenLayers.Projection(this.projection);}
this.units=this.projection.getUnits()||this.units||this.map.units;this.initResolutions();if(!this.isBaseLayer){this.inRange=this.calculateInRange();var show=((this.visibility)&&(this.inRange));this.div.style.display=show?"":"none";}
this.setTileSize();}},removeMap:function(map){},getImageSize:function(){return(this.imageSize||this.tileSize);},setTileSize:function(size){var tileSize=(size)?size:((this.tileSize)?this.tileSize:this.map.getTileSize());this.tileSize=tileSize;if(this.gutter){this.imageOffset=new OpenLayers.Pixel(-this.gutter,-this.gutter);this.imageSize=new OpenLayers.Size(tileSize.w+(2*this.gutter),tileSize.h+(2*this.gutter));}},getVisibility:function(){return this.visibility;},setVisibility:function(visibility){if(visibility!=this.visibility){this.visibility=visibility;this.display(visibility);this.redraw();if(this.map!=null){this.map.events.triggerEvent("changelayer",{layer:this,property:"visibility"});}
-this.events.triggerEvent("visibilitychanged");}},display:function(display){var inRange=this.calculateInRange();if(display!=(this.div.style.display!="none")){this.div.style.display=(display&&inRange)?"block":"none";}},calculateInRange:function(){var inRange=false;if(this.map){var resolution=this.map.getResolution();inRange=((resolution>=this.minResolution)&&(resolution<=this.maxResolution));}
-return inRange;},setIsBaseLayer:function(isBaseLayer){if(isBaseLayer!=this.isBaseLayer){this.isBaseLayer=isBaseLayer;if(this.map!=null){this.map.events.triggerEvent("changebaselayer",{layer:this});}}},initResolutions:function(){var props=new Array('projection','units','scales','resolutions','maxScale','minScale','maxResolution','minResolution','minExtent','maxExtent','numZoomLevels','maxZoomLevel');var confProps={};for(var i=0;i<props.length;i++){var property=props[i];confProps[property]=this.options[property]||this.map[property];}
-if(this.options.minScale!=null&&this.options.maxScale!=null&&this.options.scales==null){confProps.scales=null;}
-if(this.options.minResolution!=null&&this.options.maxResolution!=null&&this.options.resolutions==null){confProps.resolutions=null;}
+this.events.triggerEvent("visibilitychanged");}},display:function(display){var inRange=this.calculateInRange();if(display!=(this.div.style.display!="none")){this.div.style.display=(display&&inRange)?"block":"none";}},calculateInRange:function(){var inRange=false;if(this.alwaysInRange){inRange=true;}else{if(this.map){var resolution=this.map.getResolution();inRange=((resolution>=this.minResolution)&&(resolution<=this.maxResolution));}}
+return inRange;},setIsBaseLayer:function(isBaseLayer){if(isBaseLayer!=this.isBaseLayer){this.isBaseLayer=isBaseLayer;if(this.map!=null){this.map.events.triggerEvent("changebaselayer",{layer:this});}}},initResolutions:function(){var props=new Array('projection','units','scales','resolutions','maxScale','minScale','maxResolution','minResolution','minExtent','maxExtent','numZoomLevels','maxZoomLevel');var notScaleProps=['projection','units'];var useInRange=false;var confProps={};for(var i=0,len=props.length;i<len;i++){var property=props[i];if(this.options[property]&&OpenLayers.Util.indexOf(notScaleProps,property)==-1){useInRange=true;}
+confProps[property]=this.options[property]||this.map[property];}
+if(this.alwaysInRange==null){this.alwaysInRange=!useInRange;}
+if((this.options.minScale!=null||this.options.maxScale!=null)&&this.options.scales==null){confProps.scales=null;}
+if((this.options.minResolution!=null||this.options.maxResolution!=null)&&this.options.resolutions==null){confProps.resolutions=null;}
if((!confProps.numZoomLevels)&&(confProps.maxZoomLevel)){confProps.numZoomLevels=confProps.maxZoomLevel+1;}
-if((confProps.scales!=null)||(confProps.resolutions!=null)){if(confProps.scales!=null){confProps.resolutions=[];for(var i=0;i<confProps.scales.length;i++){var scale=confProps.scales[i];confProps.resolutions[i]=OpenLayers.Util.getResolutionFromScale(scale,confProps.units);}}
+if((confProps.scales!=null)||(confProps.resolutions!=null)){if(confProps.scales!=null){confProps.resolutions=[];for(var i=0,len=confProps.scales.length;i<len;i++){var scale=confProps.scales[i];confProps.resolutions[i]=OpenLayers.Util.getResolutionFromScale(scale,confProps.units);}}
confProps.numZoomLevels=confProps.resolutions.length;}else{if(confProps.minScale){confProps.maxResolution=OpenLayers.Util.getResolutionFromScale(confProps.minScale,confProps.units);}else if(confProps.maxResolution=="auto"){var viewSize=this.map.getSize();var wRes=confProps.maxExtent.getWidth()/viewSize.w;var hRes=confProps.maxExtent.getHeight()/viewSize.h;confProps.maxResolution=Math.max(wRes,hRes);}
if(confProps.maxScale!=null){confProps.minResolution=OpenLayers.Util.getResolutionFromScale(confProps.maxScale,confProps.units);}else if((confProps.minResolution=="auto")&&(confProps.minExtent!=null)){var viewSize=this.map.getSize();var wRes=confProps.minExtent.getWidth()/viewSize.w;var hRes=confProps.minExtent.getHeight()/viewSize.h;confProps.minResolution=Math.max(wRes,hRes);}
if(confProps.minResolution!=null&&this.options.numZoomLevels==undefined){var ratio=confProps.maxResolution/confProps.minResolution;confProps.numZoomLevels=Math.floor(Math.log(ratio)/Math.log(2))+1;}
confProps.resolutions=new Array(confProps.numZoomLevels);var base=2;if(typeof confProps.minResolution=="number"&&confProps.numZoomLevels>1){base=Math.pow((confProps.maxResolution/confProps.minResolution),(1/(confProps.numZoomLevels-1)));}
for(var i=0;i<confProps.numZoomLevels;i++){var res=confProps.maxResolution/Math.pow(base,i);confProps.resolutions[i]=res;}}
-confProps.resolutions.sort(function(a,b){return(b-a);});this.resolutions=confProps.resolutions;this.maxResolution=confProps.resolutions[0];var lastIndex=confProps.resolutions.length-1;this.minResolution=confProps.resolutions[lastIndex];this.scales=[];for(var i=0;i<confProps.resolutions.length;i++){this.scales[i]=OpenLayers.Util.getScaleFromResolution(confProps.resolutions[i],confProps.units);}
+confProps.resolutions.sort(function(a,b){return(b-a);});this.resolutions=confProps.resolutions;this.maxResolution=confProps.resolutions[0];var lastIndex=confProps.resolutions.length-1;this.minResolution=confProps.resolutions[lastIndex];this.scales=[];for(var i=0,len=confProps.resolutions.length;i<len;i++){this.scales[i]=OpenLayers.Util.getScaleFromResolution(confProps.resolutions[i],confProps.units);}
this.minScale=this.scales[0];this.maxScale=this.scales[this.scales.length-1];this.numZoomLevels=confProps.numZoomLevels;},getResolution:function(){var zoom=this.map.getZoom();return this.getResolutionForZoom(zoom);},getExtent:function(){return this.map.calculateBounds();},getZoomForExtent:function(extent,closest){var viewSize=this.map.getSize();var idealResolution=Math.max(extent.getWidth()/viewSize.w,extent.getHeight()/viewSize.h);return this.getZoomForResolution(idealResolution,closest);},getDataExtent:function(){},getResolutionForZoom:function(zoom){zoom=Math.max(0,Math.min(zoom,this.resolutions.length-1));var resolution;if(this.map.fractionalZoom){var low=Math.floor(zoom);var high=Math.ceil(zoom);resolution=this.resolutions[high]+
((zoom-low)*(this.resolutions[low]-this.resolutions[high]));}else{resolution=this.resolutions[Math.round(zoom)];}
-return resolution;},getZoomForResolution:function(resolution,closest){var zoom;if(this.map.fractionalZoom){var lowZoom=0;var highZoom=this.resolutions.length-1;var highRes=this.resolutions[lowZoom];var lowRes=this.resolutions[highZoom];var res;for(var i=0;i<this.resolutions.length;++i){res=this.resolutions[i];if(res>=resolution){highRes=res;lowZoom=i;}
+return resolution;},getZoomForResolution:function(resolution,closest){var zoom;if(this.map.fractionalZoom){var lowZoom=0;var highZoom=this.resolutions.length-1;var highRes=this.resolutions[lowZoom];var lowRes=this.resolutions[highZoom];var res;for(var i=0,len=this.resolutions.length;i<len;++i){res=this.resolutions[i];if(res>=resolution){highRes=res;lowZoom=i;}
if(res<=resolution){lowRes=res;highZoom=i;break;}}
-var dRes=highRes-lowRes;if(dRes>0){zoom=lowZoom+((resolution-lowRes)/dRes);}else{zoom=lowZoom;}}else{var diff;var minDiff=Number.POSITIVE_INFINITY;for(var i=0;i<this.resolutions.length;i++){if(closest){diff=Math.abs(this.resolutions[i]-resolution);if(diff>minDiff){break;}
+var dRes=highRes-lowRes;if(dRes>0){zoom=lowZoom+((resolution-lowRes)/dRes);}else{zoom=lowZoom;}}else{var diff;var minDiff=Number.POSITIVE_INFINITY;for(var i=0,len=this.resolutions.length;i<len;i++){if(closest){diff=Math.abs(this.resolutions[i]-resolution);if(diff>minDiff){break;}
minDiff=diff;}else{if(this.resolutions[i]<resolution){break;}}}
zoom=Math.max(0,i-1);}
return zoom;},getLonLatFromViewPortPx:function(viewPortPx){var lonlat=null;if(viewPortPx!=null){var size=this.map.getSize();var center=this.map.getCenter();if(center){var res=this.map.getResolution();var delta_x=viewPortPx.x-(size.w/2);var delta_y=viewPortPx.y-(size.h/2);lonlat=new OpenLayers.LonLat(center.lon+delta_x*res,center.lat-delta_y*res);if(this.wrapDateLine){lonlat=lonlat.wrapDateLine(this.maxExtent);}}}
return lonlat;},getViewPortPxFromLonLat:function(lonlat){var px=null;if(lonlat!=null){var resolution=this.map.getResolution();var extent=this.map.getExtent();px=new OpenLayers.Pixel((1/resolution*(lonlat.lon-extent.left)),(1/resolution*(extent.top-lonlat.lat)));}
-return px;},setOpacity:function(opacity){if(opacity!=this.opacity){this.opacity=opacity;for(var i=0;i<this.div.childNodes.length;++i){var element=this.div.childNodes[i].firstChild;OpenLayers.Util.modifyDOMElement(element,null,null,null,null,null,null,opacity);}}},setZIndex:function(zIndex){this.div.style.zIndex=zIndex;},adjustBounds:function(bounds){if(this.gutter){var mapGutter=this.gutter*this.map.getResolution();bounds=new OpenLayers.Bounds(bounds.left-mapGutter,bounds.bottom-mapGutter,bounds.right+mapGutter,bounds.top+mapGutter);}
+return px;},setOpacity:function(opacity){if(opacity!=this.opacity){this.opacity=opacity;for(var i=0,len=this.div.childNodes.length;i<len;++i){var element=this.div.childNodes[i].firstChild;OpenLayers.Util.modifyDOMElement(element,null,null,null,null,null,null,opacity);}}},getZIndex:function(){return this.div.style.zIndex;},setZIndex:function(zIndex){this.div.style.zIndex=zIndex;},adjustBounds:function(bounds){if(this.gutter){var mapGutter=this.gutter*this.map.getResolution();bounds=new OpenLayers.Bounds(bounds.left-mapGutter,bounds.bottom-mapGutter,bounds.right+mapGutter,bounds.top+mapGutter);}
if(this.wrapDateLine){var wrappingOptions={'rightTolerance':this.getResolution()};bounds=bounds.wrapDateLine(this.maxExtent,wrappingOptions);}
return bounds;},CLASS_NAME:"OpenLayers.Layer"});OpenLayers.Marker.Box=OpenLayers.Class(OpenLayers.Marker,{bounds:null,div:null,initialize:function(bounds,borderColor,borderWidth){this.bounds=bounds;this.div=OpenLayers.Util.createDiv();this.div.style.overflow='hidden';this.events=new OpenLayers.Events(this,this.div,null);this.setBorder(borderColor,borderWidth);},destroy:function(){this.bounds=null;this.div=null;OpenLayers.Marker.prototype.destroy.apply(this,arguments);},setBorder:function(color,width){if(!color){color="red";}
if(!width){width=2;}
this.div.style.border=width+"px solid "+color;},draw:function(px,sz){OpenLayers.Util.modifyDOMElement(this.div,null,px,sz);return this.div;},onScreen:function(){var onScreen=false;if(this.map){var screenBounds=this.map.getExtent();onScreen=screenBounds.containsBounds(this.bounds,true,true);}
-return onScreen;},display:function(display){this.div.style.display=(display)?"":"none";},CLASS_NAME:"OpenLayers.Marker.Box"});OpenLayers.Control.DragPan=OpenLayers.Class(OpenLayers.Control,{type:OpenLayers.Control.TYPE_TOOL,panned:false,draw:function(){this.handler=new OpenLayers.Handler.Drag(this,{"move":this.panMap,"done":this.panMapDone});},panMap:function(xy){this.panned=true;this.map.pan(this.handler.last.x-xy.x,this.handler.last.y-xy.y,{dragging:this.handler.dragging,animate:false});},panMapDone:function(xy){if(this.panned){this.panMap(xy);this.panned=false;}},CLASS_NAME:"OpenLayers.Control.DragPan"});OpenLayers.Handler.Box=OpenLayers.Class(OpenLayers.Handler,{dragHandler:null,boxDivClassName:'olHandlerBoxZoomBox',initialize:function(control,callbacks,options){OpenLayers.Handler.prototype.initialize.apply(this,arguments);var callbacks={"down":this.startBox,"move":this.moveBox,"out":this.removeBox,"up":this.endBox};this.dragHandler=new OpenLayers.Handler.Drag(this,callbacks,{keyMask:this.keyMask});},setMap:function(map){OpenLayers.Handler.prototype.setMap.apply(this,arguments);if(this.dragHandler){this.dragHandler.setMap(map);}},startBox:function(xy){this.zoomBox=OpenLayers.Util.createDiv('zoomBox',this.dragHandler.start);this.zoomBox.className=this.boxDivClassName;this.zoomBox.style.zIndex=this.map.Z_INDEX_BASE["Popup"]-1;this.map.viewPortDiv.appendChild(this.zoomBox);this.map.div.style.cursor="crosshair";},moveBox:function(xy){var deltaX=Math.abs(this.dragHandler.start.x-xy.x);var deltaY=Math.abs(this.dragHandler.start.y-xy.y);this.zoomBox.style.width=Math.max(1,deltaX)+"px";this.zoomBox.style.height=Math.max(1,deltaY)+"px";if(xy.x<this.dragHandler.start.x){this.zoomBox.style.left=xy.x+"px";}
-if(xy.y<this.dragHandler.start.y){this.zoomBox.style.top=xy.y+"px";}},endBox:function(end){var result;if(Math.abs(this.dragHandler.start.x-end.x)>5||Math.abs(this.dragHandler.start.y-end.y)>5){var start=this.dragHandler.start;var top=Math.min(start.y,end.y);var bottom=Math.max(start.y,end.y);var left=Math.min(start.x,end.x);var right=Math.max(start.x,end.x);result=new OpenLayers.Bounds(left,bottom,right,top);}else{result=this.dragHandler.start.clone();}
-this.removeBox();this.map.div.style.cursor="";this.callback("done",[result]);},removeBox:function(){this.map.viewPortDiv.removeChild(this.zoomBox);this.zoomBox=null;},activate:function(){if(OpenLayers.Handler.prototype.activate.apply(this,arguments)){this.dragHandler.activate();return true;}else{return false;}},deactivate:function(){if(OpenLayers.Handler.prototype.deactivate.apply(this,arguments)){this.dragHandler.deactivate();return true;}else{return false;}},CLASS_NAME:"OpenLayers.Handler.Box"});OpenLayers.Layer.HTTPRequest=OpenLayers.Class(OpenLayers.Layer,{URL_HASH_FACTOR:(Math.sqrt(5)-1)/2,url:null,params:null,reproject:false,initialize:function(name,url,params,options){var newArguments=arguments;newArguments=[name,options];OpenLayers.Layer.prototype.initialize.apply(this,newArguments);this.url=url;this.params=OpenLayers.Util.extend({},params);},destroy:function(){this.url=null;this.params=null;OpenLayers.Layer.prototype.destroy.apply(this,arguments);},clone:function(obj){if(obj==null){obj=new OpenLayers.Layer.HTTPRequest(this.name,this.url,this.params,this.options);}
-obj=OpenLayers.Layer.prototype.clone.apply(this,[obj]);return obj;},setUrl:function(newUrl){this.url=newUrl;},mergeNewParams:function(newParams){this.params=OpenLayers.Util.extend(this.params,newParams);return this.redraw();},redraw:function(force){if(force){return this.mergeNewParams({"_olSalt":Math.random()});}else{return OpenLayers.Layer.prototype.redraw.apply(this,[]);}},selectUrl:function(paramString,urls){var product=1;for(var i=0;i<paramString.length;i++){product*=paramString.charCodeAt(i)*this.URL_HASH_FACTOR;product-=Math.floor(product);}
+return onScreen;},display:function(display){this.div.style.display=(display)?"":"none";},CLASS_NAME:"OpenLayers.Marker.Box"});OpenLayers.Control.DragPan=OpenLayers.Class(OpenLayers.Control,{type:OpenLayers.Control.TYPE_TOOL,panned:false,interval:25,draw:function(){this.handler=new OpenLayers.Handler.Drag(this,{"move":this.panMap,"done":this.panMapDone},{interval:this.interval});},panMap:function(xy){this.panned=true;this.map.pan(this.handler.last.x-xy.x,this.handler.last.y-xy.y,{dragging:this.handler.dragging,animate:false});},panMapDone:function(xy){if(this.panned){this.panMap(xy);this.panned=false;}},CLASS_NAME:"OpenLayers.Control.DragPan"});OpenLayers.State={UNKNOWN:'Unknown',INSERT:'Insert',UPDATE:'Update',DELETE:'Delete'};OpenLayers.Feature.Vector=OpenLayers.Class(OpenLayers.Feature,{fid:null,geometry:null,attributes:null,state:null,style:null,renderIntent:"default",initialize:function(geometry,attributes,style){OpenLayers.Feature.prototype.initialize.apply(this,[null,null,attributes]);this.lonlat=null;this.geometry=geometry?geometry:null;this.state=null;this.attributes={};if(attributes){this.attributes=OpenLayers.Util.extend(this.attributes,attributes);}
+this.style=style?style:null;},destroy:function(){if(this.layer){this.layer.removeFeatures(this);this.layer=null;}
+this.geometry=null;OpenLayers.Feature.prototype.destroy.apply(this,arguments);},clone:function(){return new OpenLayers.Feature.Vector(this.geometry?this.geometry.clone():null,this.attributes,this.style);},onScreen:function(boundsOnly){var onScreen=false;if(this.layer&&this.layer.map){var screenBounds=this.layer.map.getExtent();if(boundsOnly){var featureBounds=this.geometry.getBounds();onScreen=screenBounds.intersectsBounds(featureBounds);}else{var screenPoly=screenBounds.toGeometry();onScreen=screenPoly.intersects(this.geometry);}}
+return onScreen;},createMarker:function(){return null;},destroyMarker:function(){},createPopup:function(){return null;},atPoint:function(lonlat,toleranceLon,toleranceLat){var atPoint=false;if(this.geometry){atPoint=this.geometry.atPoint(lonlat,toleranceLon,toleranceLat);}
+return atPoint;},destroyPopup:function(){},move:function(location){if(!this.layer||!this.geometry.move){return;}
+var pixel;if(location.CLASS_NAME=="OpenLayers.LonLat"){pixel=this.layer.getViewPortPxFromLonLat(location);}else{pixel=location;}
+var lastPixel=this.layer.getViewPortPxFromLonLat(this.geometry.getBounds().getCenterLonLat());var res=this.layer.map.getResolution();this.geometry.move(res*(pixel.x-lastPixel.x),res*(lastPixel.y-pixel.y));this.layer.drawFeature(this);return lastPixel;},toState:function(state){if(state==OpenLayers.State.UPDATE){switch(this.state){case OpenLayers.State.UNKNOWN:case OpenLayers.State.DELETE:this.state=state;break;case OpenLayers.State.UPDATE:case OpenLayers.State.INSERT:break;}}else if(state==OpenLayers.State.INSERT){switch(this.state){case OpenLayers.State.UNKNOWN:break;default:this.state=state;break;}}else if(state==OpenLayers.State.DELETE){switch(this.state){case OpenLayers.State.INSERT:break;case OpenLayers.State.DELETE:break;case OpenLayers.State.UNKNOWN:case OpenLayers.State.UPDATE:this.state=state;break;}}else if(state==OpenLayers.State.UNKNOWN){this.state=state;}},CLASS_NAME:"OpenLayers.Feature.Vector"});OpenLayers.Feature.Vector.style={'default':{fillColor:"#ee9900",fillOpacity:0.4,hoverFillColor:"white",hoverFillOpacity:0.8,strokeColor:"#ee9900",strokeOpacity:1,strokeWidth:1,strokeLinecap:"round",strokeDashstyle:"solid",hoverStrokeColor:"red",hoverStrokeOpacity:1,hoverStrokeWidth:0.2,pointRadius:6,hoverPointRadius:1,hoverPointUnit:"%",pointerEvents:"visiblePainted",cursor:"inherit"},'select':{fillColor:"blue",fillOpacity:0.4,hoverFillColor:"white",hoverFillOpacity:0.8,strokeColor:"blue",strokeOpacity:1,strokeWidth:2,strokeLinecap:"round",strokeDashstyle:"solid",hoverStrokeColor:"red",hoverStrokeOpacity:1,hoverStrokeWidth:0.2,pointRadius:6,hoverPointRadius:1,hoverPointUnit:"%",pointerEvents:"visiblePainted",cursor:"pointer"},'temporary':{fillColor:"yellow",fillOpacity:0.2,hoverFillColor:"white",hoverFillOpacity:0.8,strokeColor:"yellow",strokeOpacity:1,strokeLinecap:"round",strokeWidth:4,strokeDashstyle:"solid",hoverStrokeColor:"red",hoverStrokeOpacity:1,hoverStrokeWidth:0.2,pointRadius:6,hoverPointRadius:1,hoverPointUnit:"%",pointerEvents:"visiblePainted",cursor:"inherit"}};OpenLayers.Handler.Box=OpenLayers.Class(OpenLayers.Handler,{dragHandler:null,boxDivClassName:'olHandlerBoxZoomBox',boxCharacteristics:null,initialize:function(control,callbacks,options){OpenLayers.Handler.prototype.initialize.apply(this,arguments);var callbacks={"down":this.startBox,"move":this.moveBox,"out":this.removeBox,"up":this.endBox};this.dragHandler=new OpenLayers.Handler.Drag(this,callbacks,{keyMask:this.keyMask});},setMap:function(map){OpenLayers.Handler.prototype.setMap.apply(this,arguments);if(this.dragHandler){this.dragHandler.setMap(map);}},startBox:function(xy){this.zoomBox=OpenLayers.Util.createDiv('zoomBox',this.dragHandler.start);this.zoomBox.className=this.boxDivClassName;this.zoomBox.style.zIndex=this.map.Z_INDEX_BASE["Popup"]-1;this.map.viewPortDiv.appendChild(this.zoomBox);this.map.div.style.cursor="crosshair";},moveBox:function(xy){var startX=this.dragHandler.start.x;var startY=this.dragHandler.start.y;var deltaX=Math.abs(startX-xy.x);var deltaY=Math.abs(startY-xy.y);this.zoomBox.style.width=Math.max(1,deltaX)+"px";this.zoomBox.style.height=Math.max(1,deltaY)+"px";this.zoomBox.style.left=xy.x<startX?xy.x+"px":startX+"px";this.zoomBox.style.top=xy.y<startY?xy.y+"px":startY+"px";var box=this.getBoxCharacteristics(deltaX,deltaY);if(box.newBoxModel){if(xy.x>startX){this.zoomBox.style.width=Math.max(1,deltaX-box.xOffset)+"px";}
+if(xy.y>startY){this.zoomBox.style.height=Math.max(1,deltaY-box.yOffset)+"px";}}},endBox:function(end){var result;if(Math.abs(this.dragHandler.start.x-end.x)>5||Math.abs(this.dragHandler.start.y-end.y)>5){var start=this.dragHandler.start;var top=Math.min(start.y,end.y);var bottom=Math.max(start.y,end.y);var left=Math.min(start.x,end.x);var right=Math.max(start.x,end.x);result=new OpenLayers.Bounds(left,bottom,right,top);}else{result=this.dragHandler.start.clone();}
+this.removeBox();this.map.div.style.cursor="";this.callback("done",[result]);},removeBox:function(){this.map.viewPortDiv.removeChild(this.zoomBox);this.zoomBox=null;this.boxCharacteristics=null;},activate:function(){if(OpenLayers.Handler.prototype.activate.apply(this,arguments)){this.dragHandler.activate();return true;}else{return false;}},deactivate:function(){if(OpenLayers.Handler.prototype.deactivate.apply(this,arguments)){this.dragHandler.deactivate();return true;}else{return false;}},getBoxCharacteristics:function(dx,dy){if(!this.boxCharacteristics){var xOffset=parseInt(OpenLayers.Element.getStyle(this.zoomBox,"border-left-width"))+parseInt(OpenLayers.Element.getStyle(this.zoomBox,"border-right-width"))+1;var yOffset=parseInt(OpenLayers.Element.getStyle(this.zoomBox,"border-top-width"))+parseInt(OpenLayers.Element.getStyle(this.zoomBox,"border-bottom-width"))+1;var newBoxModel=OpenLayers.Util.getBrowserName()=="msie"?document.compatMode!="BackCompat":true;this.boxCharacteristics={xOffset:xOffset,yOffset:yOffset,newBoxModel:newBoxModel}}
+return this.boxCharacteristics;},CLASS_NAME:"OpenLayers.Handler.Box"});OpenLayers.Layer.HTTPRequest=OpenLayers.Class(OpenLayers.Layer,{URL_HASH_FACTOR:(Math.sqrt(5)-1)/2,url:null,params:null,reproject:false,initialize:function(name,url,params,options){var newArguments=arguments;newArguments=[name,options];OpenLayers.Layer.prototype.initialize.apply(this,newArguments);this.url=url;this.params=OpenLayers.Util.extend({},params);},destroy:function(){this.url=null;this.params=null;OpenLayers.Layer.prototype.destroy.apply(this,arguments);},clone:function(obj){if(obj==null){obj=new OpenLayers.Layer.HTTPRequest(this.name,this.url,this.params,this.options);}
+obj=OpenLayers.Layer.prototype.clone.apply(this,[obj]);return obj;},setUrl:function(newUrl){this.url=newUrl;},mergeNewParams:function(newParams){this.params=OpenLayers.Util.extend(this.params,newParams);return this.redraw();},redraw:function(force){if(force){return this.mergeNewParams({"_olSalt":Math.random()});}else{return OpenLayers.Layer.prototype.redraw.apply(this,[]);}},selectUrl:function(paramString,urls){var product=1;for(var i=0,len=paramString.length;i<len;i++){product*=paramString.charCodeAt(i)*this.URL_HASH_FACTOR;product-=Math.floor(product);}
return urls[Math.floor(product*urls.length)];},getFullRequestString:function(newParams,altUrl){var url=altUrl||this.url;var allParams=OpenLayers.Util.extend({},this.params);allParams=OpenLayers.Util.extend(allParams,newParams);var paramsString=OpenLayers.Util.getParameterString(allParams);if(url instanceof Array){url=this.selectUrl(paramsString,url);}
var urlParams=OpenLayers.Util.upperCaseObject(OpenLayers.Util.getParameters(url));for(var key in allParams){if(key.toUpperCase()in urlParams){delete allParams[key];}}
paramsString=OpenLayers.Util.getParameterString(allParams);var requestString=url;if(paramsString!=""){var lastServerChar=url.charAt(url.length-1);if((lastServerChar=="&")||(lastServerChar=="?")){requestString+=paramsString;}else{if(url.indexOf('?')==-1){requestString+='?'+paramsString;}else{requestString+='&'+paramsString;}}}
-return requestString;},CLASS_NAME:"OpenLayers.Layer.HTTPRequest"});OpenLayers.Control.ZoomBox=OpenLayers.Class(OpenLayers.Control,{type:OpenLayers.Control.TYPE_TOOL,out:false,draw:function(){this.handler=new OpenLayers.Handler.Box(this,{done:this.zoomBox},{keyMask:this.keyMask});},zoomBox:function(position){if(position instanceof OpenLayers.Bounds){if(!this.out){var minXY=this.map.getLonLatFromPixel(new OpenLayers.Pixel(position.left,position.bottom));var maxXY=this.map.getLonLatFromPixel(new OpenLayers.Pixel(position.right,position.top));var bounds=new OpenLayers.Bounds(minXY.lon,minXY.lat,maxXY.lon,maxXY.lat);}else{var pixWidth=Math.abs(position.right-position.left);var pixHeight=Math.abs(position.top-position.bottom);var zoomFactor=Math.min((this.map.size.h/pixHeight),(this.map.size.w/pixWidth));var extent=this.map.getExtent();var center=this.map.getLonLatFromPixel(position.getCenterPixel());var xmin=center.lon-(extent.getWidth()/2)*zoomFactor;var xmax=center.lon+(extent.getWidth()/2)*zoomFactor;var ymin=center.lat-(extent.getHeight()/2)*zoomFactor;var ymax=center.lat+(extent.getHeight()/2)*zoomFactor;var bounds=new OpenLayers.Bounds(xmin,ymin,xmax,ymax);}
-this.map.zoomToExtent(bounds);}else{if(!this.out){this.map.setCenter(this.map.getLonLatFromPixel(position),this.map.getZoom()+1);}else{this.map.setCenter(this.map.getLonLatFromPixel(position),this.map.getZoom()-1);}}},CLASS_NAME:"OpenLayers.Control.ZoomBox"});OpenLayers.Layer.Grid=OpenLayers.Class(OpenLayers.Layer.HTTPRequest,{tileSize:null,grid:null,singleTile:false,ratio:1.5,buffer:2,numLoadingTiles:0,initialize:function(name,url,params,options){OpenLayers.Layer.HTTPRequest.prototype.initialize.apply(this,arguments);this.events.addEventType("tileloaded");this.grid=[];},destroy:function(){this.clearGrid();this.grid=null;this.tileSize=null;OpenLayers.Layer.HTTPRequest.prototype.destroy.apply(this,arguments);},clearGrid:function(){if(this.grid){for(var iRow=0;iRow<this.grid.length;iRow++){var row=this.grid[iRow];for(var iCol=0;iCol<row.length;iCol++){var tile=row[iCol];this.removeTileMonitoringHooks(tile);tile.destroy();}}
+return requestString;},CLASS_NAME:"OpenLayers.Layer.HTTPRequest"});OpenLayers.Control.DrawFeature=OpenLayers.Class(OpenLayers.Control,{layer:null,callbacks:null,EVENT_TYPES:["featureadded"],featureAdded:function(){},handlerOptions:null,initialize:function(layer,handler,options){this.EVENT_TYPES=OpenLayers.Control.DrawFeature.prototype.EVENT_TYPES.concat(OpenLayers.Control.prototype.EVENT_TYPES);OpenLayers.Control.prototype.initialize.apply(this,[options]);this.callbacks=OpenLayers.Util.extend({done:this.drawFeature},this.callbacks);this.layer=layer;this.handler=new handler(this,this.callbacks,this.handlerOptions);},drawFeature:function(geometry){var feature=new OpenLayers.Feature.Vector(geometry);this.layer.addFeatures([feature]);this.featureAdded(feature);this.events.triggerEvent("featureadded",{feature:feature});},CLASS_NAME:"OpenLayers.Control.DrawFeature"});OpenLayers.Control.ZoomBox=OpenLayers.Class(OpenLayers.Control,{type:OpenLayers.Control.TYPE_TOOL,out:false,draw:function(){this.handler=new OpenLayers.Handler.Box(this,{done:this.zoomBox},{keyMask:this.keyMask});},zoomBox:function(position){if(position instanceof OpenLayers.Bounds){if(!this.out){var minXY=this.map.getLonLatFromPixel(new OpenLayers.Pixel(position.left,position.bottom));var maxXY=this.map.getLonLatFromPixel(new OpenLayers.Pixel(position.right,position.top));var bounds=new OpenLayers.Bounds(minXY.lon,minXY.lat,maxXY.lon,maxXY.lat);}else{var pixWidth=Math.abs(position.right-position.left);var pixHeight=Math.abs(position.top-position.bottom);var zoomFactor=Math.min((this.map.size.h/pixHeight),(this.map.size.w/pixWidth));var extent=this.map.getExtent();var center=this.map.getLonLatFromPixel(position.getCenterPixel());var xmin=center.lon-(extent.getWidth()/2)*zoomFactor;var xmax=center.lon+(extent.getWidth()/2)*zoomFactor;var ymin=center.lat-(extent.getHeight()/2)*zoomFactor;var ymax=center.lat+(extent.getHeight()/2)*zoomFactor;var bounds=new OpenLayers.Bounds(xmin,ymin,xmax,ymax);}
+this.map.zoomToExtent(bounds);}else{if(!this.out){this.map.setCenter(this.map.getLonLatFromPixel(position),this.map.getZoom()+1);}else{this.map.setCenter(this.map.getLonLatFromPixel(position),this.map.getZoom()-1);}}},CLASS_NAME:"OpenLayers.Control.ZoomBox"});OpenLayers.Format.WKT=OpenLayers.Class(OpenLayers.Format,{initialize:function(options){this.regExes={'typeStr':/^\s*(\w+)\s*\(\s*(.*)\s*\)\s*$/,'spaces':/\s+/,'parenComma':/\)\s*,\s*\(/,'doubleParenComma':/\)\s*\)\s*,\s*\(\s*\(/,'trimParens':/^\s*\(?(.*?)\)?\s*$/};OpenLayers.Format.prototype.initialize.apply(this,[options]);},read:function(wkt){var features,type,str;var matches=this.regExes.typeStr.exec(wkt);if(matches){type=matches[1].toLowerCase();str=matches[2];if(this.parse[type]){features=this.parse[type].apply(this,[str]);}
+if(this.internalProjection&&this.externalProjection){if(features&&features.CLASS_NAME=="OpenLayers.Feature.Vector"){features.geometry.transform(this.externalProjection,this.internalProjection);}else if(features&&type!="geometrycollection"&&typeof features=="object"){for(var i=0,len=features.length;i<len;i++){var component=features[i];component.geometry.transform(this.externalProjection,this.internalProjection);}}}}
+return features;},write:function(features){var collection,geometry,type,data,isCollection;if(features.constructor==Array){collection=features;isCollection=true;}else{collection=[features];isCollection=false;}
+var pieces=[];if(isCollection){pieces.push('GEOMETRYCOLLECTION(');}
+for(var i=0,len=collection.length;i<len;++i){if(isCollection&&i>0){pieces.push(',');}
+geometry=collection[i].geometry;type=geometry.CLASS_NAME.split('.')[2].toLowerCase();if(!this.extract[type]){return null;}
+if(this.internalProjection&&this.externalProjection){geometry=geometry.clone();geometry.transform(this.internalProjection,this.externalProjection);}
+data=this.extract[type].apply(this,[geometry]);pieces.push(type.toUpperCase()+'('+data+')');}
+if(isCollection){pieces.push(')');}
+return pieces.join('');},extract:{'point':function(point){return point.x+' '+point.y;},'multipoint':function(multipoint){var array=[];for(var i=0,len=multipoint.components.length;i<len;++i){array.push(this.extract.point.apply(this,[multipoint.components[i]]));}
+return array.join(',');},'linestring':function(linestring){var array=[];for(var i=0,len=linestring.components.length;i<len;++i){array.push(this.extract.point.apply(this,[linestring.components[i]]));}
+return array.join(',');},'multilinestring':function(multilinestring){var array=[];for(var i=0,len=multilinestring.components.length;i<len;++i){array.push('('+
+this.extract.linestring.apply(this,[multilinestring.components[i]])+')');}
+return array.join(',');},'polygon':function(polygon){var array=[];for(var i=0,len=polygon.components.length;i<len;++i){array.push('('+
+this.extract.linestring.apply(this,[polygon.components[i]])+')');}
+return array.join(',');},'multipolygon':function(multipolygon){var array=[];for(var i=0,len=multipolygon.components.length;i<len;++i){array.push('('+
+this.extract.polygon.apply(this,[multipolygon.components[i]])+')');}
+return array.join(',');}},parse:{'point':function(str){var coords=OpenLayers.String.trim(str).split(this.regExes.spaces);return new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(coords[0],coords[1]));},'multipoint':function(str){var points=OpenLayers.String.trim(str).split(',');var components=[];for(var i=0,len=points.length;i<len;++i){components.push(this.parse.point.apply(this,[points[i]]).geometry);}
+return new OpenLayers.Feature.Vector(new OpenLayers.Geometry.MultiPoint(components));},'linestring':function(str){var points=OpenLayers.String.trim(str).split(',');var components=[];for(var i=0,len=points.length;i<len;++i){components.push(this.parse.point.apply(this,[points[i]]).geometry);}
+return new OpenLayers.Feature.Vector(new OpenLayers.Geometry.LineString(components));},'multilinestring':function(str){var line;var lines=OpenLayers.String.trim(str).split(this.regExes.parenComma);var components=[];for(var i=0,len=lines.length;i<len;++i){line=lines[i].replace(this.regExes.trimParens,'$1');components.push(this.parse.linestring.apply(this,[line]).geometry);}
+return new OpenLayers.Feature.Vector(new OpenLayers.Geometry.MultiLineString(components));},'polygon':function(str){var ring,linestring,linearring;var rings=OpenLayers.String.trim(str).split(this.regExes.parenComma);var components=[];for(var i=0,len=rings.length;i<len;++i){ring=rings[i].replace(this.regExes.trimParens,'$1');linestring=this.parse.linestring.apply(this,[ring]).geometry;linearring=new OpenLayers.Geometry.LinearRing(linestring.components);components.push(linearring);}
+return new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Polygon(components));},'multipolygon':function(str){var polygon;var polygons=OpenLayers.String.trim(str).split(this.regExes.doubleParenComma);var components=[];for(var i=0,len=polygons.length;i<len;++i){polygon=polygons[i].replace(this.regExes.trimParens,'$1');components.push(this.parse.polygon.apply(this,[polygon]).geometry);}
+return new OpenLayers.Feature.Vector(new OpenLayers.Geometry.MultiPolygon(components));},'geometrycollection':function(str){str=str.replace(/,\s*([A-Za-z])/g,'|$1');var wktArray=OpenLayers.String.trim(str).split('|');var components=[];for(var i=0,len=wktArray.length;i<len;++i){components.push(OpenLayers.Format.WKT.prototype.read.apply(this,[wktArray[i]]));}
+return components;}},CLASS_NAME:"OpenLayers.Format.WKT"});OpenLayers.Layer.Grid=OpenLayers.Class(OpenLayers.Layer.HTTPRequest,{tileSize:null,grid:null,singleTile:false,ratio:1.5,buffer:2,numLoadingTiles:0,initialize:function(name,url,params,options){OpenLayers.Layer.HTTPRequest.prototype.initialize.apply(this,arguments);this.events.addEventType("tileloaded");this.grid=[];},destroy:function(){this.clearGrid();this.grid=null;this.tileSize=null;OpenLayers.Layer.HTTPRequest.prototype.destroy.apply(this,arguments);},clearGrid:function(){if(this.grid){for(var iRow=0,len=this.grid.length;iRow<len;iRow++){var row=this.grid[iRow];for(var iCol=0,clen=row.length;iCol<clen;iCol++){var tile=row[iCol];this.removeTileMonitoringHooks(tile);tile.destroy();}}
this.grid=[];}},clone:function(obj){if(obj==null){obj=new OpenLayers.Layer.Grid(this.name,this.url,this.params,this.options);}
obj=OpenLayers.Layer.HTTPRequest.prototype.clone.apply(this,[obj]);if(this.tileSize!=null){obj.tileSize=this.tileSize.clone();}
obj.grid=[];return obj;},moveTo:function(bounds,zoomChanged,dragging){OpenLayers.Layer.HTTPRequest.prototype.moveTo.apply(this,arguments);bounds=bounds||this.map.getExtent();if(bounds!=null){var forceReTile=!this.grid.length||zoomChanged;var tilesBounds=this.getTilesBounds();if(this.singleTile){if(forceReTile||(!dragging&&!tilesBounds.containsBounds(bounds))){this.initSingleTile(bounds);}}else{if(forceReTile||!tilesBounds.containsBounds(bounds,true)){this.initGriddedTiles(bounds);}else{this.moveGriddedTiles(bounds);}}}},setTileSize:function(size){if(this.singleTile){size=this.map.getSize().clone();size.h=parseInt(size.h*this.ratio);size.w=parseInt(size.w*this.ratio);}
@@ -496,34 +629,138 @@
var tile=this.grid[0][0];if(!tile){tile=this.addTile(tileBounds,px);this.addTileMonitoringHooks(tile);tile.draw();this.grid[0][0]=tile;}else{tile.moveTo(tileBounds,px);}
this.removeExcessTiles(1,1);},calculateGridLayout:function(bounds,extent,resolution){var tilelon=resolution*this.tileSize.w;var tilelat=resolution*this.tileSize.h;var offsetlon=bounds.left-extent.left;var tilecol=Math.floor(offsetlon/tilelon)-this.buffer;var tilecolremain=offsetlon/tilelon-tilecol;var tileoffsetx=-tilecolremain*this.tileSize.w;var tileoffsetlon=extent.left+tilecol*tilelon;var offsetlat=bounds.top-(extent.bottom+tilelat);var tilerow=Math.ceil(offsetlat/tilelat)+this.buffer;var tilerowremain=tilerow-offsetlat/tilelat;var tileoffsety=-tilerowremain*this.tileSize.h;var tileoffsetlat=extent.bottom+tilerow*tilelat;return{tilelon:tilelon,tilelat:tilelat,tileoffsetlon:tileoffsetlon,tileoffsetlat:tileoffsetlat,tileoffsetx:tileoffsetx,tileoffsety:tileoffsety};},initGriddedTiles:function(bounds){var viewSize=this.map.getSize();var minRows=Math.ceil(viewSize.h/this.tileSize.h)+
Math.max(1,2*this.buffer);var minCols=Math.ceil(viewSize.w/this.tileSize.w)+
-Math.max(1,2*this.buffer);var extent=this.map.getMaxExtent();var resolution=this.map.getResolution();var tileLayout=this.calculateGridLayout(bounds,extent,resolution);var tileoffsetx=Math.round(tileLayout.tileoffsetx);var tileoffsety=Math.round(tileLayout.tileoffsety);var tileoffsetlon=tileLayout.tileoffsetlon;var tileoffsetlat=tileLayout.tileoffsetlat;var tilelon=tileLayout.tilelon;var tilelat=tileLayout.tilelat;this.origin=new OpenLayers.Pixel(tileoffsetx,tileoffsety);var startX=tileoffsetx;var startLon=tileoffsetlon;var rowidx=0;var layerContainerDivLeft=parseInt(this.map.layerContainerDiv.style.left);var layerContainerDivTop=parseInt(this.map.layerContainerDiv.style.top);do{var row=this.grid[rowidx++];if(!row){row=[];this.grid.push(row);}
+Math.max(1,2*this.buffer);var extent=this.maxExtent;var resolution=this.map.getResolution();var tileLayout=this.calculateGridLayout(bounds,extent,resolution);var tileoffsetx=Math.round(tileLayout.tileoffsetx);var tileoffsety=Math.round(tileLayout.tileoffsety);var tileoffsetlon=tileLayout.tileoffsetlon;var tileoffsetlat=tileLayout.tileoffsetlat;var tilelon=tileLayout.tilelon;var tilelat=tileLayout.tilelat;this.origin=new OpenLayers.Pixel(tileoffsetx,tileoffsety);var startX=tileoffsetx;var startLon=tileoffsetlon;var rowidx=0;var layerContainerDivLeft=parseInt(this.map.layerContainerDiv.style.left);var layerContainerDivTop=parseInt(this.map.layerContainerDiv.style.top);do{var row=this.grid[rowidx++];if(!row){row=[];this.grid.push(row);}
tileoffsetlon=startLon;tileoffsetx=startX;var colidx=0;do{var tileBounds=new OpenLayers.Bounds(tileoffsetlon,tileoffsetlat,tileoffsetlon+tilelon,tileoffsetlat+tilelat);var x=tileoffsetx;x-=layerContainerDivLeft;var y=tileoffsety;y-=layerContainerDivTop;var px=new OpenLayers.Pixel(x,y);var tile=row[colidx++];if(!tile){tile=this.addTile(tileBounds,px);this.addTileMonitoringHooks(tile);row.push(tile);}else{tile.moveTo(tileBounds,px,false);}
tileoffsetlon+=tilelon;tileoffsetx+=this.tileSize.w;}while((tileoffsetlon<=bounds.right+tilelon*this.buffer)||colidx<minCols)
tileoffsetlat-=tilelat;tileoffsety+=this.tileSize.h;}while((tileoffsetlat>=bounds.bottom-tilelat*this.buffer)||rowidx<minRows)
this.removeExcessTiles(rowidx,colidx);this.spiralTileLoad();},spiralTileLoad:function(){var tileQueue=[];var directions=["right","down","left","up"];var iRow=0;var iCell=-1;var direction=OpenLayers.Util.indexOf(directions,"right");var directionsTried=0;while(directionsTried<directions.length){var testRow=iRow;var testCell=iCell;switch(directions[direction]){case"right":testCell++;break;case"down":testRow++;break;case"left":testCell--;break;case"up":testRow--;break;}
var tile=null;if((testRow<this.grid.length)&&(testRow>=0)&&(testCell<this.grid[0].length)&&(testCell>=0)){tile=this.grid[testRow][testCell];}
if((tile!=null)&&(!tile.queued)){tileQueue.unshift(tile);tile.queued=true;directionsTried=0;iRow=testRow;iCell=testCell;}else{direction=(direction+1)%4;directionsTried++;}}
-for(var i=0;i<tileQueue.length;i++){var tile=tileQueue[i];tile.draw();tile.queued=false;}},addTile:function(bounds,position){},addTileMonitoringHooks:function(tile){tile.onLoadStart=function(){if(this.numLoadingTiles==0){this.events.triggerEvent("loadstart");}
-this.numLoadingTiles++;};tile.events.register("loadstart",this,tile.onLoadStart);tile.onLoadEnd=function(){this.numLoadingTiles--;this.events.triggerEvent("tileloaded");if(this.numLoadingTiles==0){this.events.triggerEvent("loadend");}};tile.events.register("loadend",this,tile.onLoadEnd);tile.events.register("unload",this,tile.onLoadEnd);},removeTileMonitoringHooks:function(tile){tile.unload();tile.events.un({"loadstart":tile.onLoadStart,"loadend":tile.onLoadEnd,"unload":tile.onLoadEnd,scope:this});},moveGriddedTiles:function(bounds){var buffer=this.buffer||1;while(true){var tlLayer=this.grid[0][0].position;var tlViewPort=this.map.getViewPortPxFromLayerPx(tlLayer);if(tlViewPort.x>-this.tileSize.w*(buffer-1)){this.shiftColumn(true);}else if(tlViewPort.x<-this.tileSize.w*buffer){this.shiftColumn(false);}else if(tlViewPort.y>-this.tileSize.h*(buffer-1)){this.shiftRow(true);}else if(tlViewPort.y<-this.tileSize.h*buffer){this.shiftRow(false);}else{break;}};},shiftRow:function(prepend){var modelRowIndex=(prepend)?0:(this.grid.length-1);var grid=this.grid;var modelRow=grid[modelRowIndex];var resolution=this.map.getResolution();var deltaY=(prepend)?-this.tileSize.h:this.tileSize.h;var deltaLat=resolution*-deltaY;var row=(prepend)?grid.pop():grid.shift();for(var i=0;i<modelRow.length;i++){var modelTile=modelRow[i];var bounds=modelTile.bounds.clone();var position=modelTile.position.clone();bounds.bottom=bounds.bottom+deltaLat;bounds.top=bounds.top+deltaLat;position.y=position.y+deltaY;row[i].moveTo(bounds,position);}
-if(prepend){grid.unshift(row);}else{grid.push(row);}},shiftColumn:function(prepend){var deltaX=(prepend)?-this.tileSize.w:this.tileSize.w;var resolution=this.map.getResolution();var deltaLon=resolution*deltaX;for(var i=0;i<this.grid.length;i++){var row=this.grid[i];var modelTileIndex=(prepend)?0:(row.length-1);var modelTile=row[modelTileIndex];var bounds=modelTile.bounds.clone();var position=modelTile.position.clone();bounds.left=bounds.left+deltaLon;bounds.right=bounds.right+deltaLon;position.x=position.x+deltaX;var tile=prepend?this.grid[i].pop():this.grid[i].shift();tile.moveTo(bounds,position);if(prepend){row.unshift(tile);}else{row.push(tile);}}},removeExcessTiles:function(rows,columns){while(this.grid.length>rows){var row=this.grid.pop();for(var i=0,l=row.length;i<l;i++){var tile=row[i];this.removeTileMonitoringHooks(tile);tile.destroy();}}
-while(this.grid[0].length>columns){for(var i=0,l=this.grid.length;i<l;i++){var row=this.grid[i];var tile=row.pop();this.removeTileMonitoringHooks(tile);tile.destroy();}}},onMapResize:function(){if(this.singleTile){this.clearGrid();this.setTileSize();}},getTileBounds:function(viewPortPx){var maxExtent=this.map.getMaxExtent();var resolution=this.getResolution();var tileMapWidth=resolution*this.tileSize.w;var tileMapHeight=resolution*this.tileSize.h;var mapPoint=this.getLonLatFromViewPortPx(viewPortPx);var tileLeft=maxExtent.left+(tileMapWidth*Math.floor((mapPoint.lon-
+for(var i=0,len=tileQueue.length;i<len;i++){var tile=tileQueue[i];tile.draw();tile.queued=false;}},addTile:function(bounds,position){},addTileMonitoringHooks:function(tile){tile.onLoadStart=function(){if(this.numLoadingTiles==0){this.events.triggerEvent("loadstart");}
+this.numLoadingTiles++;};tile.events.register("loadstart",this,tile.onLoadStart);tile.onLoadEnd=function(){this.numLoadingTiles--;this.events.triggerEvent("tileloaded");if(this.numLoadingTiles==0){this.events.triggerEvent("loadend");}};tile.events.register("loadend",this,tile.onLoadEnd);tile.events.register("unload",this,tile.onLoadEnd);},removeTileMonitoringHooks:function(tile){tile.unload();tile.events.un({"loadstart":tile.onLoadStart,"loadend":tile.onLoadEnd,"unload":tile.onLoadEnd,scope:this});},moveGriddedTiles:function(bounds){var buffer=this.buffer||1;while(true){var tlLayer=this.grid[0][0].position;var tlViewPort=this.map.getViewPortPxFromLayerPx(tlLayer);if(tlViewPort.x>-this.tileSize.w*(buffer-1)){this.shiftColumn(true);}else if(tlViewPort.x<-this.tileSize.w*buffer){this.shiftColumn(false);}else if(tlViewPort.y>-this.tileSize.h*(buffer-1)){this.shiftRow(true);}else if(tlViewPort.y<-this.tileSize.h*buffer){this.shiftRow(false);}else{break;}};},shiftRow:function(prepend){var modelRowIndex=(prepend)?0:(this.grid.length-1);var grid=this.grid;var modelRow=grid[modelRowIndex];var resolution=this.map.getResolution();var deltaY=(prepend)?-this.tileSize.h:this.tileSize.h;var deltaLat=resolution*-deltaY;var row=(prepend)?grid.pop():grid.shift();for(var i=0,len=modelRow.length;i<len;i++){var modelTile=modelRow[i];var bounds=modelTile.bounds.clone();var position=modelTile.position.clone();bounds.bottom=bounds.bottom+deltaLat;bounds.top=bounds.top+deltaLat;position.y=position.y+deltaY;row[i].moveTo(bounds,position);}
+if(prepend){grid.unshift(row);}else{grid.push(row);}},shiftColumn:function(prepend){var deltaX=(prepend)?-this.tileSize.w:this.tileSize.w;var resolution=this.map.getResolution();var deltaLon=resolution*deltaX;for(var i=0,len=this.grid.length;i<len;i++){var row=this.grid[i];var modelTileIndex=(prepend)?0:(row.length-1);var modelTile=row[modelTileIndex];var bounds=modelTile.bounds.clone();var position=modelTile.position.clone();bounds.left=bounds.left+deltaLon;bounds.right=bounds.right+deltaLon;position.x=position.x+deltaX;var tile=prepend?this.grid[i].pop():this.grid[i].shift();tile.moveTo(bounds,position);if(prepend){row.unshift(tile);}else{row.push(tile);}}},removeExcessTiles:function(rows,columns){while(this.grid.length>rows){var row=this.grid.pop();for(var i=0,l=row.length;i<l;i++){var tile=row[i];this.removeTileMonitoringHooks(tile);tile.destroy();}}
+while(this.grid[0].length>columns){for(var i=0,l=this.grid.length;i<l;i++){var row=this.grid[i];var tile=row.pop();this.removeTileMonitoringHooks(tile);tile.destroy();}}},onMapResize:function(){if(this.singleTile){this.clearGrid();this.setTileSize();}},getTileBounds:function(viewPortPx){var maxExtent=this.maxExtent;var resolution=this.getResolution();var tileMapWidth=resolution*this.tileSize.w;var tileMapHeight=resolution*this.tileSize.h;var mapPoint=this.getLonLatFromViewPortPx(viewPortPx);var tileLeft=maxExtent.left+(tileMapWidth*Math.floor((mapPoint.lon-
maxExtent.left)/tileMapWidth));var tileBottom=maxExtent.bottom+(tileMapHeight*Math.floor((mapPoint.lat-
-maxExtent.bottom)/tileMapHeight));return new OpenLayers.Bounds(tileLeft,tileBottom,tileLeft+tileMapWidth,tileBottom+tileMapHeight);},CLASS_NAME:"OpenLayers.Layer.Grid"});OpenLayers.Control.Navigation=OpenLayers.Class(OpenLayers.Control,{dragPan:null,zoomBox:null,zoomWheelEnabled:true,initialize:function(options){this.handlers={};OpenLayers.Control.prototype.initialize.apply(this,arguments);},destroy:function(){this.deactivate();if(this.dragPan){this.dragPan.destroy();}
+maxExtent.bottom)/tileMapHeight));return new OpenLayers.Bounds(tileLeft,tileBottom,tileLeft+tileMapWidth,tileBottom+tileMapHeight);},CLASS_NAME:"OpenLayers.Layer.Grid"});OpenLayers.Style=OpenLayers.Class({name:null,title:null,description:null,layerName:null,isDefault:false,rules:null,context:null,defaultStyle:null,propertyStyles:null,initialize:function(style,options){this.rules=[];this.setDefaultStyle(style||OpenLayers.Feature.Vector.style["default"]);OpenLayers.Util.extend(this,options);},destroy:function(){for(var i=0,len=this.rules.length;i<len;i++){this.rules[i].destroy();this.rules[i]=null;}
+this.rules=null;this.defaultStyle=null;},createSymbolizer:function(feature){var style=this.createLiterals(OpenLayers.Util.extend({},this.defaultStyle),feature);var rules=this.rules;var rule,context;var elseRules=[];var appliedRules=false;for(var i=0,len=rules.length;i<len;i++){rule=rules[i];var applies=rule.evaluate(feature);if(applies){if(rule instanceof OpenLayers.Rule&&rule.elseFilter){elseRules.push(rule);}else{appliedRules=true;this.applySymbolizer(rule,style,feature);}}}
+if(appliedRules==false&&elseRules.length>0){appliedRules=true;for(var i=0,len=elseRules.length;i<len;i++){this.applySymbolizer(elseRules[i],style,feature);}}
+if(rules.length>0&&appliedRules==false){style.display="none";}else{style.display="";}
+return style;},applySymbolizer:function(rule,style,feature){var symbolizerPrefix=feature.geometry?this.getSymbolizerPrefix(feature.geometry):OpenLayers.Style.SYMBOLIZER_PREFIXES[0];var symbolizer=rule.symbolizer[symbolizerPrefix]||rule.symbolizer;return this.createLiterals(OpenLayers.Util.extend(style,symbolizer),feature);},createLiterals:function(style,feature){var context=this.context||feature.attributes||feature.data;for(var i in this.propertyStyles){style[i]=OpenLayers.Style.createLiteral(style[i],context,feature);}
+return style;},findPropertyStyles:function(){var propertyStyles={};var style=this.defaultStyle;this.addPropertyStyles(propertyStyles,style);var rules=this.rules;var symbolizer,value;for(var i=0,len=rules.length;i<len;i++){var symbolizer=rules[i].symbolizer;for(var key in symbolizer){value=symbolizer[key];if(typeof value=="object"){this.addPropertyStyles(propertyStyles,value);}else{this.addPropertyStyles(propertyStyles,symbolizer);break;}}}
+return propertyStyles;},addPropertyStyles:function(propertyStyles,symbolizer){var property;for(var key in symbolizer){property=symbolizer[key];if(typeof property=="string"&&property.match(/\$\{\w+\}/)){propertyStyles[key]=true;}}
+return propertyStyles;},addRules:function(rules){this.rules=this.rules.concat(rules);this.propertyStyles=this.findPropertyStyles();},setDefaultStyle:function(style){this.defaultStyle=style;this.propertyStyles=this.findPropertyStyles();},getSymbolizerPrefix:function(geometry){var prefixes=OpenLayers.Style.SYMBOLIZER_PREFIXES;for(var i=0,len=prefixes.length;i<len;i++){if(geometry.CLASS_NAME.indexOf(prefixes[i])!=-1){return prefixes[i];}}},CLASS_NAME:"OpenLayers.Style"});OpenLayers.Style.createLiteral=function(value,context,feature){if(typeof value=="string"&&value.indexOf("${")!=-1){value=OpenLayers.String.format(value,context,[feature]);value=(isNaN(value)||!value)?value:parseFloat(value);}
+return value;};OpenLayers.Style.SYMBOLIZER_PREFIXES=['Point','Line','Polygon','Text'];OpenLayers.Control.Navigation=OpenLayers.Class(OpenLayers.Control,{dragPan:null,dragPanOptions:null,zoomBox:null,zoomWheelEnabled:true,handleRightClicks:true,initialize:function(options){this.handlers={};OpenLayers.Control.prototype.initialize.apply(this,arguments);},destroy:function(){this.deactivate();if(this.dragPan){this.dragPan.destroy();}
this.dragPan=null;if(this.zoomBox){this.zoomBox.destroy();}
this.zoomBox=null;OpenLayers.Control.prototype.destroy.apply(this,arguments);},activate:function(){this.dragPan.activate();if(this.zoomWheelEnabled){this.handlers.wheel.activate();}
-this.handlers.click.activate();this.zoomBox.activate();return OpenLayers.Control.prototype.activate.apply(this,arguments);},deactivate:function(){this.zoomBox.deactivate();this.dragPan.deactivate();this.handlers.click.deactivate();this.handlers.wheel.deactivate();return OpenLayers.Control.prototype.deactivate.apply(this,arguments);},draw:function(){this.handlers.click=new OpenLayers.Handler.Click(this,{'dblclick':this.defaultDblClick},{'double':true,'stopDouble':true});this.dragPan=new OpenLayers.Control.DragPan({map:this.map});this.zoomBox=new OpenLayers.Control.ZoomBox({map:this.map,keyMask:OpenLayers.Handler.MOD_SHIFT});this.dragPan.draw();this.zoomBox.draw();this.handlers.wheel=new OpenLayers.Handler.MouseWheel(this,{"up":this.wheelUp,"down":this.wheelDown});this.activate();},defaultDblClick:function(evt){var newCenter=this.map.getLonLatFromViewPortPx(evt.xy);this.map.setCenter(newCenter,this.map.zoom+1);},wheelChange:function(evt,deltaZ){var newZoom=this.map.getZoom()+deltaZ;if(!this.map.isValidZoomLevel(newZoom)){return;}
-var size=this.map.getSize();var deltaX=size.w/2-evt.xy.x;var deltaY=evt.xy.y-size.h/2;var newRes=this.map.baseLayer.getResolutionForZoom(newZoom);var zoomPoint=this.map.getLonLatFromPixel(evt.xy);var newCenter=new OpenLayers.LonLat(zoomPoint.lon+deltaX*newRes,zoomPoint.lat+deltaY*newRes);this.map.setCenter(newCenter,newZoom);},wheelUp:function(evt){this.wheelChange(evt,1);},wheelDown:function(evt){this.wheelChange(evt,-1);},disableZoomWheel:function(){this.zoomWheelEnabled=false;this.handlers.wheel.deactivate();},enableZoomWheel:function(){this.zoomWheelEnabled=true;if(this.active){this.handlers.wheel.activate();}},CLASS_NAME:"OpenLayers.Control.Navigation"});OpenLayers.Layer.MapGuide=OpenLayers.Class(OpenLayers.Layer.Grid,{isBaseLayer:true,singleTile:false,TILE_PARAMS:{operation:'GETTILEIMAGE',version:'1.2.0'},SINGLE_TILE_PARAMS:{operation:'GETMAPIMAGE',format:'PNG',locale:'en',clip:'1',version:'1.0.0'},defaultSize:new OpenLayers.Size(300,300),initialize:function(name,url,params,options){OpenLayers.Layer.Grid.prototype.initialize.apply(this,arguments);if(options==null||options.isBaseLayer==null){this.isBaseLayer=((this.transparent!="true")&&(this.transparent!=true));}
+this.handlers.click.activate();this.zoomBox.activate();return OpenLayers.Control.prototype.activate.apply(this,arguments);},deactivate:function(){this.zoomBox.deactivate();this.dragPan.deactivate();this.handlers.click.deactivate();this.handlers.wheel.deactivate();return OpenLayers.Control.prototype.deactivate.apply(this,arguments);},draw:function(){if(this.handleRightClicks){this.map.div.oncontextmenu=function(){return false;};}
+var clickCallbacks={'dblclick':this.defaultDblClick,'dblrightclick':this.defaultDblRightClick};var clickOptions={'double':true,'stopDouble':true};this.handlers.click=new OpenLayers.Handler.Click(this,clickCallbacks,clickOptions);this.dragPan=new OpenLayers.Control.DragPan(OpenLayers.Util.extend({map:this.map},this.dragPanOptions));this.zoomBox=new OpenLayers.Control.ZoomBox({map:this.map,keyMask:OpenLayers.Handler.MOD_SHIFT});this.dragPan.draw();this.zoomBox.draw();this.handlers.wheel=new OpenLayers.Handler.MouseWheel(this,{"up":this.wheelUp,"down":this.wheelDown});this.activate();},defaultDblClick:function(evt){var newCenter=this.map.getLonLatFromViewPortPx(evt.xy);this.map.setCenter(newCenter,this.map.zoom+1);},defaultDblRightClick:function(evt){var newCenter=this.map.getLonLatFromViewPortPx(evt.xy);this.map.setCenter(newCenter,this.map.zoom-1);},wheelChange:function(evt,deltaZ){var newZoom=this.map.getZoom()+deltaZ;if(!this.map.isValidZoomLevel(newZoom)){return;}
+var size=this.map.getSize();var deltaX=size.w/2-evt.xy.x;var deltaY=evt.xy.y-size.h/2;var newRes=this.map.baseLayer.getResolutionForZoom(newZoom);var zoomPoint=this.map.getLonLatFromPixel(evt.xy);var newCenter=new OpenLayers.LonLat(zoomPoint.lon+deltaX*newRes,zoomPoint.lat+deltaY*newRes);this.map.setCenter(newCenter,newZoom);},wheelUp:function(evt){this.wheelChange(evt,1);},wheelDown:function(evt){this.wheelChange(evt,-1);},disableZoomWheel:function(){this.zoomWheelEnabled=false;this.handlers.wheel.deactivate();},enableZoomWheel:function(){this.zoomWheelEnabled=true;if(this.active){this.handlers.wheel.activate();}},CLASS_NAME:"OpenLayers.Control.Navigation"});OpenLayers.Geometry=OpenLayers.Class({id:null,parent:null,bounds:null,initialize:function(){this.id=OpenLayers.Util.createUniqueID(this.CLASS_NAME+"_");},destroy:function(){this.id=null;this.bounds=null;},clone:function(){return new OpenLayers.Geometry();},setBounds:function(bounds){if(bounds){this.bounds=bounds.clone();}},clearBounds:function(){this.bounds=null;if(this.parent){this.parent.clearBounds();}},extendBounds:function(newBounds){var bounds=this.getBounds();if(!bounds){this.setBounds(newBounds);}else{this.bounds.extend(newBounds);}},getBounds:function(){if(this.bounds==null){this.calculateBounds();}
+return this.bounds;},calculateBounds:function(){},atPoint:function(lonlat,toleranceLon,toleranceLat){var atPoint=false;var bounds=this.getBounds();if((bounds!=null)&&(lonlat!=null)){var dX=(toleranceLon!=null)?toleranceLon:0;var dY=(toleranceLat!=null)?toleranceLat:0;var toleranceBounds=new OpenLayers.Bounds(this.bounds.left-dX,this.bounds.bottom-dY,this.bounds.right+dX,this.bounds.top+dY);atPoint=toleranceBounds.containsLonLat(lonlat);}
+return atPoint;},getLength:function(){return 0.0;},getArea:function(){return 0.0;},toString:function(){return OpenLayers.Format.WKT.prototype.write(new OpenLayers.Feature.Vector(this));},CLASS_NAME:"OpenLayers.Geometry"});OpenLayers.Geometry.segmentsIntersect=function(seg1,seg2,point){var intersection=false;var x11_21=seg1.x1-seg2.x1;var y11_21=seg1.y1-seg2.y1;var x12_11=seg1.x2-seg1.x1;var y12_11=seg1.y2-seg1.y1;var y22_21=seg2.y2-seg2.y1;var x22_21=seg2.x2-seg2.x1;var d=(y22_21*x12_11)-(x22_21*y12_11);var n1=(x22_21*y11_21)-(y22_21*x11_21);var n2=(x12_11*y11_21)-(y12_11*x11_21);if(d==0){if(n1==0&&n2==0){intersection=true;}}else{var along1=n1/d;var along2=n2/d;if(along1>=0&&along1<=1&&along2>=0&&along2<=1){if(!point){intersection=true;}else{var x=seg1.x1+(along1*x12_11);var y=seg1.y1+(along1*y12_11);intersection=new OpenLayers.Geometry.Point(x,y);}}}
+return intersection;};OpenLayers.Layer.MapGuide=OpenLayers.Class(OpenLayers.Layer.Grid,{isBaseLayer:true,singleTile:false,TILE_PARAMS:{operation:'GETTILEIMAGE',version:'1.2.0'},SINGLE_TILE_PARAMS:{operation:'GETMAPIMAGE',format:'PNG',locale:'en',clip:'1',version:'1.0.0'},defaultSize:new OpenLayers.Size(300,300),initialize:function(name,url,params,options){OpenLayers.Layer.Grid.prototype.initialize.apply(this,arguments);if(options==null||options.isBaseLayer==null){this.isBaseLayer=((this.transparent!="true")&&(this.transparent!=true));}
if(this.singleTile){OpenLayers.Util.applyDefaults(this.params,this.SINGLE_TILE_PARAMS);}else{OpenLayers.Util.applyDefaults(this.params,this.TILE_PARAMS);this.setTileSize(this.defaultSize);}},clone:function(obj){if(obj==null){obj=new OpenLayers.Layer.MapGuide(this.name,this.url,this.params,this.options);}
-obj=OpenLayers.Layer.Grid.prototype.clone.apply(this,[obj]);return obj;},addTile:function(bounds,position){return new OpenLayers.Tile.Image(this,position,bounds,null,this.tileSize);},getURL:function(bounds){var url;var center=bounds.getCenterLonLat();var mapSize=this.map.getCurrentSize();if(this.singleTile){var params={};params.setdisplaydpi=OpenLayers.DOTS_PER_INCH;params.setdisplayheight=mapSize.h*this.ratio;params.setdisplaywidth=mapSize.w*this.ratio;params.setviewcenterx=center.lon;params.setviewcentery=center.lat;params.setviewscale=this.map.getScale();if(!this.isBaseLayer){this.params.operation="GETDYNAMICMAPOVERLAYIMAGE";var getVisParams={};getVisParams.operation="GETVISIBLEMAPEXTENT";getVisParams.version="1.0.0";getVisParams.session=this.params.session;getVisParams.mapName=this.params.mapName;getVisParams.format='text/xml';getVisParams=OpenLayers.Util.extend(getVisParams,params);new OpenLayers.Ajax.Request(this.url,{parameters:getVisParams,method:'get',asynchronous:false});}
+obj=OpenLayers.Layer.Grid.prototype.clone.apply(this,[obj]);return obj;},addTile:function(bounds,position){return new OpenLayers.Tile.Image(this,position,bounds,null,this.tileSize);},getURL:function(bounds){var url;var center=bounds.getCenterLonLat();var mapSize=this.map.getCurrentSize();if(this.singleTile){var params={};params.setdisplaydpi=OpenLayers.DOTS_PER_INCH;params.setdisplayheight=mapSize.h*this.ratio;params.setdisplaywidth=mapSize.w*this.ratio;params.setviewcenterx=center.lon;params.setviewcentery=center.lat;params.setviewscale=this.map.getScale();if(!this.isBaseLayer){this.params.operation="GETDYNAMICMAPOVERLAYIMAGE";var getVisParams={};getVisParams=OpenLayers.Util.extend(getVisParams,params);getVisParams.operation="GETVISIBLEMAPEXTENT";getVisParams.version="1.0.0";getVisParams.session=this.params.session;getVisParams.mapName=this.params.mapName;getVisParams.format='text/xml';url=this.getFullRequestString(getVisParams);OpenLayers.Request.GET({url:url,async:false});}
url=this.getFullRequestString(params);}else{var currentRes=this.map.getResolution();var colidx=Math.floor((bounds.left-this.maxExtent.left)/currentRes);colidx=Math.round(colidx/this.tileSize.w);var rowidx=Math.floor((this.maxExtent.top-bounds.top)/currentRes);rowidx=Math.round(rowidx/this.tileSize.h);url=this.getFullRequestString({tilecol:colidx,tilerow:rowidx,scaleindex:this.resolutions.length-this.map.zoom-1});}
return url;},getFullRequestString:function(newParams,altUrl){var url=(altUrl==null)?this.url:altUrl;if(typeof url=="object"){url=url[Math.floor(Math.random()*url.length)];}
var requestString=url;var allParams=OpenLayers.Util.extend({},this.params);allParams=OpenLayers.Util.extend(allParams,newParams);var urlParams=OpenLayers.Util.upperCaseObject(OpenLayers.Util.getArgs(url));for(var key in allParams){if(key.toUpperCase()in urlParams){delete allParams[key];}}
var paramsString=OpenLayers.Util.getParameterString(allParams);paramsString=paramsString.replace(/,/g,"+");if(paramsString!=""){var lastServerChar=url.charAt(url.length-1);if((lastServerChar=="&")||(lastServerChar=="?")){requestString+=paramsString;}else{if(url.indexOf('?')==-1){requestString+='?'+paramsString;}else{requestString+='&'+paramsString;}}}
-return requestString;},calculateGridLayout:function(bounds,extent,resolution){var tilelon=resolution*this.tileSize.w;var tilelat=resolution*this.tileSize.h;var offsetlon=bounds.left-extent.left;var tilecol=Math.floor(offsetlon/tilelon)-this.buffer;var tilecolremain=offsetlon/tilelon-tilecol;var tileoffsetx=-tilecolremain*this.tileSize.w;var tileoffsetlon=extent.left+tilecol*tilelon;var offsetlat=extent.top-bounds.top+tilelat;var tilerow=Math.floor(offsetlat/tilelat)-this.buffer;var tilerowremain=tilerow-offsetlat/tilelat;var tileoffsety=tilerowremain*this.tileSize.h;var tileoffsetlat=extent.top-tilelat*tilerow;return{tilelon:tilelon,tilelat:tilelat,tileoffsetlon:tileoffsetlon,tileoffsetlat:tileoffsetlat,tileoffsetx:tileoffsetx,tileoffsety:tileoffsety};},CLASS_NAME:"OpenLayers.Layer.MapGuide"});OpenLayers.Layer.MapServer=OpenLayers.Class(OpenLayers.Layer.Grid,{DEFAULT_PARAMS:{mode:"map",map_imagetype:"png"},initialize:function(name,url,params,options){var newArguments=[];newArguments.push(name,url,params,options);OpenLayers.Layer.Grid.prototype.initialize.apply(this,newArguments);if(arguments.length>0){OpenLayers.Util.applyDefaults(this.params,this.DEFAULT_PARAMS);}
-if(options==null||options.isBaseLayer==null){this.isBaseLayer=((this.params.transparent!="true")&&(this.params.transparent!=true));}},clone:function(obj){if(obj==null){obj=new OpenLayers.Layer.MapServer(this.name,this.url,this.params,this.options);}
+return requestString;},calculateGridLayout:function(bounds,extent,resolution){var tilelon=resolution*this.tileSize.w;var tilelat=resolution*this.tileSize.h;var offsetlon=bounds.left-extent.left;var tilecol=Math.floor(offsetlon/tilelon)-this.buffer;var tilecolremain=offsetlon/tilelon-tilecol;var tileoffsetx=-tilecolremain*this.tileSize.w;var tileoffsetlon=extent.left+tilecol*tilelon;var offsetlat=extent.top-bounds.top+tilelat;var tilerow=Math.floor(offsetlat/tilelat)-this.buffer;var tilerowremain=tilerow-offsetlat/tilelat;var tileoffsety=tilerowremain*this.tileSize.h;var tileoffsetlat=extent.top-tilelat*tilerow;return{tilelon:tilelon,tilelat:tilelat,tileoffsetlon:tileoffsetlon,tileoffsetlat:tileoffsetlat,tileoffsetx:tileoffsetx,tileoffsety:tileoffsety};},CLASS_NAME:"OpenLayers.Layer.MapGuide"});OpenLayers.Layer.MapServer=OpenLayers.Class(OpenLayers.Layer.Grid,{DEFAULT_PARAMS:{mode:"map",map_imagetype:"png"},initialize:function(name,url,params,options){var newArguments=[];newArguments.push(name,url,params,options);OpenLayers.Layer.Grid.prototype.initialize.apply(this,newArguments);this.params=OpenLayers.Util.applyDefaults(this.params,this.DEFAULT_PARAMS);if(options==null||options.isBaseLayer==null){this.isBaseLayer=((this.params.transparent!="true")&&(this.params.transparent!=true));}},clone:function(obj){if(obj==null){obj=new OpenLayers.Layer.MapServer(this.name,this.url,this.params,this.options);}
obj=OpenLayers.Layer.Grid.prototype.clone.apply(this,[obj]);return obj;},addTile:function(bounds,position){return new OpenLayers.Tile.Image(this,position,bounds,null,this.tileSize);},getURL:function(bounds){bounds=this.adjustBounds(bounds);var extent=[bounds.left,bounds.bottom,bounds.right,bounds.top];var imageSize=this.getImageSize();var url=this.getFullRequestString({mapext:extent,imgext:extent,map_size:[imageSize.w,imageSize.h],imgx:imageSize.w/2,imgy:imageSize.h/2,imgxy:[imageSize.w,imageSize.h]});return url;},getFullRequestString:function(newParams,altUrl){var url=(altUrl==null)?this.url:altUrl;var allParams=OpenLayers.Util.extend({},this.params);allParams=OpenLayers.Util.extend(allParams,newParams);var paramsString=OpenLayers.Util.getParameterString(allParams);if(url instanceof Array){url=this.selectUrl(paramsString,url);}
var urlParams=OpenLayers.Util.upperCaseObject(OpenLayers.Util.getParameters(url));for(var key in allParams){if(key.toUpperCase()in urlParams){delete allParams[key];}}
paramsString=OpenLayers.Util.getParameterString(allParams);var requestString=url;paramsString=paramsString.replace(/,/g,"+");if(paramsString!=""){var lastServerChar=url.charAt(url.length-1);if((lastServerChar=="&")||(lastServerChar=="?")){requestString+=paramsString;}else{if(url.indexOf('?')==-1){requestString+='?'+paramsString;}else{requestString+='&'+paramsString;}}}
return requestString;},CLASS_NAME:"OpenLayers.Layer.MapServer"});OpenLayers.Layer.WMS=OpenLayers.Class(OpenLayers.Layer.Grid,{DEFAULT_PARAMS:{service:"WMS",version:"1.1.1",request:"GetMap",styles:"",exceptions:"application/vnd.ogc.se_inimage",format:"image/jpeg"},reproject:false,isBaseLayer:true,encodeBBOX:false,initialize:function(name,url,params,options){var newArguments=[];params=OpenLayers.Util.upperCaseObject(params);newArguments.push(name,url,params,options);OpenLayers.Layer.Grid.prototype.initialize.apply(this,newArguments);OpenLayers.Util.applyDefaults(this.params,OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS));if(this.params.TRANSPARENT&&this.params.TRANSPARENT.toString().toLowerCase()=="true"){if((options==null)||(!options.isBaseLayer)){this.isBaseLayer=false;}
if(this.params.FORMAT=="image/jpeg"){this.params.FORMAT=OpenLayers.Util.alphaHack()?"image/gif":"image/png";}}},destroy:function(){OpenLayers.Layer.Grid.prototype.destroy.apply(this,arguments);},clone:function(obj){if(obj==null){obj=new OpenLayers.Layer.WMS(this.name,this.url,this.params,this.options);}
-obj=OpenLayers.Layer.Grid.prototype.clone.apply(this,[obj]);return obj;},getURL:function(bounds){bounds=this.adjustBounds(bounds);var imageSize=this.getImageSize();var newParams={'BBOX':this.encodeBBOX?bounds.toBBOX():bounds.toArray(),'WIDTH':imageSize.w,'HEIGHT':imageSize.h};var requestString=this.getFullRequestString(newParams);return requestString;},addTile:function(bounds,position){return new OpenLayers.Tile.Image(this,position,bounds,null,this.tileSize);},mergeNewParams:function(newParams){var upperParams=OpenLayers.Util.upperCaseObject(newParams);var newArguments=[upperParams];return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(this,newArguments);},getFullRequestString:function(newParams,altUrl){var projectionCode=this.map.getProjection();this.params.SRS=(projectionCode=="none")?null:projectionCode;return OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(this,arguments);},CLASS_NAME:"OpenLayers.Layer.WMS"});
\ No newline at end of file
+obj=OpenLayers.Layer.Grid.prototype.clone.apply(this,[obj]);return obj;},getURL:function(bounds){bounds=this.adjustBounds(bounds);var imageSize=this.getImageSize();var newParams={'BBOX':this.encodeBBOX?bounds.toBBOX():bounds.toArray(),'WIDTH':imageSize.w,'HEIGHT':imageSize.h};var requestString=this.getFullRequestString(newParams);return requestString;},addTile:function(bounds,position){return new OpenLayers.Tile.Image(this,position,bounds,null,this.tileSize);},mergeNewParams:function(newParams){var upperParams=OpenLayers.Util.upperCaseObject(newParams);var newArguments=[upperParams];return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(this,newArguments);},getFullRequestString:function(newParams,altUrl){var projectionCode=this.map.getProjection();this.params.SRS=(projectionCode=="none")?null:projectionCode;return OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(this,arguments);},CLASS_NAME:"OpenLayers.Layer.WMS"});OpenLayers.StyleMap=OpenLayers.Class({styles:null,extendDefault:true,initialize:function(style,options){this.styles={"default":new OpenLayers.Style(OpenLayers.Feature.Vector.style["default"]),"select":new OpenLayers.Style(OpenLayers.Feature.Vector.style["select"]),"temporary":new OpenLayers.Style(OpenLayers.Feature.Vector.style["temporary"])};if(style instanceof OpenLayers.Style){this.styles["default"]=style;this.styles["select"]=style;this.styles["temporary"]=style;}else if(typeof style=="object"){for(var key in style){if(style[key]instanceof OpenLayers.Style){this.styles[key]=style[key];}else if(typeof style[key]=="object"){this.styles[key]=new OpenLayers.Style(style[key]);}else{this.styles["default"]=new OpenLayers.Style(style);this.styles["select"]=new OpenLayers.Style(style);this.styles["temporary"]=new OpenLayers.Style(style);break;}}}
+OpenLayers.Util.extend(this,options);},destroy:function(){for(var key in this.styles){this.styles[key].destroy();}
+this.styles=null;},createSymbolizer:function(feature,intent){if(!feature){feature=new OpenLayers.Feature.Vector();}
+if(!this.styles[intent]){intent="default";}
+feature.renderIntent=intent;var defaultSymbolizer={};if(this.extendDefault&&intent!="default"){defaultSymbolizer=this.styles["default"].createSymbolizer(feature);}
+return OpenLayers.Util.extend(defaultSymbolizer,this.styles[intent].createSymbolizer(feature));},addUniqueValueRules:function(renderIntent,property,symbolizers,context){var rules=[];for(var value in symbolizers){rules.push(new OpenLayers.Rule({symbolizer:symbolizers[value],context:context,filter:new OpenLayers.Filter.Comparison({type:OpenLayers.Filter.Comparison.EQUAL_TO,property:property,value:value})}));}
+this.styles[renderIntent].addRules(rules);},CLASS_NAME:"OpenLayers.StyleMap"});OpenLayers.Geometry.Collection=OpenLayers.Class(OpenLayers.Geometry,{components:null,componentTypes:null,initialize:function(components){OpenLayers.Geometry.prototype.initialize.apply(this,arguments);this.components=[];if(components!=null){this.addComponents(components);}},destroy:function(){this.components.length=0;this.components=null;},clone:function(){var geometry=eval("new "+this.CLASS_NAME+"()");for(var i=0,len=this.components.length;i<len;i++){geometry.addComponent(this.components[i].clone());}
+OpenLayers.Util.applyDefaults(geometry,this);return geometry;},getComponentsString:function(){var strings=[];for(var i=0,len=this.components.length;i<len;i++){strings.push(this.components[i].toShortString());}
+return strings.join(",");},calculateBounds:function(){this.bounds=null;if(this.components&&this.components.length>0){this.setBounds(this.components[0].getBounds());for(var i=1,len=this.components.length;i<len;i++){this.extendBounds(this.components[i].getBounds());}}},addComponents:function(components){if(!(components instanceof Array)){components=[components];}
+for(var i=0,len=components.length;i<len;i++){this.addComponent(components[i]);}},addComponent:function(component,index){var added=false;if(component){if(this.componentTypes==null||(OpenLayers.Util.indexOf(this.componentTypes,component.CLASS_NAME)>-1)){if(index!=null&&(index<this.components.length)){var components1=this.components.slice(0,index);var components2=this.components.slice(index,this.components.length);components1.push(component);this.components=components1.concat(components2);}else{this.components.push(component);}
+component.parent=this;this.clearBounds();added=true;}}
+return added;},removeComponents:function(components){if(!(components instanceof Array)){components=[components];}
+for(var i=components.length-1;i>=0;--i){this.removeComponent(components[i]);}},removeComponent:function(component){OpenLayers.Util.removeItem(this.components,component);this.clearBounds();},getLength:function(){var length=0.0;for(var i=0,len=this.components.length;i<len;i++){length+=this.components[i].getLength();}
+return length;},getArea:function(){var area=0.0;for(var i=0,len=this.components.length;i<len;i++){area+=this.components[i].getArea();}
+return area;},move:function(x,y){for(var i=0,len=this.components.length;i<len;i++){this.components[i].move(x,y);}},rotate:function(angle,origin){for(var i=0,len=this.components.length;i<len;++i){this.components[i].rotate(angle,origin);}},resize:function(scale,origin,ratio){for(var i=0;i<this.components.length;++i){this.components[i].resize(scale,origin,ratio);}},equals:function(geometry){var equivalent=true;if(!geometry||!geometry.CLASS_NAME||(this.CLASS_NAME!=geometry.CLASS_NAME)){equivalent=false;}else if(!(geometry.components instanceof Array)||(geometry.components.length!=this.components.length)){equivalent=false;}else{for(var i=0,len=this.components.length;i<len;++i){if(!this.components[i].equals(geometry.components[i])){equivalent=false;break;}}}
+return equivalent;},transform:function(source,dest){if(source&&dest){for(var i=0,len=this.components.length;i<len;i++){var component=this.components[i];component.transform(source,dest);}
+this.bounds=null;}
+return this;},intersects:function(geometry){var intersect=false;for(var i=0,len=this.components.length;i<len;++i){intersect=geometry.intersects(this.components[i]);if(intersect){break;}}
+return intersect;},CLASS_NAME:"OpenLayers.Geometry.Collection"});OpenLayers.Geometry.Point=OpenLayers.Class(OpenLayers.Geometry,{x:null,y:null,initialize:function(x,y){OpenLayers.Geometry.prototype.initialize.apply(this,arguments);this.x=parseFloat(x);this.y=parseFloat(y);},clone:function(obj){if(obj==null){obj=new OpenLayers.Geometry.Point(this.x,this.y);}
+OpenLayers.Util.applyDefaults(obj,this);return obj;},calculateBounds:function(){this.bounds=new OpenLayers.Bounds(this.x,this.y,this.x,this.y);},distanceTo:function(point){var distance=0.0;if((this.x!=null)&&(this.y!=null)&&(point!=null)&&(point.x!=null)&&(point.y!=null)){var dx2=Math.pow(this.x-point.x,2);var dy2=Math.pow(this.y-point.y,2);distance=Math.sqrt(dx2+dy2);}
+return distance;},equals:function(geom){var equals=false;if(geom!=null){equals=((this.x==geom.x&&this.y==geom.y)||(isNaN(this.x)&&isNaN(this.y)&&isNaN(geom.x)&&isNaN(geom.y)));}
+return equals;},toShortString:function(){return(this.x+", "+this.y);},move:function(x,y){this.x=this.x+x;this.y=this.y+y;this.clearBounds();},rotate:function(angle,origin){angle*=Math.PI/180;var radius=this.distanceTo(origin);var theta=angle+Math.atan2(this.y-origin.y,this.x-origin.x);this.x=origin.x+(radius*Math.cos(theta));this.y=origin.y+(radius*Math.sin(theta));this.clearBounds();},resize:function(scale,origin,ratio){ratio=(ratio==undefined)?1:ratio;this.x=origin.x+(scale*ratio*(this.x-origin.x));this.y=origin.y+(scale*(this.y-origin.y));this.clearBounds();},intersects:function(geometry){var intersect=false;if(geometry.CLASS_NAME=="OpenLayers.Geometry.Point"){intersect=this.equals(geometry);}else{intersect=geometry.intersects(this);}
+return intersect;},transform:function(source,dest){if((source&&dest)){OpenLayers.Projection.transform(this,source,dest);this.bounds=null;}
+return this;},CLASS_NAME:"OpenLayers.Geometry.Point"});OpenLayers.Layer.Vector=OpenLayers.Class(OpenLayers.Layer,{EVENT_TYPES:["beforefeatureadded","featureadded","featuresadded","beforefeatureremoved","featureremoved","featuresremoved","beforefeatureselected","featureselected","featureunselected","beforefeaturemodified","featuremodified","afterfeaturemodified"],isBaseLayer:false,isFixed:false,isVector:true,features:null,selectedFeatures:null,unrenderedFeatures:null,reportError:true,style:null,styleMap:null,strategies:null,protocol:null,renderers:['SVG','VML','Canvas'],renderer:null,rendererOptions:null,geometryType:null,drawn:false,initialize:function(name,options){this.EVENT_TYPES=OpenLayers.Layer.Vector.prototype.EVENT_TYPES.concat(OpenLayers.Layer.prototype.EVENT_TYPES);OpenLayers.Layer.prototype.initialize.apply(this,arguments);if(!this.renderer||!this.renderer.supported()){this.assignRenderer();}
+if(!this.renderer||!this.renderer.supported()){this.renderer=null;this.displayError();}
+if(!this.styleMap){this.styleMap=new OpenLayers.StyleMap();}
+this.features=[];this.selectedFeatures=[];this.unrenderedFeatures={};if(this.strategies){for(var i=0,len=this.strategies.length;i<len;i++){this.strategies[i].setLayer(this);}}},destroy:function(){if(this.strategies){var strategy,i,len;for(i=0,len=this.strategies.length;i<len;i++){strategy=this.strategies[i];if(strategy.autoDestroy){strategy.destroy();}}
+this.strategies=null;}
+if(this.protocol){if(this.protocol.autoDestroy){this.protocol.destroy();}
+this.protocol=null;}
+this.destroyFeatures();this.features=null;this.selectedFeatures=null;this.unrenderedFeatures=null;if(this.renderer){this.renderer.destroy();}
+this.renderer=null;this.geometryType=null;this.drawn=null;OpenLayers.Layer.prototype.destroy.apply(this,arguments);},assignRenderer:function(){for(var i=0,len=this.renderers.length;i<this.renderers.length;i++){var rendererClass=OpenLayers.Renderer[this.renderers[i]];if(rendererClass&&rendererClass.prototype.supported()){this.renderer=new rendererClass(this.div,this.rendererOptions);break;}}},displayError:function(){if(this.reportError){OpenLayers.Console.userError(OpenLayers.i18n("browserNotSupported",{'renderers':this.renderers.join("\n")}));}},setMap:function(map){OpenLayers.Layer.prototype.setMap.apply(this,arguments);if(!this.renderer){this.map.removeLayer(this);}else{this.renderer.map=this.map;this.renderer.setSize(this.map.getSize());}
+if(this.strategies){var strategy,i,len;for(i=0,len=this.strategies.length;i<len;i++){strategy=this.strategies[i];if(strategy.autoActivate){strategy.activate();}}}},removeMap:function(map){if(this.strategies){var strategy,i,len;for(i=0,len=this.strategies.length;i<len;i++){strategy=this.strategies[i];if(strategy.autoActivate){strategy.deactivate();}}}},onMapResize:function(){OpenLayers.Layer.prototype.onMapResize.apply(this,arguments);this.renderer.setSize(this.map.getSize());},moveTo:function(bounds,zoomChanged,dragging){OpenLayers.Layer.prototype.moveTo.apply(this,arguments);var coordSysUnchanged=true;if(!dragging){this.renderer.root.style.visibility="hidden";this.div.style.left=-parseInt(this.map.layerContainerDiv.style.left)+"px";this.div.style.top=-parseInt(this.map.layerContainerDiv.style.top)+"px";var extent=this.map.getExtent();coordSysUnchanged=this.renderer.setExtent(extent,zoomChanged);this.renderer.root.style.visibility="visible";if(navigator.userAgent.toLowerCase().indexOf("gecko")!=-1){this.div.scrollLeft=this.div.scrollLeft;}
+if(!zoomChanged&&coordSysUnchanged){var unrenderedFeatures={};for(var i in this.unrenderedFeatures){var feature=this.unrenderedFeatures[i];if(!this.drawFeature(feature)){unrenderedFeatures[i]=feature;}}
+this.unrenderedFeatures=unrenderedFeatures;}}
+if(!this.drawn||zoomChanged||!coordSysUnchanged){this.unrenderedFeatures={};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;}
+feature=this.features[i];if(!this.drawFeature(feature)){this.unrenderedFeatures[feature.id]=feature;};}}},addFeatures:function(features,options){if(!(features instanceof Array)){features=[features];}
+var notify=!options||!options.silent;for(var i=0,len=features.length;i<len;i++){if(i!=(features.length-1)){this.renderer.locked=true;}else{this.renderer.locked=false;}
+var feature=features[i];if(this.geometryType&&!(feature.geometry instanceof this.geometryType)){var throwStr=OpenLayers.i18n('componentShouldBe',{'geomType':this.geometryType.prototype.CLASS_NAME});throw throwStr;}
+this.features.push(feature);feature.layer=this;if(!feature.style&&this.style){feature.style=OpenLayers.Util.extend({},this.style);}
+if(notify){this.events.triggerEvent("beforefeatureadded",{feature:feature});this.preFeatureInsert(feature);}
+if(this.drawn){if(!this.drawFeature(feature)){this.unrenderedFeatures[feature.id]=feature;}}
+if(notify){this.events.triggerEvent("featureadded",{feature:feature});this.onFeatureInsert(feature);}}
+if(notify){this.events.triggerEvent("featuresadded",{features:features});}},removeFeatures:function(features,options){if(!(features instanceof Array)){features=[features];}
+if(features.length<=0){return;}
+var notify=!options||!options.silent;for(var i=features.length-1;i>=0;i--){if(i!=0&&features[i-1].geometry){this.renderer.locked=true;}else{this.renderer.locked=false;}
+var feature=features[i];delete this.unrenderedFeatures[feature.id];if(notify){this.events.triggerEvent("beforefeatureremoved",{feature:feature});}
+this.features=OpenLayers.Util.removeItem(this.features,feature);feature.layer=null;if(feature.geometry){this.renderer.eraseGeometry(feature.geometry);}
+if(OpenLayers.Util.indexOf(this.selectedFeatures,feature)!=-1){OpenLayers.Util.removeItem(this.selectedFeatures,feature);}
+if(notify){this.events.triggerEvent("featureremoved",{feature:feature});}}
+if(notify){this.events.triggerEvent("featuresremoved",{features:features});}},destroyFeatures:function(features,options){var all=(features==undefined);if(all){features=this.features;}
+this.removeFeatures(features,options);for(var i=0;i<features.length;i++){features[i].destroy();}},drawFeature:function(feature,style){if(typeof style!="object"){var renderIntent=typeof style=="string"?style:feature.renderIntent;style=feature.style||this.style;if(!style){style=this.styleMap.createSymbolizer(feature,renderIntent);}}
+return this.renderer.drawFeature(feature,style);},eraseFeatures:function(features){this.renderer.eraseFeatures(features);},getFeatureFromEvent:function(evt){if(!this.renderer){OpenLayers.Console.error(OpenLayers.i18n("getFeatureError"));return null;}
+var featureId=this.renderer.getFeatureIdFromEvent(evt);return this.getFeatureById(featureId);},getFeatureById:function(featureId){var feature=null;for(var i=0,len=this.features.length;i<len;++i){if(this.features[i].id==featureId){feature=this.features[i];break;}}
+return feature;},onFeatureInsert:function(feature){},preFeatureInsert:function(feature){},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++){maxExtent.extend(this.features[i].geometry.getBounds());}}
+return maxExtent;},CLASS_NAME:"OpenLayers.Layer.Vector"});OpenLayers.Geometry.MultiPoint=OpenLayers.Class(OpenLayers.Geometry.Collection,{componentTypes:["OpenLayers.Geometry.Point"],initialize:function(components){OpenLayers.Geometry.Collection.prototype.initialize.apply(this,arguments);},addPoint:function(point,index){this.addComponent(point,index);},removePoint:function(point){this.removeComponent(point);},CLASS_NAME:"OpenLayers.Geometry.MultiPoint"});OpenLayers.Geometry.Polygon=OpenLayers.Class(OpenLayers.Geometry.Collection,{componentTypes:["OpenLayers.Geometry.LinearRing"],initialize:function(components){OpenLayers.Geometry.Collection.prototype.initialize.apply(this,arguments);},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;},containsPoint:function(point){var numRings=this.components.length;var contained=false;if(numRings>0){contained=this.components[0].containsPoint(point);if(contained!==1){if(contained&&numRings>1){var hole;for(var i=1;i<numRings;++i){hole=this.components[i].containsPoint(point);if(hole){if(hole===1){contained=1;}else{contained=false;}
+break;}}}}}
+return contained;},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"){for(i=0,len=this.components.length;i<len;++i){intersect=geometry.intersects(this.components[i]);if(intersect){break;}}
+if(!intersect){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;}}}
+if(!intersect&&geometry.CLASS_NAME=="OpenLayers.Geometry.Polygon"){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"});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=OpenLayers.Class(OpenLayers.Handler,{point:null,layer:null,drawing:false,mouseDown:false,lastDown:null,lastUp:null,initialize:function(control,callbacks,options){this.style=OpenLayers.Util.extend(OpenLayers.Feature.Vector.style['default'],{});OpenLayers.Handler.prototype.initialize.apply(this,arguments);},activate:function(){if(!OpenLayers.Handler.prototype.activate.apply(this,arguments)){return false;}
+var options={displayInLayerSwitcher:false,calculateInRange:function(){return true;}};this.layer=new OpenLayers.Layer.Vector(this.CLASS_NAME,options);this.map.addLayer(this.layer);return true;},createFeature:function(){this.point=new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point());},deactivate:function(){if(!OpenLayers.Handler.prototype.deactivate.apply(this,arguments)){return false;}
+if(this.drawing){this.cancel();}
+if(this.layer.map!=null){this.layer.destroy(false);}
+this.layer=null;return true;},destroyFeature:function(){if(this.point){this.point.destroy();}
+this.point=null;},finalize:function(){this.layer.renderer.clear();this.drawing=false;this.mouseDown=false;this.lastDown=null;this.lastUp=null;this.callback("done",[this.geometryClone()]);this.destroyFeature();},cancel:function(){this.layer.renderer.clear();this.drawing=false;this.mouseDown=false;this.lastDown=null;this.lastUp=null;this.callback("cancel",[this.geometryClone()]);this.destroyFeature();},click:function(evt){OpenLayers.Event.stop(evt);return false;},dblclick:function(evt){OpenLayers.Event.stop(evt);return false;},drawFeature:function(){this.layer.drawFeature(this.point,this.style);},geometryClone:function(){return this.point.geometry.clone();},mousedown:function(evt){if(!this.checkModifiers(evt)){return true;}
+if(this.lastDown&&this.lastDown.equals(evt.xy)){return true;}
+if(this.lastDown==null){this.createFeature();}
+this.lastDown=evt.xy;this.drawing=true;var lonlat=this.map.getLonLatFromPixel(evt.xy);this.point.geometry.x=lonlat.lon;this.point.geometry.y=lonlat.lat;this.drawFeature();return false;},mousemove:function(evt){if(this.drawing){var lonlat=this.map.getLonLatFromPixel(evt.xy);this.point.geometry.x=lonlat.lon;this.point.geometry.y=lonlat.lat;this.point.geometry.clearBounds();this.drawFeature();}
+return true;},mouseup:function(evt){if(this.drawing){this.finalize();return false;}else{return true;}},CLASS_NAME:"OpenLayers.Handler.Point"});OpenLayers.Geometry.Curve=OpenLayers.Class(OpenLayers.Geometry.MultiPoint,{componentTypes:["OpenLayers.Geometry.Point"],initialize:function(points){OpenLayers.Geometry.MultiPoint.prototype.initialize.apply(this,arguments);},getLength:function(){var length=0.0;if(this.components&&(this.components.length>1)){for(var i=1,len=this.components.length;i<len;i++){length+=this.components[i-1].distanceTo(this.components[i]);}}
+return length;},CLASS_NAME:"OpenLayers.Geometry.Curve"});OpenLayers.Geometry.LineString=OpenLayers.Class(OpenLayers.Geometry.Curve,{initialize:function(points){OpenLayers.Geometry.Curve.prototype.initialize.apply(this,arguments);},removeComponent:function(point){if(this.components&&(this.components.length>2)){OpenLayers.Geometry.Collection.prototype.removeComponent.apply(this,arguments);}},intersects:function(geometry){var intersect=false;var type=geometry.CLASS_NAME;if(type=="OpenLayers.Geometry.LineString"||type=="OpenLayers.Geometry.LinearRing"||type=="OpenLayers.Geometry.Point"){var segs1=this.getSortedSegments();var segs2;if(type=="OpenLayers.Geometry.Point"){segs2=[{x1:geometry.x,y1:geometry.y,x2:geometry.x,y2:geometry.y}];}else{segs2=geometry.getSortedSegments();}
+var seg1,seg1x1,seg1x2,seg1y1,seg1y2,seg2,seg2y1,seg2y2;outer:for(var i=0,len=segs1.length;i<len;++i){seg1=segs1[i];seg1x1=seg1.x1;seg1x2=seg1.x2;seg1y1=seg1.y1;seg1y2=seg1.y2;inner:for(var j=0,jlen=segs2.length;j<jlen;++j){seg2=segs2[j];if(seg2.x1>seg1x2){break;}
+if(seg2.x2<seg1x1){continue;}
+seg2y1=seg2.y1;seg2y2=seg2.y2;if(Math.min(seg2y1,seg2y2)>Math.max(seg1y1,seg1y2)){continue;}
+if(Math.max(seg2y1,seg2y2)<Math.min(seg1y1,seg1y2)){continue;}
+if(OpenLayers.Geometry.segmentsIntersect(seg1,seg2)){intersect=true;break outer;}}}}else{intersect=geometry.intersects(this);}
+return intersect;},getSortedSegments:function(){var numSeg=this.components.length-1;var segments=new Array(numSeg);for(var i=0;i<numSeg;++i){point1=this.components[i];point2=this.components[i+1];if(point1.x<point2.x){segments[i]={x1:point1.x,y1:point1.y,x2:point2.x,y2:point2.y};}else{segments[i]={x1:point2.x,y1:point2.y,x2:point1.x,y2:point1.y};}}
+function byX1(seg1,seg2){return seg1.x1-seg2.x1;}
+return segments.sort(byX1);},CLASS_NAME:"OpenLayers.Geometry.LineString"});OpenLayers.Handler.Path=OpenLayers.Class(OpenLayers.Handler.Point,{line:null,freehand:false,freehandToggle:'shiftKey',initialize:function(control,callbacks,options){OpenLayers.Handler.Point.prototype.initialize.apply(this,arguments);},createFeature:function(){this.line=new OpenLayers.Feature.Vector(new OpenLayers.Geometry.LineString());this.point=new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point());},destroyFeature:function(){OpenLayers.Handler.Point.prototype.destroyFeature.apply(this);if(this.line){this.line.destroy();}
+this.line=null;},addPoint:function(){this.line.geometry.addComponent(this.point.geometry.clone(),this.line.geometry.components.length);this.callback("point",[this.point.geometry]);},freehandMode:function(evt){return(this.freehandToggle&&evt[this.freehandToggle])?!this.freehand:this.freehand;},modifyFeature:function(){var index=this.line.geometry.components.length-1;this.line.geometry.components[index].x=this.point.geometry.x;this.line.geometry.components[index].y=this.point.geometry.y;this.line.geometry.components[index].clearBounds();},drawFeature:function(){this.layer.drawFeature(this.line,this.style);this.layer.drawFeature(this.point,this.style);},geometryClone:function(){return this.line.geometry.clone();},mousedown:function(evt){if(this.lastDown&&this.lastDown.equals(evt.xy)){return false;}
+if(this.lastDown==null){this.createFeature();}
+this.mouseDown=true;this.lastDown=evt.xy;var lonlat=this.control.map.getLonLatFromPixel(evt.xy);this.point.geometry.x=lonlat.lon;this.point.geometry.y=lonlat.lat;if((this.lastUp==null)||!this.lastUp.equals(evt.xy)){this.addPoint();}
+this.drawFeature();this.drawing=true;return false;},mousemove:function(evt){if(this.drawing){var lonlat=this.map.getLonLatFromPixel(evt.xy);this.point.geometry.x=lonlat.lon;this.point.geometry.y=lonlat.lat;this.point.geometry.clearBounds();if(this.mouseDown&&this.freehandMode(evt)){this.addPoint();}else{this.modifyFeature();}
+this.drawFeature();}
+return true;},mouseup:function(evt){this.mouseDown=false;if(this.drawing){if(this.freehandMode(evt)){this.finalize();}else{if(this.lastUp==null){this.addPoint();}
+this.lastUp=evt.xy;}
+return false;}
+return true;},dblclick:function(evt){if(!this.freehandMode(evt)){var index=this.line.geometry.components.length-1;this.line.geometry.removeComponent(this.line.geometry.components[index]);this.finalize();}
+return false;},CLASS_NAME:"OpenLayers.Handler.Path"});OpenLayers.Handler.Polygon=OpenLayers.Class(OpenLayers.Handler.Path,{polygon:null,initialize:function(control,callbacks,options){OpenLayers.Handler.Path.prototype.initialize.apply(this,arguments);},createFeature:function(){this.polygon=new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Polygon());this.line=new OpenLayers.Feature.Vector(new OpenLayers.Geometry.LinearRing());this.polygon.geometry.addComponent(this.line.geometry);this.point=new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point());},destroyFeature:function(){OpenLayers.Handler.Path.prototype.destroyFeature.apply(this);if(this.polygon){this.polygon.destroy();}
+this.polygon=null;},modifyFeature:function(){var index=this.line.geometry.components.length-2;this.line.geometry.components[index].x=this.point.geometry.x;this.line.geometry.components[index].y=this.point.geometry.y;this.line.geometry.components[index].clearBounds();},drawFeature:function(){this.layer.drawFeature(this.polygon,this.style);this.layer.drawFeature(this.point,this.style);},geometryClone:function(){return this.polygon.geometry.clone();},dblclick:function(evt){if(!this.freehandMode(evt)){var index=this.line.geometry.components.length-2;this.line.geometry.removeComponent(this.line.geometry.components[index]);this.finalize();}
+return false;},CLASS_NAME:"OpenLayers.Handler.Polygon"});
\ No newline at end of file
Modified: trunk/lib/OpenLayers/OpenLayersUncompressed.js
===================================================================
--- trunk/lib/OpenLayers/OpenLayersUncompressed.js 2008-09-05 14:31:06 UTC (rev 1498)
+++ trunk/lib/OpenLayers/OpenLayersUncompressed.js 2008-09-05 14:39:19 UTC (rev 1499)
@@ -43,6 +43,15 @@
*
**/
+/**
+ * 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
+ */
/* ======================================================================
OpenLayers/SingleFile.js
====================================================================== */
@@ -102,7 +111,7 @@
var scriptName = OpenLayers._scriptName;
var scripts = document.getElementsByTagName('script');
- for (var i = 0; i < scripts.length; i++) {
+ for (var i=0, len=scripts.length; i<len; i++) {
var src = scripts[i].getAttribute('src');
if (src) {
var index = src.lastIndexOf(scriptName);
@@ -146,6 +155,8 @@
"Rico/Corner.js",
"Rico/Color.js",
"OpenLayers/Ajax.js",
+ "OpenLayers/Request.js",
+ "OpenLayers/Request/XMLHttpRequest.js",
"OpenLayers/Events.js",
"OpenLayers/Projection.js",
"OpenLayers/Map.js",
@@ -238,8 +249,12 @@
"OpenLayers/Renderer.js",
"OpenLayers/Renderer/Elements.js",
"OpenLayers/Renderer/SVG.js",
+ "OpenLayers/Renderer/Canvas.js",
"OpenLayers/Renderer/VML.js",
"OpenLayers/Layer/Vector.js",
+ "OpenLayers/Strategy.js",
+ "OpenLayers/Strategy/Fixed.js",
+ "OpenLayers/Protocol.js",
"OpenLayers/Layer/PointTrack.js",
"OpenLayers/Layer/GML.js",
"OpenLayers/Style.js",
@@ -257,9 +272,14 @@
"OpenLayers/Format/WFS.js",
"OpenLayers/Format/WKT.js",
"OpenLayers/Format/OSM.js",
+ "OpenLayers/Format/GPX.js",
"OpenLayers/Format/SLD.js",
"OpenLayers/Format/SLD/v1.js",
"OpenLayers/Format/SLD/v1_0_0.js",
+ "OpenLayers/Format/SLD/v1.js",
+ "OpenLayers/Format/Filter.js",
+ "OpenLayers/Format/Filter/v1.js",
+ "OpenLayers/Format/Filter/v1_0_0.js",
"OpenLayers/Format/Text.js",
"OpenLayers/Format/JSON.js",
"OpenLayers/Format/GeoJSON.js",
@@ -281,7 +301,7 @@
var allScriptTags = new Array(jsfiles.length);
}
var host = OpenLayers._getScriptLocation() + "lib/";
- for (var i = 0; i < jsfiles.length; i++) {
+ for (var i=0, len=jsfiles.length; i<len; i++) {
if (docWrite) {
allScriptTags[i] = "<script src='" + host + jsfiles[i] +
"'></script>";
@@ -303,7 +323,7 @@
/**
* Constant: VERSION_NUMBER
*/
-OpenLayers.VERSION_NUMBER="$Revision: 6819 $";
+OpenLayers.VERSION_NUMBER="$Revision: 7862 $";
/* ======================================================================
OpenLayers/BaseTypes.js
====================================================================== */
@@ -394,7 +414,7 @@
camelize: function(str) {
var oStringList = str.split('-');
var camelizedString = oStringList[0];
- for (var i = 1; i < oStringList.length; i++) {
+ for (var i=1, len=oStringList.length; i<len; i++) {
var s = oStringList[i];
camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
}
@@ -428,7 +448,7 @@
}
var tokens = template.split("${");
var item, last, replacement;
- for(var i=1; i<tokens.length; i++) {
+ for(var i=1, len=tokens.length; i<len; i++) {
item = tokens[i];
last = item.indexOf("}");
if(last > 0) {
@@ -444,6 +464,31 @@
}
}
return tokens.join("");
+ },
+
+ /**
+ * Property: OpenLayers.String.numberRegEx
+ * Used to test strings as numbers.
+ */
+ numberRegEx: /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/,
+
+ /**
+ * APIFunction: OpenLayers.String.isNumeric
+ * Determine whether a string contains only a numeric value.
+ *
+ * Examples:
+ * (code)
+ * OpenLayers.String.isNumeric("6.02e23") // true
+ * OpenLayers.String.isNumeric("12 dozen") // false
+ * OpenLayers.String.isNumeric("4") // true
+ * OpenLayers.String.isNumeric(" 4 ") // false
+ * (end)
+ *
+ * Returns:
+ * {Boolean} String contains only a number.
+ */
+ isNumeric: function(value) {
+ return OpenLayers.String.numberRegEx.test(value);
}
};
@@ -812,7 +857,7 @@
};
var extended = {};
var parent;
- for(var i=0; i<arguments.length; ++i) {
+ for(var i=0, len=arguments.length; i<len; ++i) {
if(typeof arguments[i] == "function") {
// get the prototype of the superclass
parent = arguments[i].prototype;
@@ -863,7 +908,7 @@
OpenLayers.Class.inherit = function () {
var superClass = arguments[0];
var proto = new superClass(OpenLayers.Class.isPrototype);
- for (var i = 1; i < arguments.length; i++) {
+ for (var i=1, len=arguments.length; i<len; i++) {
if (typeof arguments[i] == "function") {
var mixin = arguments[i];
arguments[i] = new mixin(OpenLayers.Class.isPrototype);
@@ -893,7 +938,7 @@
OpenLayers.Util.getElement = function() {
var elements = [];
- for (var i = 0; i < arguments.length; i++) {
+ for (var i=0, len=arguments.length; i<len; i++) {
var element = arguments[i];
if (typeof element == 'string') {
element = document.getElementById(element);
@@ -927,7 +972,8 @@
* {Object} The destination object.
*/
OpenLayers.Util.extend = function(destination, source) {
- if(destination && source) {
+ destination = destination || {};
+ if(source) {
for(var property in source) {
var value = source[property];
if(value !== undefined) {
@@ -1012,7 +1058,7 @@
*/
OpenLayers.Util.indexOf = function(array, obj) {
- for(var i=0; i < array.length; i++) {
+ for(var i=0, len=array.length; i<len; i++) {
if (array[i] == obj) {
return i;
}
@@ -1075,10 +1121,9 @@
* Function: createDiv
* Creates a new div and optionally set some standard attributes.
* Null may be passed to each parameter if you do not wish to
- * set a particular attribute.d
+ * set a particular attribute.
+ * Note - zIndex is NOT set on the resulting div.
*
- * Note: zIndex is NOT set
- *
* Parameters:
* id - {String} An identifier for this element. If no id is
* passed an identifier will be created
@@ -1241,8 +1286,27 @@
*/
OpenLayers.Util.onImageLoadError = function() {
this._attempts = (this._attempts) ? (this._attempts + 1) : 1;
- if(this._attempts <= OpenLayers.IMAGE_RELOAD_ATTEMPTS) {
- this.src = this.src;
+ if (this._attempts <= OpenLayers.IMAGE_RELOAD_ATTEMPTS) {
+ var urls = this.urls;
+ if (urls && urls instanceof Array && urls.length > 1){
+ var src = this.src.toString();
+ var current_url, k;
+ for (k = 0; current_url = urls[k]; k++){
+ if(src.indexOf(current_url) != -1){
+ break;
+ }
+ }
+ var guess = Math.floor(urls.length * Math.random())
+ var new_url = urls[guess];
+ k = 0;
+ while(new_url == current_url && k++ < 4){
+ guess = Math.floor(urls.length * Math.random())
+ new_url = urls[guess];
+ }
+ this.src = src.replace(current_url, new_url);
+ } else {
+ this.src = this.src;
+ }
} else {
this.style.backgroundColor = OpenLayers.Util.onImageLoadErrorColor;
}
@@ -1294,8 +1358,8 @@
position, border, sizing,
opacity) {
- OpenLayers.Util.modifyDOMElement(div, id, px, sz,
- null, null, null, opacity);
+ OpenLayers.Util.modifyDOMElement(div, id, px, sz, position,
+ null, null, opacity);
var img = div.childNodes[0];
@@ -1306,8 +1370,9 @@
"relative", border);
if (OpenLayers.Util.alphaHack()) {
-
- div.style.display = "inline-block";
+ if(div.style.display != "none") {
+ div.style.display = "inline-block";
+ }
if (sizing == null) {
sizing = "scale";
}
@@ -1400,7 +1465,7 @@
* in place and returned by this function.
*/
OpenLayers.Util.applyDefaults = function (to, from) {
-
+ to = to || {};
/*
* FF/Windows < 2.0.0.13 reports "Illegal operation on WrappedNative
* prototype object" when calling hawOwnProperty if the source object is an
@@ -1453,7 +1518,7 @@
if (typeof value == 'object' && value.constructor == Array) {
/* value is an array; encode items and separate with "," */
var encodedItemArray = [];
- for (var itemIndex=0; itemIndex<value.length; itemIndex++) {
+ for (var itemIndex=0, len=value.length; itemIndex<len; itemIndex++) {
encodedItemArray.push(encodeURIComponent(value[itemIndex]));
}
encodedValue = encodedItemArray.join(",");
@@ -1504,7 +1569,7 @@
OpenLayers.Util.Try = function() {
var returnValue = null;
- for (var i = 0; i < arguments.length; i++) {
+ for (var i=0, len=arguments.length; i<len; i++) {
var lambda = arguments[i];
try {
returnValue = lambda();
@@ -1553,7 +1618,7 @@
*/
OpenLayers.Util._getNodes=function(nodes, tagName) {
var retArray = [];
- for (var i=0;i<nodes.length;i++) {
+ for (var i=0, len=nodes.length; i<len; i++) {
if (nodes[i].nodeName==tagName) {
retArray.push(nodes[i]);
}
@@ -1729,7 +1794,7 @@
var parameters = {};
var pairs = paramsString.split(/[&;]/);
- for(var i = 0; i < pairs.length; ++i) {
+ for(var i=0, len=pairs.length; i<len; ++i) {
var keyValue = pairs[i].split('=');
if (keyValue[0]) {
var key = decodeURIComponent(keyValue[0]);
@@ -1737,7 +1802,7 @@
//decode individual values
value = value.split(",");
- for(var j=0; j < value.length; j++) {
+ for(var j=0, jlen=value.length; j<jlen; j++) {
value[j] = decodeURIComponent(value[j]);
}
@@ -1924,12 +1989,7 @@
while(element) {
if(element == document.body) {
- // FIXME: IE, when passed 'window' as the forElement, treats it as
- // equal to document.body, but window.style fails, so getStyle
- // fails, so we are paranoid and check this here. This check should
- // probably move into element.getStyle in 2.6.
- if(child && child.style &&
- OpenLayers.Element.getStyle(child, 'position') == 'absolute') {
+ if(OpenLayers.Element.getStyle(child, 'position') == 'absolute') {
break;
}
}
@@ -1999,7 +2059,7 @@
//compare all keys (host, port, etc)
for(var key in urlObj1) {
if (options.test) {
- alert(key + "\n1:" + urlObj1[key] + "\n2:" + urlObj2[key]);
+ OpenLayers.Console.userError(key + "\n1:" + urlObj1[key] + "\n2:" + urlObj2[key]);
}
var val1 = urlObj1[key];
var val2 = urlObj2[key];
@@ -2232,17 +2292,21 @@
* scrollbars do not flicker
*
* Parameters:
+ * contentHTML
* size - {<OpenLayers.Size>} If either the 'w' or 'h' properties is
* specified, we fix that dimension of the div to be measured. This is
* useful in the case where we have a limit in one dimension and must
* therefore meaure the flow in the other dimension.
+ * options - {Object}
+ * displayClass - {String} Optional parameter. A CSS class name(s) string
+ * to provide the CSS context of the rendered content.
*
* Returns:
* {OpenLayers.Size}
*/
-OpenLayers.Util.getRenderedDimensions = function(contentHTML, size) {
+OpenLayers.Util.getRenderedDimensions = function(contentHTML, size, options) {
- var w = h = null;
+ var w, h;
// create temp container div with restricted size
var container = document.createElement("div");
@@ -2253,11 +2317,18 @@
//fix a dimension, if specified.
if (size) {
if (size.w) {
- w = container.style.width = size.w;
+ w = size.w;
+ container.style.width = w + "px";
} else if (size.h) {
- h = container.style.height = size.h;
+ h = size.h
+ container.style.height = h + "px";
}
}
+
+ //add css classes, if specified
+ if (options && options.displayClass) {
+ container.className = options.displayClass;
+ }
// create temp content div and assign content
var content = document.createElement("div");
@@ -2352,1003 +2423,340 @@
return scrollbarWidth;
};
/* ======================================================================
- OpenLayers/Ajax.js
+ Rico/Corner.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. */
-
-
-OpenLayers.ProxyHost = "";
-//OpenLayers.ProxyHost = "examples/proxy.cgi?url=";
-
-/**
- * Ajax reader for OpenLayers
- *
- * @uri url to do remote XML http get
- * @param {String} 'get' format params (x=y&a=b...)
- * @who object to handle callbacks for this request
- * @complete the function to be called on success
- * @failure the function to be called on failure
+/*
+ * This file has been edited substantially from the Rico-released
+ * version by the OpenLayers development team.
*
- * example usage from a caller:
+ * Copyright 2005 Sabre Airline Solutions
*
- * caps: function(request) {
- * -blah-
- * },
+ * 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
*
- * OpenLayers.loadURL(url,params,this,caps);
+ * 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.
*
- * Notice the above example does not provide an error handler; a default empty
- * handler is provided which merely logs the error if a failure handler is not
- * supplied
- *
- */
+ */
+OpenLayers.Rico = new Object();
+OpenLayers.Rico.Corner = {
+ round: function(e, options) {
+ e = OpenLayers.Util.getElement(e);
+ this._setOptions(options);
-/**
-* @param {} request
-*/
-OpenLayers.nullHandler = function(request) {
- alert(OpenLayers.i18n("unhandledRequest", {'statusText':request.statusText}));
-};
-
-/**
- * Function: loadURL
- * Background load a document.
- *
- * Parameters:
- * uri - {String} URI of source doc
- * params - {String} Params on get (doesnt seem to work)
- * caller - {Object} object which gets callbacks
- * onComplete - {Function} Optional callback for success. The callback
- * will be called with this set to caller and will receive the request
- * object as an argument.
- * onFailure - {Function} Optional callback for failure. In the event of
- * a failure, the callback will be called with this set to caller and will
- * receive the request object as an argument.
- *
- * Returns:
- * {XMLHttpRequest} The request object. To abort loading, call
- * request.abort().
- */
-OpenLayers.loadURL = function(uri, params, caller,
- onComplete, onFailure) {
-
- var success = (onComplete) ? OpenLayers.Function.bind(onComplete, caller)
- : OpenLayers.nullHandler;
-
- var failure = (onFailure) ? OpenLayers.Function.bind(onFailure, caller)
- : OpenLayers.nullHandler;
-
- // from prototype.js
- var request = new OpenLayers.Ajax.Request(
- uri,
- {
- method: 'get',
- parameters: params,
- onComplete: success,
- onFailure: failure
+ var color = this.options.color;
+ if ( this.options.color == "fromElement" ) {
+ color = this._background(e);
}
- );
- return request.transport;
-};
-
-/**
- * Function: parseXMLString
- * Parse XML into a doc structure
- *
- * Parameters:
- * text - {String}
- *
- * Returns:
- * {?} Parsed AJAX Responsev
- */
-OpenLayers.parseXMLString = function(text) {
-
- //MS sucks, if the server is bad it dies
- var index = text.indexOf('<');
- if (index > 0) {
- text = text.substring(index);
- }
-
- var ajaxResponse = OpenLayers.Util.Try(
- function() {
- var xmldom = new ActiveXObject('Microsoft.XMLDOM');
- xmldom.loadXML(text);
- return xmldom;
- },
- function() {
- return new DOMParser().parseFromString(text, 'text/xml');
- },
- function() {
- var req = new XMLHttpRequest();
- req.open("GET", "data:" + "text/xml" +
- ";charset=utf-8," + encodeURIComponent(text), false);
- if (req.overrideMimeType) {
- req.overrideMimeType("text/xml");
- }
- req.send(null);
- return req.responseXML;
+ var bgColor = this.options.bgColor;
+ if ( this.options.bgColor == "fromParent" ) {
+ bgColor = this._background(e.offsetParent);
}
- );
-
- return ajaxResponse;
-};
-
-
-/**
- * Namespace: OpenLayers.Ajax
- */
-OpenLayers.Ajax = {
-
- /**
- * Method: emptyFunction
- */
- emptyFunction: function () {},
-
- /**
- * Method: getTransport
- *
- * Returns:
- * {Object} Transport mechanism for whichever browser we're in, or false if
- * none available.
- */
- getTransport: function() {
- return OpenLayers.Util.Try(
- function() {return new XMLHttpRequest();},
- function() {return new ActiveXObject('Msxml2.XMLHTTP');},
- function() {return new ActiveXObject('Microsoft.XMLHTTP');}
- ) || false;
+ this._roundCornersImpl(e, color, bgColor);
},
- /**
- * Property: activeRequestCount
- * {Integer}
- */
- activeRequestCount: 0
-};
+ /** This is a helper function to change the background
+ * color of <div> that has had Rico rounded corners added.
+ *
+ * It seems we cannot just set the background color for the
+ * outer <div> so each <span> element used to create the
+ * corners must have its background color set individually.
+ *
+ * @param {DOM} theDiv - A child of the outer <div> that was
+ * supplied to the `round` method.
+ *
+ * @param {String} newColor - The new background color to use.
+ */
+ changeColor: function(theDiv, newColor) {
+
+ theDiv.style.backgroundColor = newColor;
-/**
- * Namespace: OpenLayers.Ajax.Responders
- * {Object}
- */
-OpenLayers.Ajax.Responders = {
-
- /**
- * Property: responders
- * {Array}
- */
- responders: [],
-
- /**
- * Method: register
- *
- * Parameters:
- * responderToAdd - {?}
- */
- register: function(responderToAdd) {
- for (var i = 0; i < this.responders.length; i++){
- if (responderToAdd == this.responders[i]){
- return;
- }
- }
- this.responders.push(responderToAdd);
- },
-
- /**
- * Method: unregister
- *
- * Parameters:
- * responderToRemove - {?}
- */
- unregister: function(responderToRemove) {
- OpenLayers.Util.removeItem(this.reponders, responderToRemove);
- },
-
- /**
- * Method: dispatch
- *
- * Parameters:
- * callback - {?}
- * request - {?}
- * transport - {?}
- */
- dispatch: function(callback, request, transport) {
- var responder;
- for (var i = 0; i < this.responders.length; i++) {
- responder = this.responders[i];
-
- if (responder[callback] &&
- typeof responder[callback] == 'function') {
- try {
- responder[callback].apply(responder,
- [request, transport]);
- } catch (e) {}
- }
- }
- }
-};
-
-OpenLayers.Ajax.Responders.register({
- /**
- * Function: onCreate
- */
- onCreate: function() {
- OpenLayers.Ajax.activeRequestCount++;
- },
-
- /**
- * Function: onComplete
- */
- onComplete: function() {
- OpenLayers.Ajax.activeRequestCount--;
- }
-});
-
-/**
- * Class: OpenLayers.Ajax.Base
- */
-OpenLayers.Ajax.Base = OpenLayers.Class({
-
- /**
- * Constructor: OpenLayers.Ajax.Base
- *
- * Parameters:
- * options - {Object}
- */
- initialize: function(options) {
- this.options = {
- method: 'post',
- asynchronous: true,
- contentType: 'application/xml',
- parameters: ''
- };
- OpenLayers.Util.extend(this.options, options || {});
+ var spanElements = theDiv.parentNode.getElementsByTagName("span");
- this.options.method = this.options.method.toLowerCase();
-
- if (typeof this.options.parameters == 'string') {
- this.options.parameters =
- OpenLayers.Util.getParameters(this.options.parameters);
+ for (var currIdx = 0; currIdx < spanElements.length; currIdx++) {
+ spanElements[currIdx].style.backgroundColor = newColor;
}
- }
-});
+ },
-/**
- * Class: OpenLayers.Ajax.Request
- *
- * Inherit:
- * - <OpenLayers.Ajax.Base>
- */
-OpenLayers.Ajax.Request = OpenLayers.Class(OpenLayers.Ajax.Base, {
- /**
- * Property: _complete
- *
- * {Boolean}
- */
- _complete: false,
-
- /**
- * Constructor: OpenLayers.Ajax.Request
- *
- * Parameters:
- * url - {String}
- * options - {Object}
- */
- initialize: function(url, options) {
- OpenLayers.Ajax.Base.prototype.initialize.apply(this, [options]);
+ /** This is a helper function to change the background
+ * opacity of <div> that has had Rico rounded corners added.
+ *
+ * See changeColor (above) for algorithm explanation
+ *
+ * @param {DOM} theDiv A child of the outer <div> that was
+ * supplied to the `round` method.
+ *
+ * @param {int} newOpacity The new opacity to use (0-1).
+ */
+ changeOpacity: function(theDiv, newOpacity) {
+
+ var mozillaOpacity = newOpacity;
+ var ieOpacity = 'alpha(opacity=' + newOpacity * 100 + ')';
- if (OpenLayers.ProxyHost && OpenLayers.String.startsWith(url, "http")) {
- url = OpenLayers.ProxyHost + encodeURIComponent(url);
- }
-
- this.transport = OpenLayers.Ajax.getTransport();
- this.request(url);
- },
+ theDiv.style.opacity = mozillaOpacity;
+ theDiv.style.filter = ieOpacity;
- /**
- * Method: request
- *
- * Parameters:
- * url - {String}
- */
- request: function(url) {
- this.url = url;
- this.method = this.options.method;
- var params = OpenLayers.Util.extend({}, this.options.parameters);
+ var spanElements = theDiv.parentNode.getElementsByTagName("span");
- if (this.method != 'get' && this.method != 'post') {
- // simulate other verbs over post
- params['_method'] = this.method;
- this.method = 'post';
+ for (var currIdx = 0; currIdx < spanElements.length; currIdx++) {
+ spanElements[currIdx].style.opacity = mozillaOpacity;
+ spanElements[currIdx].style.filter = ieOpacity;
}
- this.parameters = params;
-
- if (params = OpenLayers.Util.getParameterString(params)) {
- // when GET, append parameters to URL
- if (this.method == 'get') {
- this.url += ((this.url.indexOf('?') > -1) ? '&' : '?') + params;
- } else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
- params += '&_=';
- }
- }
- try {
- var response = new OpenLayers.Ajax.Response(this);
- if (this.options.onCreate) {
- this.options.onCreate(response);
- }
-
- OpenLayers.Ajax.Responders.dispatch('onCreate',
- this,
- response);
-
- this.transport.open(this.method.toUpperCase(),
- this.url,
- this.options.asynchronous);
-
- if (this.options.asynchronous) {
- window.setTimeout(
- OpenLayers.Function.bind(this.respondToReadyState, this, 1),
- 10);
- }
-
- this.transport.onreadystatechange =
- OpenLayers.Function.bind(this.onStateChange, this);
- this.setRequestHeaders();
-
- this.body = this.method == 'post' ?
- (this.options.postBody || params) : null;
- this.transport.send(this.body);
-
- // Force Firefox to handle ready state 4 for synchronous requests
- if (!this.options.asynchronous &&
- this.transport.overrideMimeType) {
- this.onStateChange();
- }
- } catch (e) {
- this.dispatchException(e);
- }
},
- /**
- * Method: onStateChange
- */
- onStateChange: function() {
- var readyState = this.transport.readyState;
- if (readyState > 1 && !((readyState == 4) && this._complete)) {
- this.respondToReadyState(this.transport.readyState);
- }
- },
-
- /**
- * Method: setRequestHeaders
- */
- setRequestHeaders: function() {
- var headers = {
- 'X-Requested-With': 'XMLHttpRequest',
- 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*',
- 'OpenLayers': true
- };
+ /** this function takes care of redoing the rico cornering
+ *
+ * you can't just call updateRicoCorners() again and pass it a
+ * new options string. you have to first remove the divs that
+ * rico puts on top and below the content div.
+ *
+ * @param {DOM} theDiv - A child of the outer <div> that was
+ * supplied to the `round` method.
+ *
+ * @param {Object} options - list of options
+ */
+ reRound: function(theDiv, options) {
- if (this.method == 'post') {
- headers['Content-type'] = this.options.contentType +
- (this.options.encoding ? '; charset=' + this.options.encoding : '');
-
- /* Force "Connection: close" for older Mozilla browsers to work
- * around a bug where XMLHttpRequest sends an incorrect
- * Content-length header. See Mozilla Bugzilla #246651.
- */
- if (this.transport.overrideMimeType &&
- (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005) {
- headers['Connection'] = 'close';
- }
- }
- // user-defined headers
- if (typeof this.options.requestHeaders == 'object') {
- var extras = this.options.requestHeaders;
-
- if (typeof extras.push == 'function') {
- for (var i = 0, length = extras.length; i < length; i += 2) {
- headers[extras[i]] = extras[i+1];
- }
- } else {
- for (var i in extras) {
- headers[i] = pair[i];
- }
- }
- }
-
- for (var name in headers) {
- this.transport.setRequestHeader(name, headers[name]);
- }
- },
-
- /**
- * Method: success
- *
- * Returns:
- * {Boolean} -
- */
- success: function() {
- var status = this.getStatus();
- return !status || (status >=200 && status < 300);
- },
-
- /**
- * Method: getStatus
- *
- * Returns:
- * {Integer} - Status
- */
- getStatus: function() {
- try {
- return this.transport.status || 0;
- } catch (e) {
- return 0;
- }
- },
+ var topRico = theDiv.parentNode.childNodes[0];
+ //theDiv would be theDiv.parentNode.childNodes[1]
+ var bottomRico = theDiv.parentNode.childNodes[2];
+
+ theDiv.parentNode.removeChild(topRico);
+ theDiv.parentNode.removeChild(bottomRico);
- /**
- * Method: respondToReadyState
- *
- * Parameters:
- * readyState - {?}
- */
- respondToReadyState: function(readyState) {
- var state = OpenLayers.Ajax.Request.Events[readyState];
- var response = new OpenLayers.Ajax.Response(this);
-
- if (state == 'Complete') {
- try {
- this._complete = true;
- (this.options['on' + response.status] ||
- this.options['on' + (this.success() ? 'Success' : 'Failure')] ||
- OpenLayers.Ajax.emptyFunction)(response);
- } catch (e) {
- this.dispatchException(e);
- }
-
- var contentType = response.getHeader('Content-type');
- }
-
- try {
- (this.options['on' + state] ||
- OpenLayers.Ajax.emptyFunction)(response);
- OpenLayers.Ajax.Responders.dispatch('on' + state,
- this,
- response);
- } catch (e) {
- this.dispatchException(e);
- }
-
- if (state == 'Complete') {
- // avoid memory leak in MSIE: clean up
- this.transport.onreadystatechange = OpenLayers.Ajax.emptyFunction;
- }
- },
-
- /**
- * Method: getHeader
- *
- * Parameters:
- * name - {String} Header name
- *
- * Returns:
- * {?} - response header for the given name
- */
- getHeader: function(name) {
- try {
- return this.transport.getResponseHeader(name);
- } catch (e) {
- return null;
- }
- },
+ this.round(theDiv.parentNode, options);
+ },
- /**
- * Method: dispatchException
- * If the optional onException function is set, execute it
- * and then dispatch the call to any other listener registered
- * for onException.
- *
- * If no optional onException function is set, we suspect that
- * the user may have also not used
- * OpenLayers.Ajax.Responders.register to register a listener
- * for the onException call. To make sure that something
- * gets done with this exception, only dispatch the call if there
- * are listeners.
- *
- * If you explicitly want to swallow exceptions, set
- * request.options.onException to an empty function (function(){})
- * or register an empty function with <OpenLayers.Ajax.Responders>
- * for onException.
- *
- * Parameters:
- * exception - {?}
- */
- dispatchException: function(exception) {
- var handler = this.options.onException;
- if(handler) {
- // call options.onException and alert any other listeners
- handler(this, exception);
- OpenLayers.Ajax.Responders.dispatch('onException', this, exception);
- } else {
- // check if there are any other listeners
- var listener = false;
- var responders = OpenLayers.Ajax.Responders.responders;
- for (var i = 0; i < responders.length; i++) {
- if(responders[i].onException) {
- listener = true;
- break;
- }
- }
- if(listener) {
- // call all listeners
- OpenLayers.Ajax.Responders.dispatch('onException', this, exception);
- } else {
- // let the exception through
- throw exception;
- }
- }
- }
-});
+ _roundCornersImpl: function(e, color, bgColor) {
+ if(this.options.border) {
+ this._renderBorder(e,bgColor);
+ }
+ if(this._isTopRounded()) {
+ this._roundTopCorners(e,color,bgColor);
+ }
+ if(this._isBottomRounded()) {
+ this._roundBottomCorners(e,color,bgColor);
+ }
+ },
-/**
- * Property: Events
- * {Array(String)}
- */
-OpenLayers.Ajax.Request.Events =
- ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
+ _renderBorder: function(el,bgColor) {
+ var borderValue = "1px solid " + this._borderColor(bgColor);
+ var borderL = "border-left: " + borderValue;
+ var borderR = "border-right: " + borderValue;
+ var style = "style='" + borderL + ";" + borderR + "'";
+ el.innerHTML = "<div " + style + ">" + el.innerHTML + "</div>";
+ },
-/**
- * Class: OpenLayers.Ajax.Response
- */
-OpenLayers.Ajax.Response = OpenLayers.Class({
+ _roundTopCorners: function(el, color, bgColor) {
+ var corner = this._createCorner(bgColor);
+ for(var i=0 ; i < this.options.numSlices ; i++ ) {
+ corner.appendChild(this._createCornerSlice(color,bgColor,i,"top"));
+ }
+ el.style.paddingTop = 0;
+ el.insertBefore(corner,el.firstChild);
+ },
- /**
- * Property: status
- *
- * {Integer}
- */
- status: 0,
-
+ _roundBottomCorners: function(el, color, bgColor) {
+ var corner = this._createCorner(bgColor);
+ for(var i=(this.options.numSlices-1) ; i >= 0 ; i-- ) {
+ corner.appendChild(this._createCornerSlice(color,bgColor,i,"bottom"));
+ }
+ el.style.paddingBottom = 0;
+ el.appendChild(corner);
+ },
- /**
- * Property: statusText
- *
- * {String}
- */
- statusText: '',
-
- /**
- * Constructor: OpenLayers.Ajax.Response
- *
- * Parameters:
- * request - {Object}
- */
- initialize: function(request) {
- this.request = request;
- var transport = this.transport = request.transport,
- readyState = this.readyState = transport.readyState;
-
- if ((readyState > 2 &&
- !(!!(window.attachEvent && !window.opera))) ||
- readyState == 4) {
- this.status = this.getStatus();
- this.statusText = this.getStatusText();
- this.responseText = transport.responseText == null ?
- '' : String(transport.responseText);
- }
-
- if(readyState == 4) {
- var xml = transport.responseXML;
- this.responseXML = xml === undefined ? null : xml;
- }
- },
-
- /**
- * Method: getStatus
- */
- getStatus: OpenLayers.Ajax.Request.prototype.getStatus,
-
- /**
- * Method: getStatustext
- *
- * Returns:
- * {String} - statusText
- */
- getStatusText: function() {
- try {
- return this.transport.statusText || '';
- } catch (e) {
- return '';
- }
- },
-
- /**
- * Method: getHeader
- */
- getHeader: OpenLayers.Ajax.Request.prototype.getHeader,
-
- /**
- * Method: getResponseHeader
- *
- * Returns:
- * {?} - response header for given name
- */
- getResponseHeader: function(name) {
- return this.transport.getResponseHeader(name);
- }
-});
+ _createCorner: function(bgColor) {
+ var corner = document.createElement("div");
+ corner.style.backgroundColor = (this._isTransparent() ? "transparent" : bgColor);
+ return corner;
+ },
+ _createCornerSlice: function(color,bgColor, n, position) {
+ var slice = document.createElement("span");
-/**
- * Function: getElementsByTagNameNS
- *
- * Parameters:
- * parentnode - {?}
- * nsuri - {?}
- * nsprefix - {?}
- * tagname - {?}
- *
- * Returns:
- * {?}
- */
-OpenLayers.Ajax.getElementsByTagNameNS = function(parentnode, nsuri,
- nsprefix, tagname) {
- var elem = null;
- if (parentnode.getElementsByTagNameNS) {
- elem = parentnode.getElementsByTagNameNS(nsuri, tagname);
- } else {
- elem = parentnode.getElementsByTagName(nsprefix + ':' + tagname);
- }
- return elem;
-};
+ var inStyle = slice.style;
+ inStyle.backgroundColor = color;
+ inStyle.display = "block";
+ inStyle.height = "1px";
+ inStyle.overflow = "hidden";
+ inStyle.fontSize = "1px";
+ var borderColor = this._borderColor(color,bgColor);
+ if ( this.options.border && n == 0 ) {
+ inStyle.borderTopStyle = "solid";
+ inStyle.borderTopWidth = "1px";
+ inStyle.borderLeftWidth = "0px";
+ inStyle.borderRightWidth = "0px";
+ inStyle.borderBottomWidth = "0px";
+ inStyle.height = "0px"; // assumes css compliant box model
+ inStyle.borderColor = borderColor;
+ }
+ else if(borderColor) {
+ inStyle.borderColor = borderColor;
+ inStyle.borderStyle = "solid";
+ inStyle.borderWidth = "0px 1px";
+ }
-/**
- * Function: serializeXMLToString
- * Wrapper function around XMLSerializer, which doesn't exist/work in
- * IE/Safari. We need to come up with a way to serialize in those browser:
- * for now, these browsers will just fail. #535, #536
- *
- * Parameters:
- * xmldom {XMLNode} xml dom to serialize
- *
- * Returns:
- * {?}
- */
-OpenLayers.Ajax.serializeXMLToString = function(xmldom) {
- var serializer = new XMLSerializer();
- var data = serializer.serializeToString(xmldom);
- return data;
-};
-/* ======================================================================
- OpenLayers/Console.js
- ====================================================================== */
+ if ( !this.options.compact && (n == (this.options.numSlices-1)) ) {
+ inStyle.height = "2px";
+ }
+ this._setMargin(slice, n, position);
+ this._setBorder(slice, n, position);
+ return slice;
+ },
-/* 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. */
+ _setOptions: function(options) {
+ this.options = {
+ corners : "all",
+ color : "fromElement",
+ bgColor : "fromParent",
+ blend : true,
+ border : false,
+ compact : false
+ };
+ OpenLayers.Util.extend(this.options, options || {});
-/**
- * Namespace: OpenLayers.Console
- * The OpenLayers.Console namespace is used for debugging and error logging.
- * If the Firebug Lite (../Firebug/firebug.js) is included before this script,
- * calls to OpenLayers.Console methods will get redirected to window.console.
- * This makes use of the Firebug extension where available and allows for
- * cross-browser debugging Firebug style.
- *
- * Note:
- * Note that behavior will differ with the Firebug extention and Firebug Lite.
- * Most notably, the Firebug Lite console does not currently allow for
- * hyperlinks to code or for clicking on object to explore their properties.
- *
- */
-OpenLayers.Console = {
- /**
- * Create empty functions for all console methods. The real value of these
- * properties will be set if Firebug Lite (../Firebug/firebug.js script) is
- * included. We explicitly require the Firebug Lite script to trigger
- * functionality of the OpenLayers.Console methods.
- */
-
- /**
- * APIFunction: log
- * Log an object in the console. The Firebug Lite console logs string
- * representation of objects. Given multiple arguments, they will
- * be cast to strings and logged with a space delimiter. If the first
- * argument is a string with printf-like formatting, subsequent arguments
- * will be used in string substitution. Any additional arguments (beyond
- * the number substituted in a format string) will be appended in a space-
- * delimited line.
- *
- * Parameters:
- * object - {Object}
- */
- log: function() {},
+ this.options.numSlices = this.options.compact ? 2 : 4;
+ if ( this._isTransparent() ) {
+ this.options.blend = false;
+ }
+ },
- /**
- * APIFunction: debug
- * Writes a message to the console, including a hyperlink to the line
- * where it was called.
- *
- * May be called with multiple arguments as with OpenLayers.Console.log().
- *
- * Parameters:
- * object - {Object}
- */
- debug: function() {},
+ _whichSideTop: function() {
+ if ( this._hasString(this.options.corners, "all", "top") ) {
+ return "";
+ }
+ if ( this.options.corners.indexOf("tl") >= 0 && this.options.corners.indexOf("tr") >= 0 ) {
+ return "";
+ }
+ if (this.options.corners.indexOf("tl") >= 0) {
+ return "left";
+ } else if (this.options.corners.indexOf("tr") >= 0) {
+ return "right";
+ }
+ return "";
+ },
- /**
- * APIFunction: info
- * Writes a message to the console with the visual "info" icon and color
- * coding and a hyperlink to the line where it was called.
- *
- * May be called with multiple arguments as with OpenLayers.Console.log().
- *
- * Parameters:
- * object - {Object}
- */
- info: function() {},
+ _whichSideBottom: function() {
+ if ( this._hasString(this.options.corners, "all", "bottom") ) {
+ return "";
+ }
+ if ( this.options.corners.indexOf("bl")>=0 && this.options.corners.indexOf("br")>=0 ) {
+ return "";
+ }
- /**
- * APIFunction: warn
- * Writes a message to the console with the visual "warning" icon and
- * color coding and a hyperlink to the line where it was called.
- *
- * May be called with multiple arguments as with OpenLayers.Console.log().
- *
- * Parameters:
- * object - {Object}
- */
- warn: function() {},
+ if(this.options.corners.indexOf("bl") >=0) {
+ return "left";
+ } else if(this.options.corners.indexOf("br")>=0) {
+ return "right";
+ }
+ return "";
+ },
- /**
- * APIFunction: error
- * Writes a message to the console with the visual "error" icon and color
- * coding and a hyperlink to the line where it was called.
- *
- * May be called with multiple arguments as with OpenLayers.Console.log().
- *
- * Parameters:
- * object - {Object}
- */
- error: function() {},
+ _borderColor : function(color,bgColor) {
+ if ( color == "transparent" ) {
+ return bgColor;
+ } else if ( this.options.border ) {
+ return this.options.border;
+ } else if ( this.options.blend ) {
+ return this._blend( bgColor, color );
+ } else {
+ return "";
+ }
+ },
- /**
- * APIFunction: assert
- * Tests that an expression is true. If not, it will write a message to
- * the console and throw an exception.
- *
- * May be called with multiple arguments as with OpenLayers.Console.log().
- *
- * Parameters:
- * object - {Object}
- */
- assert: function() {},
- /**
- * APIFunction: dir
- * Prints an interactive listing of all properties of the object. This
- * looks identical to the view that you would see in the DOM tab.
- *
- * Parameters:
- * object - {Object}
- */
- dir: function() {},
+ _setMargin: function(el, n, corners) {
+ var marginSize = this._marginSize(n);
+ var whichSide = corners == "top" ? this._whichSideTop() : this._whichSideBottom();
- /**
- * APIFunction: dirxml
- * Prints the XML source tree of an HTML or XML element. This looks
- * identical to the view that you would see in the HTML tab. You can click
- * on any node to inspect it in the HTML tab.
- *
- * Parameters:
- * object - {Object}
- */
- dirxml: function() {},
+ if ( whichSide == "left" ) {
+ el.style.marginLeft = marginSize + "px"; el.style.marginRight = "0px";
+ }
+ else if ( whichSide == "right" ) {
+ el.style.marginRight = marginSize + "px"; el.style.marginLeft = "0px";
+ }
+ else {
+ el.style.marginLeft = marginSize + "px"; el.style.marginRight = marginSize + "px";
+ }
+ },
- /**
- * APIFunction: trace
- * Prints an interactive stack trace of JavaScript execution at the point
- * where it is called. The stack trace details the functions on the stack,
- * as well as the values that were passed as arguments to each function.
- * You can click each function to take you to its source in the Script tab,
- * and click each argument value to inspect it in the DOM or HTML tabs.
- *
- */
- trace: function() {},
+ _setBorder: function(el,n,corners) {
+ var borderSize = this._borderSize(n);
+ var whichSide = corners == "top" ? this._whichSideTop() : this._whichSideBottom();
+ if ( whichSide == "left" ) {
+ el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = "0px";
+ }
+ else if ( whichSide == "right" ) {
+ el.style.borderRightWidth = borderSize + "px"; el.style.borderLeftWidth = "0px";
+ }
+ else {
+ el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = borderSize + "px";
+ }
+ if (this.options.border != false) {
+ el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = borderSize + "px";
+ }
+ },
- /**
- * APIFunction: group
- * Writes a message to the console and opens a nested block to indent all
- * future messages sent to the console. Call OpenLayers.Console.groupEnd()
- * to close the block.
- *
- * May be called with multiple arguments as with OpenLayers.Console.log().
- *
- * Parameters:
- * object - {Object}
- */
- group: function() {},
+ _marginSize: function(n) {
+ if ( this._isTransparent() ) {
+ return 0;
+ }
+ var marginSizes = [ 5, 3, 2, 1 ];
+ var blendedMarginSizes = [ 3, 2, 1, 0 ];
+ var compactMarginSizes = [ 2, 1 ];
+ var smBlendedMarginSizes = [ 1, 0 ];
- /**
- * APIFunction: groupEnd
- * Closes the most recently opened block created by a call to
- * OpenLayers.Console.group
- */
- groupEnd: function() {},
-
- /**
- * APIFunction: time
- * Creates a new timer under the given name. Call
- * OpenLayers.Console.timeEnd(name)
- * with the same name to stop the timer and print the time elapsed.
- *
- * Parameters:
- * name - {String}
- */
- time: function() {},
+ if ( this.options.compact && this.options.blend ) {
+ return smBlendedMarginSizes[n];
+ } else if ( this.options.compact ) {
+ return compactMarginSizes[n];
+ } else if ( this.options.blend ) {
+ return blendedMarginSizes[n];
+ } else {
+ return marginSizes[n];
+ }
+ },
- /**
- * APIFunction: timeEnd
- * Stops a timer created by a call to OpenLayers.Console.time(name) and
- * writes the time elapsed.
- *
- * Parameters:
- * name - {String}
- */
- timeEnd: function() {},
+ _borderSize: function(n) {
+ var transparentBorderSizes = [ 5, 3, 2, 1 ];
+ var blendedBorderSizes = [ 2, 1, 1, 1 ];
+ var compactBorderSizes = [ 1, 0 ];
+ var actualBorderSizes = [ 0, 2, 0, 0 ];
- /**
- * APIFunction: profile
- * Turns on the JavaScript profiler. The optional argument title would
- * contain the text to be printed in the header of the profile report.
- *
- * This function is not currently implemented in Firebug Lite.
- *
- * Parameters:
- * title - {String} Optional title for the profiler
- */
- profile: function() {},
+ if ( this.options.compact && (this.options.blend || this._isTransparent()) ) {
+ return 1;
+ } else if ( this.options.compact ) {
+ return compactBorderSizes[n];
+ } else if ( this.options.blend ) {
+ return blendedBorderSizes[n];
+ } else if ( this.options.border ) {
+ return actualBorderSizes[n];
+ } else if ( this._isTransparent() ) {
+ return transparentBorderSizes[n];
+ }
+ return 0;
+ },
- /**
- * APIFunction: profileEnd
- * Turns off the JavaScript profiler and prints its report.
- *
- * This function is not currently implemented in Firebug Lite.
- */
- profileEnd: function() {},
-
- /**
- * APIFunction: count
- * Writes the number of times that the line of code where count was called
- * was executed. The optional argument title will print a message in
- * addition to the number of the count.
- *
- * This function is not currently implemented in Firebug Lite.
- *
- * Parameters:
- * title - {String} Optional title to be printed with count
- */
- count: function() {},
-
- CLASS_NAME: "OpenLayers.Console"
+ _hasString: function(str) { for(var i=1 ; i<arguments.length ; i++) if (str.indexOf(arguments[i]) >= 0) { return true; } return false; },
+ _blend: function(c1, c2) { var cc1 = OpenLayers.Rico.Color.createFromHex(c1); cc1.blend(OpenLayers.Rico.Color.createFromHex(c2)); return cc1; },
+ _background: function(el) { try { return OpenLayers.Rico.Color.createColorFromBackground(el).asHex(); } catch(err) { return "#ffffff"; } },
+ _isTransparent: function() { return this.options.color == "transparent"; },
+ _isTopRounded: function() { return this._hasString(this.options.corners, "all", "top", "tl", "tr"); },
+ _isBottomRounded: function() { return this._hasString(this.options.corners, "all", "bottom", "bl", "br"); },
+ _hasSingleTextChild: function(el) { return el.childNodes.length == 1 && el.childNodes[0].nodeType == 3; }
};
-
-/**
- * Execute an anonymous function to extend the OpenLayers.Console namespace
- * if the firebug.js script is included. This closure is used so that the
- * "scripts" and "i" variables don't pollute the global namespace.
- */
-(function() {
- /**
- * If Firebug Lite is included (before this script), re-route all
- * OpenLayers.Console calls to the console object.
- */
- if(window.console) {
- var scripts = document.getElementsByTagName("script");
- for(var i=0; i<scripts.length; ++i) {
- if(scripts[i].src.indexOf("firebug.js") != -1) {
- OpenLayers.Util.extend(OpenLayers.Console, console);
- break;
- }
- }
- }
-})();
/* ======================================================================
- OpenLayers/BaseTypes/Size.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. */
-
-/**
- * Class: OpenLayers.Size
- * Instances of this class represent a width/height pair
- */
-OpenLayers.Size = OpenLayers.Class({
-
- /**
- * APIProperty: w
- * {Number} width
- */
- w: 0.0,
-
- /**
- * APIProperty: h
- * {Number} height
- */
- h: 0.0,
-
-
- /**
- * Constructor: OpenLayers.Size
- * Create an instance of OpenLayers.Size
- *
- * Parameters:
- * w - {Number} width
- * h - {Number} height
- */
- initialize: function(w, h) {
- this.w = parseFloat(w);
- this.h = parseFloat(h);
- },
-
- /**
- * Method: toString
- * Return the string representation of a size object
- *
- * Returns:
- * {String} The string representation of OpenLayers.Size object.
- * (ex. <i>"w=55,h=66"</i>)
- */
- toString:function() {
- return ("w=" + this.w + ",h=" + this.h);
- },
-
- /**
- * APIMethod: clone
- * Create a clone of this size object
- *
- * Returns:
- * {<OpenLayers.Size>} A new OpenLayers.Size object with the same w and h
- * values
- */
- clone:function() {
- return new OpenLayers.Size(this.w, this.h);
- },
-
- /**
- *
- * APIMethod: equals
- * Determine where this size is equal to another
- *
- * Parameters:
- * sz - {<OpenLayers.Size>}
- *
- * Returns:
- * {Boolean} The passed in size has the same h and w properties as this one.
- * Note that if sz passed in is null, returns false.
- *
- */
- equals:function(sz) {
- var equals = false;
- if (sz != null) {
- equals = ((this.w == sz.w && this.h == sz.h) ||
- (isNaN(this.w) && isNaN(this.h) && isNaN(sz.w) && isNaN(sz.h)));
- }
- return equals;
- },
-
- CLASS_NAME: "OpenLayers.Size"
-});
-/* ======================================================================
OpenLayers/BaseTypes/Bounds.js
====================================================================== */
@@ -3574,6 +2982,48 @@
},
/**
+ * Method: scale
+ * Scales the bounds around a pixel or lonlat. Note that the new
+ * bounds may return non-integer properties, even if a pixel
+ * is passed.
+ *
+ * Parameters:
+ * ratio - {Float}
+ * origin - {<OpenLayers.Pixel> or <OpenLayers.LonLat>}
+ * Default is center.
+ *
+ * Returns:
+ * {<OpenLayers.Bound>} A new bounds that is scaled by ratio
+ * from origin.
+ */
+
+ scale: function(ratio, origin){
+ if(origin == null){
+ origin = this.getCenterLonLat();
+ }
+
+ var bounds = [];
+
+ var origx,origy;
+
+ // get origin coordinates
+ if(origin.CLASS_NAME == "OpenLayers.LonLat"){
+ origx = origin.lon;
+ origy = origin.lat;
+ } else {
+ origx = origin.x;
+ origy = origin.y;
+ }
+
+ var left = (this.left - origx) * ratio + origx;
+ 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);
+ },
+
+ /**
* APIMethod: add
*
* Parameters:
@@ -3987,7 +3437,7 @@
* element - {DOMElement} Actually user can pass any number of elements
*/
toggle: function() {
- for (var i = 0; i < arguments.length; i++) {
+ for (var i=0, len=arguments.length; i<len; i++) {
var element = OpenLayers.Util.getElement(arguments[i]);
var display = OpenLayers.Element.visible(element) ? 'hide'
: 'show';
@@ -4004,7 +3454,7 @@
* element - {DOMElement} Actually user can pass any number of elements
*/
hide: function() {
- for (var i = 0; i < arguments.length; i++) {
+ for (var i=0, len=arguments.length; i<len; i++) {
var element = OpenLayers.Util.getElement(arguments[i]);
element.style.display = 'none';
}
@@ -4018,7 +3468,7 @@
* element - {DOMElement} Actually user can pass any number of elements
*/
show: function() {
- for (var i = 0; i < arguments.length; i++) {
+ for (var i=0, len=arguments.length; i<len; i++) {
var element = OpenLayers.Util.getElement(arguments[i]);
element.style.display = '';
}
@@ -4083,6 +3533,86 @@
},
/**
+ * Function: hasClass
+ * Tests if an element has the given CSS class name.
+ *
+ * Parameters:
+ * element - {DOMElement} A DOM element node.
+ * name - {String} The CSS class name to search for.
+ *
+ * Returns:
+ * {Boolean} The element has the given class name.
+ */
+ hasClass: function(element, name) {
+ var names = element.className;
+ return (!!names && new RegExp("(^|\\s)" + name + "(\\s|$)").test(names));
+ },
+
+ /**
+ * Function: addClass
+ * Add a CSS class name to an element. Safe where element already has
+ * the class name.
+ *
+ * Parameters:
+ * element - {DOMElement} A DOM element node.
+ * name - {String} The CSS class name to add.
+ *
+ * Returns:
+ * {DOMElement} The element.
+ */
+ addClass: function(element, name) {
+ if(!OpenLayers.Element.hasClass(element, name)) {
+ element.className += (element.className ? " " : "") + name;
+ }
+ return element;
+ },
+
+ /**
+ * Function: removeClass
+ * Remove a CSS class name from an element. Safe where element does not
+ * have the class name.
+ *
+ * Parameters:
+ * element - {DOMElement} A DOM element node.
+ * name - {String} The CSS class name to remove.
+ *
+ * Returns:
+ * {DOMElement} The element.
+ */
+ removeClass: function(element, name) {
+ var names = element.className;
+ if(names) {
+ element.className = OpenLayers.String.trim(
+ names.replace(
+ new RegExp("(^|\\s+)" + name + "(\\s+|$)"), " "
+ )
+ );
+ }
+ return element;
+ },
+
+ /**
+ * Function: toggleClass
+ * Remove a CSS class name from an element if it exists. Add the class name
+ * if it doesn't exist.
+ *
+ * Parameters:
+ * element - {DOMElement} A DOM element node.
+ * name - {String} The CSS class name to toggle.
+ *
+ * Returns:
+ * {DOMElement} The element.
+ */
+ toggleClass: function(element, name) {
+ if(OpenLayers.Element.hasClass(element, name)) {
+ OpenLayers.Element.removeClass(element, name);
+ } else {
+ OpenLayers.Element.addClass(element, name);
+ }
+ return element;
+ },
+
+ /**
* APIFunction: getStyle
*
* Parameters:
@@ -4094,25 +3624,29 @@
*/
getStyle: function(element, style) {
element = OpenLayers.Util.getElement(element);
- var value = element.style[OpenLayers.String.camelize(style)];
- if (!value) {
- if (document.defaultView &&
- document.defaultView.getComputedStyle) {
-
- var css = document.defaultView.getComputedStyle(element, null);
- value = css ? css.getPropertyValue(style) : null;
- } else if (element.currentStyle) {
- value = element.currentStyle[OpenLayers.String.camelize(style)];
+
+ var value = null;
+ if (element && element.style) {
+ value = element.style[OpenLayers.String.camelize(style)];
+ if (!value) {
+ if (document.defaultView &&
+ document.defaultView.getComputedStyle) {
+
+ var css = document.defaultView.getComputedStyle(element, null);
+ value = css ? css.getPropertyValue(style) : null;
+ } else if (element.currentStyle) {
+ value = element.currentStyle[OpenLayers.String.camelize(style)];
+ }
}
+
+ var positions = ['left', 'top', 'right', 'bottom'];
+ if (window.opera &&
+ (OpenLayers.Util.indexOf(positions,style) != -1) &&
+ (OpenLayers.Element.getStyle(element, 'position') == 'static')) {
+ value = 'auto';
+ }
}
- var positions = ['left', 'top', 'right', 'bottom'];
- if (window.opera &&
- (OpenLayers.Util.indexOf(positions,style) != -1) &&
- (OpenLayers.Element.getStyle(element, 'position') == 'static')) {
- value = 'auto';
- }
-
return value == 'auto' ? null : value;
}
@@ -4237,7 +3771,8 @@
/**
* APIMethod: transform
- * Transform the LonLat object from source to dest.
+ * Transform the LonLat object from source to dest. This transformation is
+ * *in place*: if you want a *new* lonlat, use .clone() first.
*
* Parameters:
* source - {<OpenLayers.Projection>} Source projection.
@@ -4430,6 +3965,343 @@
CLASS_NAME: "OpenLayers.Pixel"
});
/* ======================================================================
+ OpenLayers/BaseTypes/Size.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. */
+
+/**
+ * Class: OpenLayers.Size
+ * Instances of this class represent a width/height pair
+ */
+OpenLayers.Size = OpenLayers.Class({
+
+ /**
+ * APIProperty: w
+ * {Number} width
+ */
+ w: 0.0,
+
+ /**
+ * APIProperty: h
+ * {Number} height
+ */
+ h: 0.0,
+
+
+ /**
+ * Constructor: OpenLayers.Size
+ * Create an instance of OpenLayers.Size
+ *
+ * Parameters:
+ * w - {Number} width
+ * h - {Number} height
+ */
+ initialize: function(w, h) {
+ this.w = parseFloat(w);
+ this.h = parseFloat(h);
+ },
+
+ /**
+ * Method: toString
+ * Return the string representation of a size object
+ *
+ * Returns:
+ * {String} The string representation of OpenLayers.Size object.
+ * (ex. <i>"w=55,h=66"</i>)
+ */
+ toString:function() {
+ return ("w=" + this.w + ",h=" + this.h);
+ },
+
+ /**
+ * APIMethod: clone
+ * Create a clone of this size object
+ *
+ * Returns:
+ * {<OpenLayers.Size>} A new OpenLayers.Size object with the same w and h
+ * values
+ */
+ clone:function() {
+ return new OpenLayers.Size(this.w, this.h);
+ },
+
+ /**
+ *
+ * APIMethod: equals
+ * Determine where this size is equal to another
+ *
+ * Parameters:
+ * sz - {<OpenLayers.Size>}
+ *
+ * Returns:
+ * {Boolean} The passed in size has the same h and w properties as this one.
+ * Note that if sz passed in is null, returns false.
+ *
+ */
+ equals:function(sz) {
+ var equals = false;
+ if (sz != null) {
+ equals = ((this.w == sz.w && this.h == sz.h) ||
+ (isNaN(this.w) && isNaN(this.h) && isNaN(sz.w) && isNaN(sz.h)));
+ }
+ return equals;
+ },
+
+ CLASS_NAME: "OpenLayers.Size"
+});
+/* ======================================================================
+ OpenLayers/Console.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. */
+
+/**
+ * Namespace: OpenLayers.Console
+ * The OpenLayers.Console namespace is used for debugging and error logging.
+ * If the Firebug Lite (../Firebug/firebug.js) is included before this script,
+ * calls to OpenLayers.Console methods will get redirected to window.console.
+ * This makes use of the Firebug extension where available and allows for
+ * cross-browser debugging Firebug style.
+ *
+ * Note:
+ * Note that behavior will differ with the Firebug extention and Firebug Lite.
+ * Most notably, the Firebug Lite console does not currently allow for
+ * hyperlinks to code or for clicking on object to explore their properties.
+ *
+ */
+OpenLayers.Console = {
+ /**
+ * Create empty functions for all console methods. The real value of these
+ * properties will be set if Firebug Lite (../Firebug/firebug.js script) is
+ * included. We explicitly require the Firebug Lite script to trigger
+ * functionality of the OpenLayers.Console methods.
+ */
+
+ /**
+ * APIFunction: log
+ * Log an object in the console. The Firebug Lite console logs string
+ * representation of objects. Given multiple arguments, they will
+ * be cast to strings and logged with a space delimiter. If the first
+ * argument is a string with printf-like formatting, subsequent arguments
+ * will be used in string substitution. Any additional arguments (beyond
+ * the number substituted in a format string) will be appended in a space-
+ * delimited line.
+ *
+ * Parameters:
+ * object - {Object}
+ */
+ log: function() {},
+
+ /**
+ * APIFunction: debug
+ * Writes a message to the console, including a hyperlink to the line
+ * where it was called.
+ *
+ * May be called with multiple arguments as with OpenLayers.Console.log().
+ *
+ * Parameters:
+ * object - {Object}
+ */
+ debug: function() {},
+
+ /**
+ * APIFunction: info
+ * Writes a message to the console with the visual "info" icon and color
+ * coding and a hyperlink to the line where it was called.
+ *
+ * May be called with multiple arguments as with OpenLayers.Console.log().
+ *
+ * Parameters:
+ * object - {Object}
+ */
+ info: function() {},
+
+ /**
+ * APIFunction: warn
+ * Writes a message to the console with the visual "warning" icon and
+ * color coding and a hyperlink to the line where it was called.
+ *
+ * May be called with multiple arguments as with OpenLayers.Console.log().
+ *
+ * Parameters:
+ * object - {Object}
+ */
+ warn: function() {},
+
+ /**
+ * APIFunction: error
+ * Writes a message to the console with the visual "error" icon and color
+ * coding and a hyperlink to the line where it was called.
+ *
+ * May be called with multiple arguments as with OpenLayers.Console.log().
+ *
+ * Parameters:
+ * object - {Object}
+ */
+ error: function() {},
+
+ /**
+ * APIFunction: userError
+ * A single interface for showing error messages to the user. The default
+ * behavior is a Javascript alert, though this can be overridden by
+ * reassigning OpenLayers.Console.userError to a different function.
+ *
+ * Expects a single error message
+ *
+ * Parameters:
+ * object - {Object}
+ */
+ userError: function(error) {
+ alert(error);
+ },
+
+ /**
+ * APIFunction: assert
+ * Tests that an expression is true. If not, it will write a message to
+ * the console and throw an exception.
+ *
+ * May be called with multiple arguments as with OpenLayers.Console.log().
+ *
+ * Parameters:
+ * object - {Object}
+ */
+ assert: function() {},
+
+ /**
+ * APIFunction: dir
+ * Prints an interactive listing of all properties of the object. This
+ * looks identical to the view that you would see in the DOM tab.
+ *
+ * Parameters:
+ * object - {Object}
+ */
+ dir: function() {},
+
+ /**
+ * APIFunction: dirxml
+ * Prints the XML source tree of an HTML or XML element. This looks
+ * identical to the view that you would see in the HTML tab. You can click
+ * on any node to inspect it in the HTML tab.
+ *
+ * Parameters:
+ * object - {Object}
+ */
+ dirxml: function() {},
+
+ /**
+ * APIFunction: trace
+ * Prints an interactive stack trace of JavaScript execution at the point
+ * where it is called. The stack trace details the functions on the stack,
+ * as well as the values that were passed as arguments to each function.
+ * You can click each function to take you to its source in the Script tab,
+ * and click each argument value to inspect it in the DOM or HTML tabs.
+ *
+ */
+ trace: function() {},
+
+ /**
+ * APIFunction: group
+ * Writes a message to the console and opens a nested block to indent all
+ * future messages sent to the console. Call OpenLayers.Console.groupEnd()
+ * to close the block.
+ *
+ * May be called with multiple arguments as with OpenLayers.Console.log().
+ *
+ * Parameters:
+ * object - {Object}
+ */
+ group: function() {},
+
+ /**
+ * APIFunction: groupEnd
+ * Closes the most recently opened block created by a call to
+ * OpenLayers.Console.group
+ */
+ groupEnd: function() {},
+
+ /**
+ * APIFunction: time
+ * Creates a new timer under the given name. Call
+ * OpenLayers.Console.timeEnd(name)
+ * with the same name to stop the timer and print the time elapsed.
+ *
+ * Parameters:
+ * name - {String}
+ */
+ time: function() {},
+
+ /**
+ * APIFunction: timeEnd
+ * Stops a timer created by a call to OpenLayers.Console.time(name) and
+ * writes the time elapsed.
+ *
+ * Parameters:
+ * name - {String}
+ */
+ timeEnd: function() {},
+
+ /**
+ * APIFunction: profile
+ * Turns on the JavaScript profiler. The optional argument title would
+ * contain the text to be printed in the header of the profile report.
+ *
+ * This function is not currently implemented in Firebug Lite.
+ *
+ * Parameters:
+ * title - {String} Optional title for the profiler
+ */
+ profile: function() {},
+
+ /**
+ * APIFunction: profileEnd
+ * Turns off the JavaScript profiler and prints its report.
+ *
+ * This function is not currently implemented in Firebug Lite.
+ */
+ profileEnd: function() {},
+
+ /**
+ * APIFunction: count
+ * Writes the number of times that the line of code where count was called
+ * was executed. The optional argument title will print a message in
+ * addition to the number of the count.
+ *
+ * This function is not currently implemented in Firebug Lite.
+ *
+ * Parameters:
+ * title - {String} Optional title to be printed with count
+ */
+ count: function() {},
+
+ CLASS_NAME: "OpenLayers.Console"
+};
+
+/**
+ * Execute an anonymous function to extend the OpenLayers.Console namespace
+ * if the firebug.js script is included. This closure is used so that the
+ * "scripts" and "i" variables don't pollute the global namespace.
+ */
+(function() {
+ /**
+ * If Firebug Lite is included (before this script), re-route all
+ * OpenLayers.Console calls to the console object.
+ */
+ if(window.console) {
+ var scripts = document.getElementsByTagName("script");
+ for(var i=0, len=scripts.length; i<len; ++i) {
+ if(scripts[i].src.indexOf("firebug.js") != -1) {
+ OpenLayers.Util.extend(OpenLayers.Console, console);
+ break;
+ }
+ }
+ }
+})();
+/* ======================================================================
OpenLayers/Control.js
====================================================================== */
@@ -4478,7 +4350,7 @@
* > },
* >
* > notice: function (bounds) {
- * > alert(bounds);
+ * > OpenLayers.Console.userError(bounds);
* > }
* > });
* > map.addControl(control);
@@ -4611,7 +4483,9 @@
if(this.eventListeners instanceof Object) {
this.events.on(this.eventListeners);
}
- this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
+ if (this.id == null) {
+ this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
+ }
},
/**
@@ -5100,6 +4974,1841 @@
*/
OpenLayers.i18n = OpenLayers.Lang.translate;
/* ======================================================================
+ OpenLayers/Popup.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. */
+
+
+/**
+ * Class: OpenLayers.Popup
+ * A popup is a small div that can opened and closed on the map.
+ * Typically opened in response to clicking on a marker.
+ * See <OpenLayers.Marker>. Popup's don't require their own
+ * layer and are added the the map using the <OpenLayers.Map.addPopup>
+ * method.
+ *
+ * Example:
+ * (code)
+ * popup = new OpenLayers.Popup("chicken",
+ * new OpenLayers.LonLat(5,40),
+ * new OpenLayers.Size(200,200),
+ * "example popup",
+ * true);
+ *
+ * map.addPopup(popup);
+ * (end)
+ */
+OpenLayers.Popup = OpenLayers.Class({
+
+ /**
+ * Property: events
+ * {<OpenLayers.Events>} custom event manager
+ */
+ events: null,
+
+ /** Property: id
+ * {String} the unique identifier assigned to this popup.
+ */
+ id: "",
+
+ /**
+ * Property: lonlat
+ * {<OpenLayers.LonLat>} the position of this popup on the map
+ */
+ lonlat: null,
+
+ /**
+ * Property: div
+ * {DOMElement} the div that contains this popup.
+ */
+ div: null,
+
+ /**
+ * Property: contentSize
+ * {<OpenLayers.Size>} the width and height of the content.
+ */
+ contentSize: null,
+
+ /**
+ * Property: size
+ * {<OpenLayers.Size>} the width and height of the popup.
+ */
+ size: null,
+
+ /**
+ * Property: contentHTML
+ * {String} An HTML string for this popup to display.
+ */
+ contentHTML: null,
+
+ /**
+ * Property: backgroundColor
+ * {String} the background color used by the popup.
+ */
+ backgroundColor: "",
+
+ /**
+ * Property: opacity
+ * {float} the opacity of this popup (between 0.0 and 1.0)
+ */
+ opacity: "",
+
+ /**
+ * Property: border
+ * {String} the border size of the popup. (eg 2px)
+ */
+ border: "",
+
+ /**
+ * Property: contentDiv
+ * {DOMElement} a reference to the element that holds the content of
+ * the div.
+ */
+ contentDiv: null,
+
+ /**
+ * Property: groupDiv
+ * {DOMElement} First and only child of 'div'. The group Div contains the
+ * 'contentDiv' and the 'closeDiv'.
+ */
+ groupDiv: null,
+
+ /**
+ * Property: closeDiv
+ * {DOMElement} the optional closer image
+ */
+ closeDiv: null,
+
+ /**
+ * APIProperty: autoSize
+ * {Boolean} Resize the popup to auto-fit the contents.
+ * Default is false.
+ */
+ autoSize: false,
+
+ /**
+ * APIProperty: minSize
+ * {<OpenLayers.Size>} Minimum size allowed for the popup's contents.
+ */
+ minSize: null,
+
+ /**
+ * APIProperty: maxSize
+ * {<OpenLayers.Size>} Maximum size allowed for the popup's contents.
+ */
+ maxSize: null,
+
+ /**
+ * Property: displayClass
+ * {String} The CSS class of the popup.
+ */
+ displayClass: "olPopup",
+
+ /**
+ * Property: contentDisplayClass
+ * {String} The CSS class of the popup content div.
+ */
+ contentDisplayClass: "olPopupContent",
+
+ /**
+ * Property: padding
+ * {int or <OpenLayers.Bounds>} An extra opportunity to specify internal
+ * padding of the content div inside the popup. This was originally
+ * confused with the css padding as specified in style.css's
+ * 'olPopupContent' class. We would like to get rid of this altogether,
+ * except that it does come in handy for the framed and anchoredbubble
+ * popups, who need to maintain yet another barrier between their
+ * content and the outer border of the popup itself.
+ *
+ * Note that in order to not break API, we must continue to support
+ * this property being set as an integer. Really, though, we'd like to
+ * have this specified as a Bounds object so that user can specify
+ * distinct left, top, right, bottom paddings. With the 3.0 release
+ * we can make this only a bounds.
+ */
+ padding: 0,
+
+ /**
+ * Method: fixPadding
+ * To be removed in 3.0, this function merely helps us to deal with the
+ * case where the user may have set an integer value for padding,
+ * instead of an <OpenLayers.Bounds> object.
+ */
+ fixPadding: function() {
+ if (typeof this.padding == "number") {
+ this.padding = new OpenLayers.Bounds(
+ this.padding, this.padding, this.padding, this.padding
+ );
+ }
+ },
+
+ /**
+ * APIProperty: panMapIfOutOfView
+ * {Boolean} When drawn, pan map such that the entire popup is visible in
+ * the current viewport (if necessary).
+ * Default is false.
+ */
+ panMapIfOutOfView: false,
+
+ /**
+ * Property: map
+ * {<OpenLayers.Map>} this gets set in Map.js when the popup is added to the map
+ */
+ map: null,
+
+ /**
+ * Constructor: OpenLayers.Popup
+ * Create a popup.
+ *
+ * Parameters:
+ * id - {String} a unqiue identifier for this popup. If null is passed
+ * an identifier will be automatically generated.
+ * lonlat - {<OpenLayers.LonLat>} The position on the map the popup will
+ * be shown.
+ * contentSize - {<OpenLayers.Size>} The size of the content.
+ * contentHTML - {String} An HTML string to display inside the
+ * popup.
+ * closeBox - {Boolean} Whether to display a close box inside
+ * the popup.
+ * closeBoxCallback - {Function} Function to be called on closeBox click.
+ */
+ initialize:function(id, lonlat, contentSize, contentHTML, closeBox, closeBoxCallback) {
+ if (id == null) {
+ id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
+ }
+
+ this.id = id;
+ this.lonlat = lonlat;
+
+ this.contentSize = (contentSize != null) ? contentSize
+ : new OpenLayers.Size(
+ OpenLayers.Popup.WIDTH,
+ OpenLayers.Popup.HEIGHT);
+ if (contentHTML != null) {
+ this.contentHTML = contentHTML;
+ }
+ this.backgroundColor = OpenLayers.Popup.COLOR;
+ this.opacity = OpenLayers.Popup.OPACITY;
+ this.border = OpenLayers.Popup.BORDER;
+
+ this.div = OpenLayers.Util.createDiv(this.id, null, null,
+ null, null, null, "hidden");
+ this.div.className = this.displayClass;
+
+ var groupDivId = this.id + "_GroupDiv";
+ this.groupDiv = OpenLayers.Util.createDiv(groupDivId, null, null,
+ null, "relative", null,
+ "hidden");
+
+ var id = this.div.id + "_contentDiv";
+ this.contentDiv = OpenLayers.Util.createDiv(id, null, this.contentSize.clone(),
+ null, "relative");
+ this.contentDiv.className = this.contentDisplayClass;
+ this.groupDiv.appendChild(this.contentDiv);
+ this.div.appendChild(this.groupDiv);
+
+ if (closeBox) {
+ this.addCloseBox(closeBoxCallback);
+ }
+
+ this.registerEvents();
+ },
+
+ /**
+ * Method: destroy
+ * nullify references to prevent circular references and memory leaks
+ */
+ destroy: function() {
+
+ this.id = null;
+ this.lonlat = null;
+ this.size = null;
+ this.contentHTML = null;
+
+ this.backgroundColor = null;
+ this.opacity = null;
+ this.border = null;
+
+ this.events.destroy();
+ this.events = null;
+
+ if (this.closeDiv) {
+ OpenLayers.Event.stopObservingElement(this.closeDiv);
+ this.groupDiv.removeChild(this.closeDiv);
+ }
+ this.closeDiv = null;
+
+ this.div.removeChild(this.groupDiv);
+ this.groupDiv = null;
+
+ if (this.map != null) {
+ this.map.removePopup(this);
+ }
+ this.map = null;
+ this.div = null;
+
+ this.autoSize = null;
+ this.minSize = null;
+ this.maxSize = null;
+ this.padding = null;
+ this.panMapIfOutOfView = null;
+ },
+
+ /**
+ * Method: draw
+ * Constructs the elements that make up the popup.
+ *
+ * Parameters:
+ * px - {<OpenLayers.Pixel>} the position the popup in pixels.
+ *
+ * Returns:
+ * {DOMElement} Reference to a div that contains the drawn popup
+ */
+ draw: function(px) {
+ if (px == null) {
+ if ((this.lonlat != null) && (this.map != null)) {
+ px = this.map.getLayerPxFromLonLat(this.lonlat);
+ }
+ }
+
+ //listen to movestart, moveend to disable overflow (FF bug)
+ if (OpenLayers.Util.getBrowserName() == 'firefox') {
+ this.map.events.register("movestart", this, function() {
+ var style = document.defaultView.getComputedStyle(
+ this.contentDiv, null
+ );
+ var currentOverflow = style.getPropertyValue("overflow");
+ if (currentOverflow != "hidden") {
+ this.contentDiv._oldOverflow = currentOverflow;
+ this.contentDiv.style.overflow = "hidden";
+ }
+ });
+ this.map.events.register("moveend", this, function() {
+ var oldOverflow = this.contentDiv._oldOverflow;
+ if (oldOverflow) {
+ this.contentDiv.style.overflow = oldOverflow;
+ this.contentDiv._oldOverflow = null;
+ }
+ });
+ }
+
+ this.moveTo(px);
+ if (!this.autoSize && !this.size) {
+ this.setSize(this.contentSize);
+ }
+ this.setBackgroundColor();
+ this.setOpacity();
+ this.setBorder();
+ this.setContentHTML();
+
+ if (this.panMapIfOutOfView) {
+ this.panIntoView();
+ }
+
+ return this.div;
+ },
+
+ /**
+ * Method: updatePosition
+ * if the popup has a lonlat and its map members set,
+ * then have it move itself to its proper position
+ */
+ updatePosition: function() {
+ if ((this.lonlat) && (this.map)) {
+ var px = this.map.getLayerPxFromLonLat(this.lonlat);
+ if (px) {
+ this.moveTo(px);
+ }
+ }
+ },
+
+ /**
+ * Method: moveTo
+ *
+ * Parameters:
+ * px - {<OpenLayers.Pixel>} the top and left position of the popup div.
+ */
+ moveTo: function(px) {
+ if ((px != null) && (this.div != null)) {
+ this.div.style.left = px.x + "px";
+ this.div.style.top = px.y + "px";
+ }
+ },
+
+ /**
+ * Method: visible
+ *
+ * Returns:
+ * {Boolean} Boolean indicating whether or not the popup is visible
+ */
+ visible: function() {
+ return OpenLayers.Element.visible(this.div);
+ },
+
+ /**
+ * Method: toggle
+ * Toggles visibility of the popup.
+ */
+ toggle: function() {
+ if (this.visible()) {
+ this.hide();
+ } else {
+ this.show();
+ }
+ },
+
+ /**
+ * Method: show
+ * Makes the popup visible.
+ */
+ show: function() {
+ OpenLayers.Element.show(this.div);
+
+ if (this.panMapIfOutOfView) {
+ this.panIntoView();
+ }
+ },
+
+ /**
+ * Method: hide
+ * Makes the popup invisible.
+ */
+ hide: function() {
+ OpenLayers.Element.hide(this.div);
+ },
+
+ /**
+ * Method: setSize
+ * Used to adjust the size of the popup.
+ *
+ * Parameters:
+ * contentSize - {<OpenLayers.Size>} the new size for the popup's
+ * contents div (in pixels).
+ */
+ setSize:function(contentSize) {
+ this.size = contentSize.clone();
+
+ // if our contentDiv has a css 'padding' set on it by a stylesheet, we
+ // must add that to the desired "size".
+ var contentDivPadding = this.getContentDivPadding();
+ var wPadding = contentDivPadding.left + contentDivPadding.right;
+ var hPadding = contentDivPadding.top + contentDivPadding.bottom;
+
+ // take into account the popup's 'padding' property
+ this.fixPadding();
+ wPadding += this.padding.left + this.padding.right;
+ hPadding += this.padding.top + this.padding.bottom;
+
+ // make extra space for the close div
+ if (this.closeDiv) {
+ var closeDivWidth = parseInt(this.closeDiv.style.width);
+ wPadding += closeDivWidth + contentDivPadding.right;
+ }
+
+ //increase size of the main popup div to take into account the
+ // users's desired padding and close div.
+ this.size.w += wPadding;
+ this.size.h += hPadding;
+
+ //now if our browser is IE, we need to actually make the contents
+ // div itself bigger to take its own padding into effect. this makes
+ // me want to shoot someone, but so it goes.
+ if (OpenLayers.Util.getBrowserName() == "msie") {
+ this.contentSize.w +=
+ contentDivPadding.left + contentDivPadding.right;
+ this.contentSize.h +=
+ contentDivPadding.bottom + contentDivPadding.top;
+ }
+
+ if (this.div != null) {
+ this.div.style.width = this.size.w + "px";
+ this.div.style.height = this.size.h + "px";
+ }
+ if (this.contentDiv != null){
+ this.contentDiv.style.width = contentSize.w + "px";
+ this.contentDiv.style.height = contentSize.h + "px";
+ }
+ },
+
+ /**
+ * APIMethod: updateSize
+ * Auto size the popup so that it precisely fits its contents (as
+ * determined by this.contentDiv.innerHTML). Popup size will, of
+ * course, be limited by the available space on the current map
+ */
+ updateSize: function() {
+
+ // determine actual render dimensions of the contents by putting its
+ // contents into a fake contentDiv (for the CSS) and then measuring it
+ var preparedHTML = "<div class='" + this.contentDisplayClass+ "'>" +
+ this.contentDiv.innerHTML +
+ "<div>";
+ var realSize = OpenLayers.Util.getRenderedDimensions(
+ preparedHTML, null, { displayClass: this.displayClass }
+ );
+
+ // is the "real" size of the div is safe to display in our map?
+ var safeSize = this.getSafeContentSize(realSize);
+
+ var newSize = null;
+ if (safeSize.equals(realSize)) {
+ //real size of content is small enough to fit on the map,
+ // so we use real size.
+ newSize = realSize;
+
+ } else {
+
+ //make a new OL.Size object with the clipped dimensions
+ // set or null if not clipped.
+ var fixedSize = new OpenLayers.Size();
+ fixedSize.w = (safeSize.w < realSize.w) ? safeSize.w : null;
+ fixedSize.h = (safeSize.h < realSize.h) ? safeSize.h : null;
+
+ if (fixedSize.w && fixedSize.h) {
+ //content is too big in both directions, so we will use
+ // max popup size (safeSize), knowing well that it will
+ // overflow both ways.
+ newSize = safeSize;
+ } else {
+ //content is clipped in only one direction, so we need to
+ // run getRenderedDimensions() again with a fixed dimension
+ var clippedSize = OpenLayers.Util.getRenderedDimensions(
+ preparedHTML, fixedSize,
+ { displayClass: this.contentDisplayClass }
+ );
+
+ //if the clipped size is still the same as the safeSize,
+ // that means that our content must be fixed in the
+ // offending direction. If overflow is 'auto', this means
+ // we are going to have a scrollbar for sure, so we must
+ // adjust for that.
+ //
+ var currentOverflow = OpenLayers.Element.getStyle(
+ this.contentDiv, "overflow"
+ );
+ if ( (currentOverflow != "hidden") &&
+ (clippedSize.equals(safeSize)) ) {
+ var scrollBar = OpenLayers.Util.getScrollbarWidth();
+ if (fixedSize.w) {
+ clippedSize.h += scrollBar;
+ } else {
+ clippedSize.w += scrollBar;
+ }
+ }
+
+ newSize = this.getSafeContentSize(clippedSize);
+ }
+ }
+ this.setSize(newSize);
+ },
+
+ /**
+ * Method: setBackgroundColor
+ * Sets the background color of the popup.
+ *
+ * Parameters:
+ * color - {String} the background color. eg "#FFBBBB"
+ */
+ setBackgroundColor:function(color) {
+ if (color != undefined) {
+ this.backgroundColor = color;
+ }
+
+ if (this.div != null) {
+ this.div.style.backgroundColor = this.backgroundColor;
+ }
+ },
+
+ /**
+ * Method: setOpacity
+ * Sets the opacity of the popup.
+ *
+ * Parameters:
+ * opacity - {float} A value between 0.0 (transparent) and 1.0 (solid).
+ */
+ setOpacity:function(opacity) {
+ if (opacity != undefined) {
+ this.opacity = opacity;
+ }
+
+ if (this.div != null) {
+ // for Mozilla and Safari
+ this.div.style.opacity = this.opacity;
+
+ // for IE
+ this.div.style.filter = 'alpha(opacity=' + this.opacity*100 + ')';
+ }
+ },
+
+ /**
+ * Method: setBorder
+ * Sets the border style of the popup.
+ *
+ * Parameters:
+ * border - {String} The border style value. eg 2px
+ */
+ setBorder:function(border) {
+ if (border != undefined) {
+ this.border = border;
+ }
+
+ if (this.div != null) {
+ this.div.style.border = this.border;
+ }
+ },
+
+ /**
+ * Method: setContentHTML
+ * Allows the user to set the HTML content of the popup.
+ *
+ * Parameters:
+ * contentHTML - {String} HTML for the div.
+ */
+ setContentHTML:function(contentHTML) {
+
+ if (contentHTML != null) {
+ this.contentHTML = contentHTML;
+ }
+
+ if ((this.contentDiv != null) &&
+ (this.contentHTML != null) &&
+ (this.contentHTML != this.contentDiv.innerHTML)) {
+
+ this.contentDiv.innerHTML = this.contentHTML;
+
+ if (this.autoSize) {
+
+ //if popup has images, listen for when they finish
+ // loading and resize accordingly
+ this.registerImageListeners();
+
+ //auto size the popup to its current contents
+ this.updateSize();
+ }
+ }
+
+ },
+
+ /**
+ * Method: registerImageListeners
+ * Called when an image contained by the popup loaded. this function
+ * updates the popup size, then unregisters the image load listener.
+ */
+ registerImageListeners: function() {
+
+ // As the images load, this function will call updateSize() to
+ // resize the popup to fit the content div (which presumably is now
+ // bigger than when the image was not loaded).
+ //
+ // If the 'panMapIfOutOfView' property is set, we will pan the newly
+ // resized popup back into view.
+ //
+ // Note that this function, when called, will have 'popup' and
+ // 'img' properties in the context.
+ //
+ var onImgLoad = function() {
+
+ this.popup.updateSize();
+
+ if ( this.popup.visible() && this.popup.panMapIfOutOfView ) {
+ this.popup.panIntoView();
+ }
+
+ OpenLayers.Event.stopObserving(
+ this.img, "load", this.img._onImageLoad
+ );
+
+ };
+
+ //cycle through the images and if their size is 0x0, that means that
+ // they haven't been loaded yet, so we attach the listener, which
+ // will fire when the images finish loading and will resize the
+ // popup accordingly to its new size.
+ var images = this.contentDiv.getElementsByTagName("img");
+ for (var i = 0, len = images.length; i < len; i++) {
+ var img = images[i];
+ if (img.width == 0 || img.height == 0) {
+
+ var context = {
+ 'popup': this,
+ 'img': img
+ };
+
+ //expando this function to the image itself before registering
+ // it. This way we can easily and properly unregister it.
+ img._onImgLoad = OpenLayers.Function.bind(onImgLoad, context);
+
+ OpenLayers.Event.observe(img, 'load', img._onImgLoad);
+ }
+ }
+ },
+
+ /**
+ * APIMethod: getSafeContentSize
+ *
+ * Parameters:
+ * size - {<OpenLayers.Size>} Desired size to make the popup.
+ *
+ * Returns:
+ * {<OpenLayers.Size>} A size to make the popup which is neither smaller
+ * than the specified minimum size, nor bigger than the maximum
+ * size (which is calculated relative to the size of the viewport).
+ */
+ getSafeContentSize: function(size) {
+
+ var safeContentSize = size.clone();
+
+ // if our contentDiv has a css 'padding' set on it by a stylesheet, we
+ // must add that to the desired "size".
+ var contentDivPadding = this.getContentDivPadding();
+ var wPadding = contentDivPadding.left + contentDivPadding.right;
+ var hPadding = contentDivPadding.top + contentDivPadding.bottom;
+
+ // take into account the popup's 'padding' property
+ this.fixPadding();
+ wPadding += this.padding.left + this.padding.right;
+ hPadding += this.padding.top + this.padding.bottom;
+
+ if (this.closeDiv) {
+ var closeDivWidth = parseInt(this.closeDiv.style.width);
+ wPadding += closeDivWidth + contentDivPadding.right;
+ }
+
+ // prevent the popup from being smaller than a specified minimal size
+ if (this.minSize) {
+ safeContentSize.w = Math.max(safeContentSize.w,
+ (this.minSize.w - wPadding));
+ safeContentSize.h = Math.max(safeContentSize.h,
+ (this.minSize.h - hPadding));
+ }
+
+ // prevent the popup from being bigger than a specified maximum size
+ if (this.maxSize) {
+ safeContentSize.w = Math.min(safeContentSize.w,
+ (this.maxSize.w - wPadding));
+ safeContentSize.h = Math.min(safeContentSize.h,
+ (this.maxSize.h - hPadding));
+ }
+
+ //make sure the desired size to set doesn't result in a popup that
+ // is bigger than the map's viewport.
+ //
+ if (this.map && this.map.size) {
+
+ // Note that there *was* a reference to a
+ // 'OpenLayers.Popup.SCROLL_BAR_WIDTH' constant here, with special
+ // tolerance for it and everything... but it was never defined in
+ // the first place, so I don't know what to think.
+
+ var maxY = this.map.size.h -
+ this.map.paddingForPopups.top -
+ this.map.paddingForPopups.bottom -
+ hPadding;
+
+ var maxX = this.map.size.w -
+ this.map.paddingForPopups.left -
+ this.map.paddingForPopups.right -
+ wPadding;
+
+ safeContentSize.w = Math.min(safeContentSize.w, maxX);
+ safeContentSize.h = Math.min(safeContentSize.h, maxY);
+ }
+
+ return safeContentSize;
+ },
+
+ /**
+ * Method: getContentDivPadding
+ * Glorious, oh glorious hack in order to determine the css 'padding' of
+ * the contentDiv. IE/Opera return null here unless we actually add the
+ * popup's main 'div' element (which contains contentDiv) to the DOM.
+ * So we make it invisible and then add it to the document temporarily.
+ *
+ * Once we've taken the padding readings we need, we then remove it
+ * from the DOM (it will actually get added to the DOM in
+ * Map.js's addPopup)
+ *
+ * Returns:
+ * {<OpenLayers.Bounds>}
+ */
+ getContentDivPadding: function() {
+
+ //use cached value if we have it
+ var contentDivPadding = this._contentDivPadding;
+ if (!contentDivPadding) {
+ //make the div invisible and add it to the page
+ this.div.style.display = "none";
+ document.body.appendChild(this.div);
+
+ //read the padding settings from css, put them in an OL.Bounds
+ contentDivPadding = new OpenLayers.Bounds(
+ OpenLayers.Element.getStyle(this.contentDiv, "padding-left"),
+ OpenLayers.Element.getStyle(this.contentDiv, "padding-bottom"),
+ OpenLayers.Element.getStyle(this.contentDiv, "padding-right"),
+ OpenLayers.Element.getStyle(this.contentDiv, "padding-top")
+ );
+
+ //cache the value
+ this._contentDivPadding = contentDivPadding;
+
+ //remove the div from the page and make it visible again
+ document.body.removeChild(this.div);
+ this.div.style.display = "";
+ }
+ return contentDivPadding;
+ },
+
+ /**
+ * Method: addCloseBox
+ *
+ * Parameters:
+ * callback - {Function} The callback to be called when the close button
+ * is clicked.
+ */
+ addCloseBox: function(callback) {
+
+ this.closeDiv = OpenLayers.Util.createDiv(
+ this.id + "_close", null, new OpenLayers.Size(17, 17)
+ );
+ this.closeDiv.className = "olPopupCloseBox";
+
+ // use the content div's css padding to determine if we should
+ // padd the close div
+ var contentDivPadding = this.getContentDivPadding();
+
+ this.closeDiv.style.right = contentDivPadding.right + "px";
+ this.closeDiv.style.top = contentDivPadding.top + "px";
+ this.groupDiv.appendChild(this.closeDiv);
+
+ var closePopup = callback || function(e) {
+ this.hide();
+ OpenLayers.Event.stop(e);
+ };
+ OpenLayers.Event.observe(this.closeDiv, "click",
+ OpenLayers.Function.bindAsEventListener(closePopup, this));
+ },
+
+ /**
+ * Method: panIntoView
+ * Pans the map such that the popup is totaly viewable (if necessary)
+ */
+ panIntoView: function() {
+
+ var mapSize = this.map.getSize();
+
+ //start with the top left corner of the popup, in px,
+ // relative to the viewport
+ var origTL = this.map.getViewPortPxFromLayerPx( new OpenLayers.Pixel(
+ parseInt(this.div.style.left),
+ parseInt(this.div.style.top)
+ ));
+ var newTL = origTL.clone();
+
+ //new left (compare to margins, using this.size to calculate right)
+ if (origTL.x < this.map.paddingForPopups.left) {
+ newTL.x = this.map.paddingForPopups.left;
+ } else
+ if ( (origTL.x + this.size.w) > (mapSize.w - this.map.paddingForPopups.right)) {
+ newTL.x = mapSize.w - this.map.paddingForPopups.right - this.size.w;
+ }
+
+ //new top (compare to margins, using this.size to calculate bottom)
+ if (origTL.y < this.map.paddingForPopups.top) {
+ newTL.y = this.map.paddingForPopups.top;
+ } else
+ if ( (origTL.y + this.size.h) > (mapSize.h - this.map.paddingForPopups.bottom)) {
+ newTL.y = mapSize.h - this.map.paddingForPopups.bottom - this.size.h;
+ }
+
+ var dx = origTL.x - newTL.x;
+ var dy = origTL.y - newTL.y;
+
+ this.map.pan(dx, dy);
+ },
+
+ /**
+ * Method: registerEvents
+ * Registers events on the popup.
+ *
+ * Do this in a separate function so that subclasses can
+ * choose to override it if they wish to deal differently
+ * with mouse events
+ *
+ * Note in the following handler functions that some special
+ * care is needed to deal correctly with mousing and popups.
+ *
+ * Because the user might select the zoom-rectangle option and
+ * then drag it over a popup, we need a safe way to allow the
+ * mousemove and mouseup events to pass through the popup when
+ * they are initiated from outside.
+ *
+ * Otherwise, we want to essentially kill the event propagation
+ * for all other events, though we have to do so carefully,
+ * without disabling basic html functionality, like clicking on
+ * hyperlinks or drag-selecting text.
+ */
+ registerEvents:function() {
+ this.events = new OpenLayers.Events(this, this.div, null, true);
+
+ this.events.on({
+ "mousedown": this.onmousedown,
+ "mousemove": this.onmousemove,
+ "mouseup": this.onmouseup,
+ "click": this.onclick,
+ "mouseout": this.onmouseout,
+ "dblclick": this.ondblclick,
+ scope: this
+ });
+
+ },
+
+ /**
+ * Method: onmousedown
+ * When mouse goes down within the popup, make a note of
+ * it locally, and then do not propagate the mousedown
+ * (but do so safely so that user can select text inside)
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ onmousedown: function (evt) {
+ this.mousedown = true;
+ OpenLayers.Event.stop(evt, true);
+ },
+
+ /**
+ * Method: onmousemove
+ * If the drag was started within the popup, then
+ * do not propagate the mousemove (but do so safely
+ * so that user can select text inside)
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ onmousemove: function (evt) {
+ if (this.mousedown) {
+ OpenLayers.Event.stop(evt, true);
+ }
+ },
+
+ /**
+ * Method: onmouseup
+ * When mouse comes up within the popup, after going down
+ * in it, reset the flag, and then (once again) do not
+ * propagate the event, but do so safely so that user can
+ * select text inside
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ onmouseup: function (evt) {
+ if (this.mousedown) {
+ this.mousedown = false;
+ OpenLayers.Event.stop(evt, true);
+ }
+ },
+
+ /**
+ * Method: onclick
+ * Ignore clicks, but allowing default browser handling
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ onclick: function (evt) {
+ OpenLayers.Event.stop(evt, true);
+ },
+
+ /**
+ * Method: onmouseout
+ * When mouse goes out of the popup set the flag to false so that
+ * if they let go and then drag back in, we won't be confused.
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ onmouseout: function (evt) {
+ this.mousedown = false;
+ },
+
+ /**
+ * Method: ondblclick
+ * Ignore double-clicks, but allowing default browser handling
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ ondblclick: function (evt) {
+ OpenLayers.Event.stop(evt, true);
+ },
+
+ CLASS_NAME: "OpenLayers.Popup"
+});
+
+OpenLayers.Popup.WIDTH = 200;
+OpenLayers.Popup.HEIGHT = 200;
+OpenLayers.Popup.COLOR = "white";
+OpenLayers.Popup.OPACITY = 1;
+OpenLayers.Popup.BORDER = "0px";
+/* ======================================================================
+ OpenLayers/Protocol.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. */
+
+/**
+ * Class: OpenLayers.Protocol
+ * Abstract vector layer protocol class. Not to be instantiated directly. Use
+ * one of the protocol subclasses instead.
+ */
+OpenLayers.Protocol = OpenLayers.Class({
+
+ /**
+ * Property: format
+ * {<OpenLayers.Format>}
+ */
+ format: null,
+
+ /**
+ * Property: options
+ * Any options sent to the constructor.
+ */
+ options: null,
+
+ /**
+ * Property: autoDestroy
+ * {Boolean} The creator of the protocol can set autoDestroy to false
+ * to fully control when the protocol is destroyed. Defaults to
+ * true.
+ */
+ autoDestroy: true,
+
+ /**
+ * Constructor: OpenLayers.Protocol
+ * Abstract class for vector protocols. Create instances of a subclass.
+ *
+ * Parameters:
+ * options - {Object} Optional object whose properties will be set on the
+ * instance.
+ */
+ initialize: function(options) {
+ options = options || {};
+ OpenLayers.Util.extend(this, options);
+ this.options = options;
+ },
+
+ /**
+ * APIMethod: destroy
+ * Clean up the protocol.
+ */
+ destroy: function() {
+ this.options = null;
+ this.format = null;
+ },
+
+ /**
+ * Method: read
+ * Construct a request for reading new features.
+ *
+ * Parameters:
+ * options - {Object} Optional object for configuring the request.
+ *
+ * Returns:
+ * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
+ * object, the same object will be passed to the callback function passed
+ * if one exists in the options object.
+ */
+ read: function() {
+ },
+
+
+ /**
+ * Method: create
+ * Construct a request for writing newly created features.
+ *
+ * Parameters:
+ * features - {Array({<OpenLayers.Feature.Vector>})} or
+ * {<OpenLayers.Feature.Vector>}
+ * options - {Object} Optional object for configuring the request.
+ *
+ * Returns:
+ * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
+ * object, the same object will be passed to the callback function passed
+ * if one exists in the options object.
+ */
+ create: function() {
+ },
+
+ /**
+ * Method: update
+ * Construct a request updating modified features.
+ *
+ * Parameters:
+ * features - {Array({<OpenLayers.Feature.Vector>})} or
+ * {<OpenLayers.Feature.Vector>}
+ * options - {Object} Optional object for configuring the request.
+ *
+ * Returns:
+ * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
+ * object, the same object will be passed to the callback function passed
+ * if one exists in the options object.
+ */
+ update: function() {
+ },
+
+ /**
+ * Method: delete
+ * Construct a request deleting a removed feature.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>}
+ * options - {Object} Optional object for configuring the request.
+ *
+ * Returns:
+ * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
+ * object, the same object will be passed to the callback function passed
+ * if one exists in the options object.
+ */
+ "delete": function() {
+ },
+
+ /**
+ * Method: commit
+ * Go over the features and for each take action
+ * based on the feature state. Possible actions are create,
+ * update and delete.
+ *
+ * Parameters:
+ * features - {Array({<OpenLayers.Feature.Vector>})}
+ * options - {Object} Object whose possible keys are "create", "update",
+ * "delete", "callback" and "scope", the values referenced by the
+ * first three are objects as passed to the "create", "update", and
+ * "delete" methods, the value referenced by the "callback" key is
+ * a function which is called when the commit operation is complete
+ * using the scope referenced by the "scope" key.
+ *
+ * Returns:
+ * {Array({<OpenLayers.Protocol.Response>})} An array of
+ * <OpenLayers.Protocol.Response> objects.
+ */
+ commit: function() {
+ },
+
+ CLASS_NAME: "OpenLayers.Protocol"
+});
+
+/**
+ * Class: OpenLayers.Protocol.Response
+ * Protocols return Response objects to their users.
+ */
+OpenLayers.Protocol.Response = OpenLayers.Class({
+ /**
+ * Property: code
+ * {Number} - OpenLayers.Protocol.Response.SUCCESS or
+ * OpenLayers.Protocol.Response.FAILURE
+ */
+ code: null,
+
+ /**
+ * Property: requestType
+ * {String} The type of request this response corresponds to. Either
+ * "create", "read", "update" or "delete".
+ */
+ requestType: null,
+
+ /**
+ * Property: last
+ * {Boolean} - true if this is the last response expected in a commit,
+ * false otherwise, defaults to true.
+ */
+ last: true,
+
+ /**
+ * Property: features
+ * {Array({<OpenLayers.Feature.Vector>})} or {<OpenLayers.Feature.Vector>}
+ * The features returned in the response by the server.
+ */
+ features: null,
+
+ /**
+ * Property: reqFeatures
+ * {Array({<OpenLayers.Feature.Vector>})} or {<OpenLayers.Feature.Vector>}
+ * The features provided by the user and placed in the request by the
+ * protocol.
+ */
+ reqFeatures: null,
+
+ /**
+ * Property: priv
+ */
+ priv: null,
+
+ /**
+ * Constructor: OpenLayers.Protocol.Response
+ *
+ * Parameters:
+ * options - {Object} Optional object whose properties will be set on the
+ * instance.
+ */
+ initialize: function(options) {
+ OpenLayers.Util.extend(this, options);
+ },
+
+ /**
+ * Method: success
+ *
+ * Returns:
+ * {Boolean} - true on success, false otherwise
+ */
+ success: function() {
+ return this.code > 0;
+ },
+
+ CLASS_NAME: "OpenLayers.Protocol.Response"
+});
+
+OpenLayers.Protocol.Response.SUCCESS = 1;
+OpenLayers.Protocol.Response.FAILURE = 0;
+/* ======================================================================
+ OpenLayers/Renderer.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. */
+
+/**
+ * Class: OpenLayers.Renderer
+ * This is the base class for all renderers.
+ *
+ * This is based on a merger code written by Paul Spencer and Bertil Chapuis.
+ * It is largely composed of virtual functions that are to be implemented
+ * in technology-specific subclasses, but there is some generic code too.
+ *
+ * The functions that *are* implemented here merely deal with the maintenance
+ * of the size and extent variables, as well as the cached 'resolution'
+ * value.
+ *
+ * A note to the user that all subclasses should use getResolution() instead
+ * of directly accessing this.resolution in order to correctly use the
+ * cacheing system.
+ *
+ */
+OpenLayers.Renderer = OpenLayers.Class({
+
+ /**
+ * Property: container
+ * {DOMElement}
+ */
+ container: null,
+
+ /**
+ * Property: extent
+ * {<OpenLayers.Bounds>}
+ */
+ extent: null,
+
+ /**
+ * Property: locked
+ * {Boolean} If the renderer is currently in a state where many things
+ * are changing, the 'locked' property is set to true. This means
+ * that renderers can expect at least one more drawFeature event to be
+ * called with the 'locked' property set to 'true': In some renderers,
+ * this might make sense to use as a 'only update local information'
+ * flag.
+ */
+ locked: false,
+
+ /**
+ * Property: size
+ * {<OpenLayers.Size>}
+ */
+ size: null,
+
+ /**
+ * Property: resolution
+ * {Float} cache of current map resolution
+ */
+ resolution: null,
+
+ /**
+ * Property: map
+ * {<OpenLayers.Map>} Reference to the map -- this is set in Vector's setMap()
+ */
+ map: null,
+
+ /**
+ * Constructor: OpenLayers.Renderer
+ *
+ * Parameters:
+ * containerID - {<String>}
+ * options - {Object} options for this renderer. See sublcasses for
+ * supported options.
+ */
+ initialize: function(containerID, options) {
+ this.container = OpenLayers.Util.getElement(containerID);
+ },
+
+ /**
+ * APIMethod: destroy
+ */
+ destroy: function() {
+ this.container = null;
+ this.extent = null;
+ this.size = null;
+ this.resolution = null;
+ this.map = null;
+ },
+
+ /**
+ * APIMethod: supported
+ * This should be overridden by specific subclasses
+ *
+ * Returns:
+ * {Boolean} Whether or not the browser supports the renderer class
+ */
+ supported: function() {
+ return false;
+ },
+
+ /**
+ * Method: setExtent
+ * Set the visible part of the layer.
+ *
+ * Resolution has probably changed, so we nullify the resolution
+ * cache (this.resolution) -- this way it will be re-computed when
+ * next it is needed.
+ * We nullify the resolution cache (this.resolution) if resolutionChanged
+ * is set to true - this way it will be re-computed on the next
+ * getResolution() request.
+ *
+ * Parameters:
+ * extent - {<OpenLayers.Bounds>}
+ * resolutionChanged - {Boolean}
+ */
+ setExtent: function(extent, resolutionChanged) {
+ this.extent = extent.clone();
+ if (resolutionChanged) {
+ this.resolution = null;
+ }
+ },
+
+ /**
+ * Method: setSize
+ * Sets the size of the drawing surface.
+ *
+ * Resolution has probably changed, so we nullify the resolution
+ * cache (this.resolution) -- this way it will be re-computed when
+ * next it is needed.
+ *
+ * Parameters:
+ * size - {<OpenLayers.Size>}
+ */
+ setSize: function(size) {
+ this.size = size.clone();
+ this.resolution = null;
+ },
+
+ /**
+ * Method: getResolution
+ * Uses cached copy of resolution if available to minimize computing
+ *
+ * Returns:
+ * The current map's resolution
+ */
+ getResolution: function() {
+ this.resolution = this.resolution || this.map.getResolution();
+ return this.resolution;
+ },
+
+ /**
+ * Method: drawFeature
+ * Draw the feature. The optional style argument can be used
+ * to override the feature's own style. This method should only
+ * be called from layer.drawFeature().
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>}
+ * style - {<Object>}
+ *
+ * Returns:
+ * {Boolean} true if the feature has been drawn completely, false if not,
+ * undefined if the feature had no geometry
+ */
+ drawFeature: function(feature, style) {
+ if(style == null) {
+ style = feature.style;
+ }
+ if (feature.geometry) {
+ if (!feature.geometry.getBounds().intersectsBounds(this.extent)) {
+ style = {display: "none"};
+ }
+ return this.drawGeometry(feature.geometry, style, feature.id);
+ }
+ },
+
+
+ /**
+ * Method: drawGeometry
+ *
+ * Draw a geometry. This should only be called from the renderer itself.
+ * Use layer.drawFeature() from outside the renderer.
+ * virtual function
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry>}
+ * style - {Object}
+ * featureId - {<String>}
+ */
+ drawGeometry: function(geometry, style, featureId) {},
+
+ /**
+ * Method: clear
+ * Clear all vectors from the renderer.
+ * virtual function.
+ */
+ clear: function() {},
+
+ /**
+ * Method: getFeatureIdFromEvent
+ * Returns a feature id from an event on the renderer.
+ * How this happens is specific to the renderer. This should be
+ * called from layer.getFeatureFromEvent().
+ * Virtual function.
+ *
+ * Parameters:
+ * evt - {<OpenLayers.Event>}
+ *
+ * Returns:
+ * {String} A feature id or null.
+ */
+ getFeatureIdFromEvent: function(evt) {},
+
+ /**
+ * Method: eraseFeatures
+ * This is called by the layer to erase features
+ *
+ * Parameters:
+ * features - {Array(<OpenLayers.Feature.Vector>)}
+ */
+ eraseFeatures: function(features) {
+ if(!(features instanceof Array)) {
+ features = [features];
+ }
+ for(var i=0, len=features.length; i<len; ++i) {
+ this.eraseGeometry(features[i].geometry);
+ }
+ },
+
+ /**
+ * Method: eraseGeometry
+ * Remove a geometry from the renderer (by id).
+ * virtual function.
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry>}
+ */
+ eraseGeometry: function(geometry) {},
+
+ CLASS_NAME: "OpenLayers.Renderer"
+});
+/* ======================================================================
+ OpenLayers/Request.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. */
+
+/**
+ * Namespace: OpenLayers.Request
+ * The OpenLayers.Request namespace contains convenience methods for working
+ * with XMLHttpRequests. These methods work with a cross-browser
+ * W3C compliant <OpenLayers.Request.XMLHttpRequest> class.
+ */
+OpenLayers.Request = {
+
+ /**
+ * Constant: DEFAULT_CONFIG
+ * {Object} Default configuration for all requests.
+ */
+ DEFAULT_CONFIG: {
+ method: "GET",
+ url: window.location.href,
+ async: true,
+ user: undefined,
+ password: undefined,
+ params: null,
+ proxy: OpenLayers.ProxyHost,
+ headers: {},
+ data: null,
+ callback: function() {},
+ success: null,
+ failure: null,
+ scope: null
+ },
+
+ /**
+ * APIMethod: issue
+ * Create a new XMLHttpRequest object, open it, set any headers, bind
+ * a callback to done state, and send any data.
+ *
+ * Parameters:
+ * config - {Object} Object containing properties for configuring the
+ * request. Allowed configuration properties are described below.
+ * This object is modified and should not be reused.
+ *
+ * Allowed config properties:
+ * method - {String} One of GET, POST, PUT, DELETE, HEAD, or
+ * OPTIONS. Default is GET.
+ * url - {String} URL for the request.
+ * async - {Boolean} Open an asynchronous request. Default is true.
+ * user - {String} User for relevant authentication scheme. Set
+ * to null to clear current user.
+ * password - {String} Password for relevant authentication scheme.
+ * Set to null to clear current password.
+ * proxy - {String} Optional proxy. Defaults to
+ * <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
+ * 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.
+ * callback - {Function} Function to call when request is done.
+ * To determine if the request failed, check request.status (200
+ * indicates success).
+ * success - {Function} Optional function to call if request status is in
+ * the 200s. This will be called in addition to callback above and
+ * would typically only be used as an alternative.
+ * failure - {Function} Optional function to call if request status is not
+ * in the 200s. This will be called in addition to callback above and
+ * would typically only be used as an alternative.
+ * scope - {Object} If callback is a public method on some object,
+ * set the scope to that object.
+ *
+ * Returns:
+ * {XMLHttpRequest} Request object. To abort the request before a response
+ * is received, call abort() on the request object.
+ */
+ issue: function(config) {
+ // apply default config - proxy host may have changed
+ var defaultConfig = OpenLayers.Util.extend(
+ this.DEFAULT_CONFIG,
+ {proxy: OpenLayers.ProxyHost}
+ );
+ config = OpenLayers.Util.applyDefaults(config, defaultConfig);
+
+ // create request, open, and set headers
+ var request = new OpenLayers.Request.XMLHttpRequest();
+ var url = config.url;
+ if(config.params) {
+ url += "?" + OpenLayers.Util.getParameterString(config.params);
+ }
+ if(config.proxy && (url.indexOf("http") == 0)) {
+ url = config.proxy + encodeURIComponent(url);
+ }
+ request.open(
+ config.method, url, config.async, config.user, config.password
+ );
+ for(var header in config.headers) {
+ request.setRequestHeader(header, config.headers[header]);
+ }
+
+ // bind callbacks to readyState 4 (done)
+ var complete = (config.scope) ?
+ OpenLayers.Function.bind(config.callback, config.scope) :
+ config.callback;
+
+ // optional success callback
+ var success;
+ if(config.success) {
+ success = (config.scope) ?
+ OpenLayers.Function.bind(config.success, config.scope) :
+ config.success;
+ }
+
+ // optional failure callback
+ var failure;
+ if(config.failure) {
+ failure = (config.scope) ?
+ OpenLayers.Function.bind(config.failure, config.scope) :
+ config.failure;
+ }
+
+ request.onreadystatechange = function() {
+ if(request.readyState == OpenLayers.Request.XMLHttpRequest.DONE) {
+ complete(request);
+ if(success && (!request.status ||
+ (request.status >= 200 && request.status < 300))) {
+ success(request);
+ }
+ if(failure && (request.status &&
+ (request.status < 200 || request.status >= 300))) {
+ failure(request);
+ }
+ }
+ }
+
+ // send request (optionally with data) and return
+ request.send(config.data);
+ return request;
+ },
+
+ /**
+ * APIMethod: GET
+ * Send an HTTP GET request. Additional configuration properties are
+ * documented in the <issue> method, with the method property set
+ * to GET.
+ *
+ * Parameters:
+ * config - {Object} Object with properties for configuring the request.
+ * See the <issue> method for documentation of allowed properties.
+ * This object is modified and should not be reused.
+ *
+ * Returns:
+ * {XMLHttpRequest} Request object.
+ */
+ GET: function(config) {
+ config = OpenLayers.Util.extend(config, {method: "GET"});
+ return OpenLayers.Request.issue(config);
+ },
+
+ /**
+ * APIMethod: POST
+ * Send a POST request. Additional configuration properties are
+ * documented in the <issue> method, with the method property set
+ * to POST and "Content-Type" header set to "application/xml".
+ *
+ * Parameters:
+ * config - {Object} Object with properties for configuring the request.
+ * See the <issue> method for documentation of allowed properties. The
+ * default "Content-Type" header will be set to "application-xml" if
+ * none is provided. This object is modified and should not be reused.
+ *
+ * Returns:
+ * {XMLHttpRequest} Request object.
+ */
+ POST: function(config) {
+ config = OpenLayers.Util.extend(config, {method: "POST"});
+ // set content type to application/xml if it isn't already set
+ config.headers = config.headers ? config.headers : {};
+ if(!("CONTENT-TYPE" in OpenLayers.Util.upperCaseObject(config.headers))) {
+ config.headers["Content-Type"] = "application/xml";
+ }
+ return OpenLayers.Request.issue(config);
+ },
+
+ /**
+ * APIMethod: PUT
+ * Send an HTTP PUT request. Additional configuration properties are
+ * documented in the <issue> method, with the method property set
+ * to PUT and "Content-Type" header set to "application/xml".
+ *
+ * Parameters:
+ * config - {Object} Object with properties for configuring the request.
+ * See the <issue> method for documentation of allowed properties. The
+ * default "Content-Type" header will be set to "application-xml" if
+ * none is provided. This object is modified and should not be reused.
+ *
+ * Returns:
+ * {XMLHttpRequest} Request object.
+ */
+ PUT: function(config) {
+ config = OpenLayers.Util.extend(config, {method: "PUT"});
+ // set content type to application/xml if it isn't already set
+ config.headers = config.headers ? config.headers : {};
+ if(!("CONTENT-TYPE" in OpenLayers.Util.upperCaseObject(config.headers))) {
+ config.headers["Content-Type"] = "application/xml";
+ }
+ return OpenLayers.Request.issue(config);
+ },
+
+ /**
+ * APIMethod: DELETE
+ * Send an HTTP DELETE request. Additional configuration properties are
+ * documented in the <issue> method, with the method property set
+ * to DELETE.
+ *
+ * Parameters:
+ * config - {Object} Object with properties for configuring the request.
+ * See the <issue> method for documentation of allowed properties.
+ * This object is modified and should not be reused.
+ *
+ * Returns:
+ * {XMLHttpRequest} Request object.
+ */
+ DELETE: function(config) {
+ config = OpenLayers.Util.extend(config, {method: "DELETE"});
+ return OpenLayers.Request.issue(config);
+ },
+
+ /**
+ * APIMethod: HEAD
+ * Send an HTTP HEAD request. Additional configuration properties are
+ * documented in the <issue> method, with the method property set
+ * to HEAD.
+ *
+ * Parameters:
+ * config - {Object} Object with properties for configuring the request.
+ * See the <issue> method for documentation of allowed properties.
+ * This object is modified and should not be reused.
+ *
+ * Returns:
+ * {XMLHttpRequest} Request object.
+ */
+ HEAD: function(config) {
+ config = OpenLayers.Util.extend(config, {method: "HEAD"});
+ return OpenLayers.Request.issue(config);
+ },
+
+ /**
+ * APIMethod: OPTIONS
+ * Send an HTTP OPTIONS request. Additional configuration properties are
+ * documented in the <issue> method, with the method property set
+ * to OPTIONS.
+ *
+ * Parameters:
+ * config - {Object} Object with properties for configuring the request.
+ * See the <issue> method for documentation of allowed properties.
+ * This object is modified and should not be reused.
+ *
+ * Returns:
+ * {XMLHttpRequest} Request object.
+ */
+ OPTIONS: function(config) {
+ config = OpenLayers.Util.extend(config, {method: "OPTIONS"});
+ return OpenLayers.Request.issue(config);
+ }
+
+};
+/* ======================================================================
+ OpenLayers/Strategy.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. */
+
+/**
+ * Class: OpenLayers.Strategy
+ * Abstract vector layer strategy class. Not to be instantiated directly. Use
+ * one of the strategy subclasses instead.
+ */
+OpenLayers.Strategy = OpenLayers.Class({
+
+ /**
+ * Property: layer
+ * {<OpenLayers.Layer.Vector>}
+ */
+ layer: null,
+
+ /**
+ * Property: options
+ * {Object} Any options sent to the constructor.
+ */
+ options: null,
+
+ /**
+ * Property: active
+ * {Boolean} The control is active.
+ */
+ active: null,
+
+ /**
+ * Property: autoActivate
+ * {Boolean} The creator of the strategy can set autoActivate to false
+ * to fully control when the protocol is activated and deactivated.
+ * Defaults to true.
+ */
+ autoActivate: true,
+
+ /**
+ * Property: autoDestroy
+ * {Boolean} The creator of the strategy can set autoDestroy to false
+ * to fully control when the strategy is destroyed. Defaults to
+ * true.
+ */
+ autoDestroy: true,
+
+ /**
+ * Constructor: OpenLayers.Strategy
+ * Abstract class for vector strategies. Create instances of a subclass.
+ *
+ * Parameters:
+ * options - {Object} Optional object whose properties will be set on the
+ * instance.
+ */
+ initialize: function(options) {
+ OpenLayers.Util.extend(this, options);
+ this.options = options;
+ // set the active property here, so that user cannot override it
+ this.active = false;
+ },
+
+ /**
+ * APIMethod: destroy
+ * Clean up the strategy.
+ */
+ destroy: function() {
+ this.deactivate();
+ this.layer = null;
+ this.options = null;
+ },
+
+ /**
+ * Method: setLayer.
+ *
+ * Parameters:
+ * {<OpenLayers.Layer.Vector>}
+ */
+ setLayer: function(layer) {
+ this.layer = layer;
+ },
+
+ /**
+ * Method: activate
+ * Activate the strategy. Register any listeners, do appropriate setup.
+ *
+ * Returns:
+ * {Boolean} True if the strategy was successfully activated or false if
+ * the strategy was already active.
+ */
+ activate: function() {
+ if (!this.active) {
+ this.active = true;
+ return true;
+ }
+ return false;
+ },
+
+ /**
+ * Method: deactivate
+ * Deactivate the strategy. Unregister any listeners, do appropriate
+ * tear-down.
+ *
+ * Returns:
+ * {Boolean} True if the strategy was successfully deactivated or false if
+ * the strategy was already inactive.
+ */
+ deactivate: function() {
+ if (this.active) {
+ this.active = false;
+ return true;
+ }
+ return false;
+ },
+
+ CLASS_NAME: "OpenLayers.Strategy"
+});
+/* ======================================================================
OpenLayers/Tween.js
====================================================================== */
@@ -5422,6 +7131,254 @@
CLASS_NAME: "OpenLayers.Easing.Quad"
};
/* ======================================================================
+ Rico/Color.js
+ ====================================================================== */
+
+/*
+ * This file has been edited substantially from the Rico-released version by
+ * the OpenLayers development team.
+ *
+ * This file is licensed under the Apache License, Version 2.0.
+ */
+OpenLayers.Rico.Color = OpenLayers.Class({
+
+ initialize: function(red, green, blue) {
+ this.rgb = { r: red, g : green, b : blue };
+ },
+
+ setRed: function(r) {
+ this.rgb.r = r;
+ },
+
+ setGreen: function(g) {
+ this.rgb.g = g;
+ },
+
+ setBlue: function(b) {
+ this.rgb.b = b;
+ },
+
+ setHue: function(h) {
+
+ // get an HSB model, and set the new hue...
+ var hsb = this.asHSB();
+ hsb.h = h;
+
+ // convert back to RGB...
+ this.rgb = OpenLayers.Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b);
+ },
+
+ setSaturation: function(s) {
+ // get an HSB model, and set the new hue...
+ var hsb = this.asHSB();
+ hsb.s = s;
+
+ // convert back to RGB and set values...
+ this.rgb = OpenLayers.Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b);
+ },
+
+ setBrightness: function(b) {
+ // get an HSB model, and set the new hue...
+ var hsb = this.asHSB();
+ hsb.b = b;
+
+ // convert back to RGB and set values...
+ this.rgb = OpenLayers.Rico.Color.HSBtoRGB( hsb.h, hsb.s, hsb.b );
+ },
+
+ darken: function(percent) {
+ var hsb = this.asHSB();
+ this.rgb = OpenLayers.Rico.Color.HSBtoRGB(hsb.h, hsb.s, Math.max(hsb.b - percent,0));
+ },
+
+ brighten: function(percent) {
+ var hsb = this.asHSB();
+ this.rgb = OpenLayers.Rico.Color.HSBtoRGB(hsb.h, hsb.s, Math.min(hsb.b + percent,1));
+ },
+
+ blend: function(other) {
+ this.rgb.r = Math.floor((this.rgb.r + other.rgb.r)/2);
+ this.rgb.g = Math.floor((this.rgb.g + other.rgb.g)/2);
+ this.rgb.b = Math.floor((this.rgb.b + other.rgb.b)/2);
+ },
+
+ isBright: function() {
+ var hsb = this.asHSB();
+ return this.asHSB().b > 0.5;
+ },
+
+ isDark: function() {
+ return ! this.isBright();
+ },
+
+ asRGB: function() {
+ return "rgb(" + this.rgb.r + "," + this.rgb.g + "," + this.rgb.b + ")";
+ },
+
+ asHex: function() {
+ return "#" + this.rgb.r.toColorPart() + this.rgb.g.toColorPart() + this.rgb.b.toColorPart();
+ },
+
+ asHSB: function() {
+ return OpenLayers.Rico.Color.RGBtoHSB(this.rgb.r, this.rgb.g, this.rgb.b);
+ },
+
+ toString: function() {
+ return this.asHex();
+ }
+
+});
+
+OpenLayers.Rico.Color.createFromHex = function(hexCode) {
+ if(hexCode.length==4) {
+ var shortHexCode = hexCode;
+ var hexCode = '#';
+ for(var i=1;i<4;i++) {
+ hexCode += (shortHexCode.charAt(i) +
+shortHexCode.charAt(i));
+ }
+ }
+ if ( hexCode.indexOf('#') == 0 ) {
+ hexCode = hexCode.substring(1);
+ }
+ var red = hexCode.substring(0,2);
+ var green = hexCode.substring(2,4);
+ var blue = hexCode.substring(4,6);
+ return new OpenLayers.Rico.Color( parseInt(red,16), parseInt(green,16), parseInt(blue,16) );
+};
+
+/**
+ * Factory method for creating a color from the background of
+ * an HTML element.
+ */
+OpenLayers.Rico.Color.createColorFromBackground = function(elem) {
+
+ var actualColor =
+ RicoUtil.getElementsComputedStyle(OpenLayers.Util.getElement(elem),
+ "backgroundColor",
+ "background-color");
+
+ if ( actualColor == "transparent" && elem.parentNode ) {
+ return OpenLayers.Rico.Color.createColorFromBackground(elem.parentNode);
+ }
+ if ( actualColor == null ) {
+ return new OpenLayers.Rico.Color(255,255,255);
+ }
+ if ( actualColor.indexOf("rgb(") == 0 ) {
+ var colors = actualColor.substring(4, actualColor.length - 1 );
+ var colorArray = colors.split(",");
+ return new OpenLayers.Rico.Color( parseInt( colorArray[0] ),
+ parseInt( colorArray[1] ),
+ parseInt( colorArray[2] ) );
+
+ }
+ else if ( actualColor.indexOf("#") == 0 ) {
+ return OpenLayers.Rico.Color.createFromHex(actualColor);
+ }
+ else {
+ return new OpenLayers.Rico.Color(255,255,255);
+ }
+};
+
+OpenLayers.Rico.Color.HSBtoRGB = function(hue, saturation, brightness) {
+
+ var red = 0;
+ var green = 0;
+ var blue = 0;
+
+ if (saturation == 0) {
+ red = parseInt(brightness * 255.0 + 0.5);
+ green = red;
+ blue = red;
+ }
+ else {
+ var h = (hue - Math.floor(hue)) * 6.0;
+ var f = h - Math.floor(h);
+ var p = brightness * (1.0 - saturation);
+ var q = brightness * (1.0 - saturation * f);
+ var t = brightness * (1.0 - (saturation * (1.0 - f)));
+
+ switch (parseInt(h)) {
+ case 0:
+ red = (brightness * 255.0 + 0.5);
+ green = (t * 255.0 + 0.5);
+ blue = (p * 255.0 + 0.5);
+ break;
+ case 1:
+ red = (q * 255.0 + 0.5);
+ green = (brightness * 255.0 + 0.5);
+ blue = (p * 255.0 + 0.5);
+ break;
+ case 2:
+ red = (p * 255.0 + 0.5);
+ green = (brightness * 255.0 + 0.5);
+ blue = (t * 255.0 + 0.5);
+ break;
+ case 3:
+ red = (p * 255.0 + 0.5);
+ green = (q * 255.0 + 0.5);
+ blue = (brightness * 255.0 + 0.5);
+ break;
+ case 4:
+ red = (t * 255.0 + 0.5);
+ green = (p * 255.0 + 0.5);
+ blue = (brightness * 255.0 + 0.5);
+ break;
+ case 5:
+ red = (brightness * 255.0 + 0.5);
+ green = (p * 255.0 + 0.5);
+ blue = (q * 255.0 + 0.5);
+ break;
+ }
+ }
+
+ return { r : parseInt(red), g : parseInt(green) , b : parseInt(blue) };
+};
+
+OpenLayers.Rico.Color.RGBtoHSB = function(r, g, b) {
+
+ var hue;
+ var saturation;
+ var brightness;
+
+ var cmax = (r > g) ? r : g;
+ if (b > cmax) {
+ cmax = b;
+ }
+ var cmin = (r < g) ? r : g;
+ if (b < cmin) {
+ cmin = b;
+ }
+ brightness = cmax / 255.0;
+ if (cmax != 0) {
+ saturation = (cmax - cmin)/cmax;
+ } else {
+ saturation = 0;
+ }
+ if (saturation == 0) {
+ hue = 0;
+ } else {
+ var redc = (cmax - r)/(cmax - cmin);
+ var greenc = (cmax - g)/(cmax - cmin);
+ var bluec = (cmax - b)/(cmax - cmin);
+
+ if (r == cmax) {
+ hue = bluec - greenc;
+ } else if (g == cmax) {
+ hue = 2.0 + redc - bluec;
+ } else {
+ hue = 4.0 + greenc - redc;
+ }
+ hue = hue / 6.0;
+ if (hue < 0) {
+ hue = hue + 1.0;
+ }
+ }
+
+ return { h : hue, s : saturation, b : brightness };
+};
+
+/* ======================================================================
OpenLayers/Control/ArgParser.js
====================================================================== */
@@ -5495,7 +7452,7 @@
OpenLayers.Control.prototype.setMap.apply(this, arguments);
//make sure we dont already have an arg parser attached
- for(var i=0; i< this.map.controls.length; i++) {
+ for(var i=0, len=this.map.controls.length; i<len; i++) {
var control = this.map.controls[i];
if ( (control != this) &&
(control.CLASS_NAME == "OpenLayers.Control.ArgParser") ) {
@@ -5568,7 +7525,7 @@
if (this.layers.length == this.map.layers.length) {
this.map.events.unregister('addlayer', this, this.configureLayers);
- for(var i=0; i < this.layers.length; i++) {
+ for(var i=0, len=this.layers.length; i<len; i++) {
var layer = this.map.layers[i];
var c = this.layers.charAt(i);
@@ -5585,6 +7542,1145 @@
CLASS_NAME: "OpenLayers.Control.ArgParser"
});
/* ======================================================================
+ OpenLayers/Control/Attribution.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
+ */
+
+/**
+ * Class: OpenLayers.Control.Attribution
+ * Add attribution from layers to the map display. Uses 'attribution' property
+ * of each layer.
+ */
+OpenLayers.Control.Attribution =
+ OpenLayers.Class(OpenLayers.Control, {
+
+ /**
+ * APIProperty: seperator
+ * {String} String used to seperate layers.
+ */
+ separator: ", ",
+
+ /**
+ * Constructor: OpenLayers.Control.Attribution
+ *
+ * Parameters:
+ * options - {Object} Options for control.
+ */
+ initialize: function(options) {
+ OpenLayers.Control.prototype.initialize.apply(this, arguments);
+ },
+
+ /**
+ * Method: destroy
+ * Destroy control.
+ */
+ destroy: function() {
+ this.map.events.un({
+ "removelayer": this.updateAttribution,
+ "addlayer": this.updateAttribution,
+ "changelayer": this.updateAttribution,
+ "changebaselayer": this.updateAttribution,
+ scope: this
+ });
+
+ OpenLayers.Control.prototype.destroy.apply(this, arguments);
+ },
+
+ /**
+ * Method: draw
+ * Initialize control.
+ *
+ * Returns:
+ * {DOMElement} A reference to the DIV DOMElement containing the control
+ */
+ draw: function() {
+ OpenLayers.Control.prototype.draw.apply(this, arguments);
+
+ this.map.events.on({
+ 'changebaselayer': this.updateAttribution,
+ 'changelayer': this.updateAttribution,
+ 'addlayer': this.updateAttribution,
+ 'removelayer': this.updateAttribution,
+ scope: this
+ });
+ this.updateAttribution();
+
+ return this.div;
+ },
+
+ /**
+ * Method: updateAttribution
+ * Update attribution string.
+ */
+ updateAttribution: function() {
+ var attributions = [];
+ if (this.map && this.map.layers) {
+ for(var i=0, len=this.map.layers.length; i<len; i++) {
+ var layer = this.map.layers[i];
+ if (layer.attribution && layer.getVisibility()) {
+ attributions.push( layer.attribution );
+ }
+ }
+ this.div.innerHTML = attributions.join(this.separator);
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Control.Attribution"
+});
+/* ======================================================================
+ OpenLayers/Control/Button.js
+ ====================================================================== */
+
+/* Copyright (c) 2006-2007 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/Control.js
+ */
+
+/**
+ * Class: OpenLayers.Control.Button
+ * A very simple button controlfor use with <OpenLayers.Control.Panel>.
+ * When clicked, the function trigger() is executed.
+ *
+ * Inherits from:
+ * - <OpenLayers.Control>
+ *
+ * Use:
+ * (code)
+ * var button = new OpenLayers.Control.Button({
+ * displayClass: "MyButton", trigger: myFunction
+ * });
+ * panel.addControls([button]);
+ * (end)
+ *
+ * Will create a button with CSS class MyButtonItemInactive, that
+ * will call the function MyFunction() when clicked.
+ */
+OpenLayers.Control.Button = OpenLayers.Class(OpenLayers.Control, {
+ /**
+ * Property: type
+ * {Integer} OpenLayers.Control.TYPE_BUTTON.
+ */
+ type: OpenLayers.Control.TYPE_BUTTON,
+
+ /**
+ * Method: trigger
+ * Called by a control panel when the button is clicked.
+ */
+ trigger: function() {},
+
+ CLASS_NAME: "OpenLayers.Control.Button"
+});
+/* ======================================================================
+ OpenLayers/Control/LayerSwitcher.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
+ */
+
+/**
+ * Class: OpenLayers.Control.LayerSwitcher
+ *
+ * Inherits from:
+ * - <OpenLayers.Control>
+ */
+OpenLayers.Control.LayerSwitcher =
+ OpenLayers.Class(OpenLayers.Control, {
+
+ /**
+ * Property: activeColor
+ * {String}
+ */
+ activeColor: "darkblue",
+
+ /**
+ * Property: layerStates
+ * {Array(Object)} Basically a copy of the "state" of the map's layers
+ * the last time the control was drawn. We have this in order to avoid
+ * unnecessarily redrawing the control.
+ */
+ layerStates: null,
+
+
+ // DOM Elements
+
+ /**
+ * Property: layersDiv
+ * {DOMElement}
+ */
+ layersDiv: null,
+
+ /**
+ * Property: baseLayersDiv
+ * {DOMElement}
+ */
+ baseLayersDiv: null,
+
+ /**
+ * Property: baseLayers
+ * {Array(<OpenLayers.Layer>)}
+ */
+ baseLayers: null,
+
+
+ /**
+ * Property: dataLbl
+ * {DOMElement}
+ */
+ dataLbl: null,
+
+ /**
+ * Property: dataLayersDiv
+ * {DOMElement}
+ */
+ dataLayersDiv: null,
+
+ /**
+ * Property: dataLayers
+ * {Array(<OpenLayers.Layer>)}
+ */
+ dataLayers: null,
+
+
+ /**
+ * Property: minimizeDiv
+ * {DOMElement}
+ */
+ minimizeDiv: null,
+
+ /**
+ * Property: maximizeDiv
+ * {DOMElement}
+ */
+ maximizeDiv: null,
+
+ /**
+ * APIProperty: ascending
+ * {Boolean}
+ */
+ ascending: true,
+
+ /**
+ * Constructor: OpenLayers.Control.LayerSwitcher
+ *
+ * Parameters:
+ * options - {Object}
+ */
+ initialize: function(options) {
+ OpenLayers.Control.prototype.initialize.apply(this, arguments);
+ this.layerStates = [];
+ },
+
+ /**
+ * APIMethod: destroy
+ */
+ destroy: function() {
+
+ OpenLayers.Event.stopObservingElement(this.div);
+
+ OpenLayers.Event.stopObservingElement(this.minimizeDiv);
+ OpenLayers.Event.stopObservingElement(this.maximizeDiv);
+
+ //clear out layers info and unregister their events
+ this.clearLayersArray("base");
+ this.clearLayersArray("data");
+
+ this.map.events.un({
+ "addlayer": this.redraw,
+ "changelayer": this.redraw,
+ "removelayer": this.redraw,
+ "changebaselayer": this.redraw,
+ scope: this
+ });
+
+ OpenLayers.Control.prototype.destroy.apply(this, arguments);
+ },
+
+ /**
+ * Method: setMap
+ *
+ * Properties:
+ * map - {<OpenLayers.Map>}
+ */
+ setMap: function(map) {
+ OpenLayers.Control.prototype.setMap.apply(this, arguments);
+
+ this.map.events.on({
+ "addlayer": this.redraw,
+ "changelayer": this.redraw,
+ "removelayer": this.redraw,
+ "changebaselayer": this.redraw,
+ scope: this
+ });
+ },
+
+ /**
+ * Method: draw
+ *
+ * Returns:
+ * {DOMElement} A reference to the DIV DOMElement containing the
+ * switcher tabs.
+ */
+ draw: function() {
+ OpenLayers.Control.prototype.draw.apply(this);
+
+ // create layout divs
+ this.loadContents();
+
+ // set mode to minimize
+ if(!this.outsideViewport) {
+ this.minimizeControl();
+ }
+
+ // populate div with current info
+ this.redraw();
+
+ return this.div;
+ },
+
+ /**
+ * Method: clearLayersArray
+ * User specifies either "base" or "data". we then clear all the
+ * corresponding listeners, the div, and reinitialize a new array.
+ *
+ * Parameters:
+ * layersType - {String}
+ */
+ clearLayersArray: function(layersType) {
+ var layers = this[layersType + "Layers"];
+ if (layers) {
+ for(var i=0, len=layers.length; i<len ; i++) {
+ var layer = layers[i];
+ OpenLayers.Event.stopObservingElement(layer.inputElem);
+ OpenLayers.Event.stopObservingElement(layer.labelSpan);
+ }
+ }
+ this[layersType + "LayersDiv"].innerHTML = "";
+ this[layersType + "Layers"] = [];
+ },
+
+
+ /**
+ * Method: checkRedraw
+ * Checks if the layer state has changed since the last redraw() call.
+ *
+ * Returns:
+ * {Boolean} The layer state changed since the last redraw() call.
+ */
+ checkRedraw: function() {
+ var redraw = false;
+ if ( !this.layerStates.length ||
+ (this.map.layers.length != this.layerStates.length) ) {
+ redraw = true;
+ } else {
+ for (var i=0, len=this.layerStates.length; i<len; i++) {
+ var layerState = this.layerStates[i];
+ var layer = this.map.layers[i];
+ if ( (layerState.name != layer.name) ||
+ (layerState.inRange != layer.inRange) ||
+ (layerState.id != layer.id) ||
+ (layerState.visibility != layer.visibility) ) {
+ redraw = true;
+ break;
+ }
+ }
+ }
+ return redraw;
+ },
+
+ /**
+ * Method: redraw
+ * Goes through and takes the current state of the Map and rebuilds the
+ * control to display that state. Groups base layers into a
+ * radio-button group and lists each data layer with a checkbox.
+ *
+ * Returns:
+ * {DOMElement} A reference to the DIV DOMElement containing the control
+ */
+ redraw: function() {
+ //if the state hasn't changed since last redraw, no need
+ // to do anything. Just return the existing div.
+ if (!this.checkRedraw()) {
+ return this.div;
+ }
+
+ //clear out previous layers
+ this.clearLayersArray("base");
+ this.clearLayersArray("data");
+
+ var containsOverlays = false;
+ var containsBaseLayers = false;
+
+ // Save state -- for checking layer if the map state changed.
+ // We save this before redrawing, because in the process of redrawing
+ // we will trigger more visibility changes, and we want to not redraw
+ // and enter an infinite loop.
+ var len = this.map.layers.length;
+ this.layerStates = new Array(len);
+ for (var i=0; i <len; i++) {
+ var layer = this.map.layers[i];
+ this.layerStates[i] = {
+ 'name': layer.name,
+ 'visibility': layer.visibility,
+ 'inRange': layer.inRange,
+ 'id': layer.id
+ };
+ }
+
+ var layers = this.map.layers.slice();
+ if (!this.ascending) { layers.reverse(); }
+ for(var i=0, len=layers.length; i<len; i++) {
+ var layer = layers[i];
+ var baseLayer = layer.isBaseLayer;
+
+ if (layer.displayInLayerSwitcher) {
+
+ if (baseLayer) {
+ containsBaseLayers = true;
+ } else {
+ containsOverlays = true;
+ }
+
+ // only check a baselayer if it is *the* baselayer, check data
+ // layers if they are visible
+ var checked = (baseLayer) ? (layer == this.map.baseLayer)
+ : layer.getVisibility();
+
+ // create input element
+ var inputElem = document.createElement("input");
+ inputElem.id = this.id + "_input_" + layer.name;
+ inputElem.name = (baseLayer) ? "baseLayers" : layer.name;
+ inputElem.type = (baseLayer) ? "radio" : "checkbox";
+ inputElem.value = layer.name;
+ inputElem.checked = checked;
+ inputElem.defaultChecked = checked;
+
+ if (!baseLayer && !layer.inRange) {
+ inputElem.disabled = true;
+ }
+ var context = {
+ 'inputElem': inputElem,
+ 'layer': layer,
+ 'layerSwitcher': this
+ };
+ OpenLayers.Event.observe(inputElem, "mouseup",
+ OpenLayers.Function.bindAsEventListener(this.onInputClick,
+ context)
+ );
+
+ // create span
+ var labelSpan = document.createElement("span");
+ if (!baseLayer && !layer.inRange) {
+ labelSpan.style.color = "gray";
+ }
+ labelSpan.innerHTML = layer.name;
+ labelSpan.style.verticalAlign = (baseLayer) ? "bottom"
+ : "baseline";
+ OpenLayers.Event.observe(labelSpan, "click",
+ OpenLayers.Function.bindAsEventListener(this.onInputClick,
+ context)
+ );
+ // create line break
+ var br = document.createElement("br");
+
+
+ var groupArray = (baseLayer) ? this.baseLayers
+ : this.dataLayers;
+ groupArray.push({
+ 'layer': layer,
+ 'inputElem': inputElem,
+ 'labelSpan': labelSpan
+ });
+
+
+ var groupDiv = (baseLayer) ? this.baseLayersDiv
+ : this.dataLayersDiv;
+ groupDiv.appendChild(inputElem);
+ groupDiv.appendChild(labelSpan);
+ groupDiv.appendChild(br);
+ }
+ }
+
+ // if no overlays, dont display the overlay label
+ this.dataLbl.style.display = (containsOverlays) ? "" : "none";
+
+ // if no baselayers, dont display the baselayer label
+ this.baseLbl.style.display = (containsBaseLayers) ? "" : "none";
+
+ return this.div;
+ },
+
+ /**
+ * Method:
+ * A label has been clicked, check or uncheck its corresponding input
+ *
+ * Parameters:
+ * e - {Event}
+ *
+ * Context:
+ * - {DOMElement} inputElem
+ * - {<OpenLayers.Control.LayerSwitcher>} layerSwitcher
+ * - {<OpenLayers.Layer>} layer
+ */
+
+ onInputClick: function(e) {
+
+ if (!this.inputElem.disabled) {
+ if (this.inputElem.type == "radio") {
+ this.inputElem.checked = true;
+ this.layer.map.setBaseLayer(this.layer);
+ } else {
+ this.inputElem.checked = !this.inputElem.checked;
+ this.layerSwitcher.updateMap();
+ }
+ }
+ OpenLayers.Event.stop(e);
+ },
+
+ /**
+ * Method: onLayerClick
+ * Need to update the map accordingly whenever user clicks in either of
+ * the layers.
+ *
+ * Parameters:
+ * e - {Event}
+ */
+ onLayerClick: function(e) {
+ this.updateMap();
+ },
+
+
+ /**
+ * Method: updateMap
+ * Cycles through the loaded data and base layer input arrays and makes
+ * the necessary calls to the Map object such that that the map's
+ * visual state corresponds to what the user has selected in
+ * the control.
+ */
+ updateMap: function() {
+
+ // set the newly selected base layer
+ for(var i=0, len=this.baseLayers.length; i<len; i++) {
+ var layerEntry = this.baseLayers[i];
+ if (layerEntry.inputElem.checked) {
+ this.map.setBaseLayer(layerEntry.layer, false);
+ }
+ }
+
+ // set the correct visibilities for the overlays
+ for(var i=0, len=this.dataLayers.length; i<len; i++) {
+ var layerEntry = this.dataLayers[i];
+ layerEntry.layer.setVisibility(layerEntry.inputElem.checked);
+ }
+
+ },
+
+ /**
+ * Method: maximizeControl
+ * Set up the labels and divs for the control
+ *
+ * Parameters:
+ * e - {Event}
+ */
+ maximizeControl: function(e) {
+
+ //HACK HACK HACK - find a way to auto-size this layerswitcher
+ this.div.style.width = "20em";
+ this.div.style.height = "";
+
+ this.showControls(false);
+
+ if (e != null) {
+ OpenLayers.Event.stop(e);
+ }
+ },
+
+ /**
+ * Method: minimizeControl
+ * Hide all the contents of the control, shrink the size,
+ * add the maximize icon
+ *
+ * Parameters:
+ * e - {Event}
+ */
+ minimizeControl: function(e) {
+
+ this.div.style.width = "0px";
+ this.div.style.height = "0px";
+
+ this.showControls(true);
+
+ if (e != null) {
+ OpenLayers.Event.stop(e);
+ }
+ },
+
+ /**
+ * Method: showControls
+ * Hide/Show all LayerSwitcher controls depending on whether we are
+ * minimized or not
+ *
+ * Parameters:
+ * minimize - {Boolean}
+ */
+ showControls: function(minimize) {
+
+ this.maximizeDiv.style.display = minimize ? "" : "none";
+ this.minimizeDiv.style.display = minimize ? "none" : "";
+
+ this.layersDiv.style.display = minimize ? "none" : "";
+ },
+
+ /**
+ * Method: loadContents
+ * Set up the labels and divs for the control
+ */
+ loadContents: function() {
+
+ //configure main div
+ this.div.style.position = "absolute";
+ this.div.style.top = "25px";
+ this.div.style.right = "0px";
+ this.div.style.left = "";
+ this.div.style.fontFamily = "sans-serif";
+ this.div.style.fontWeight = "bold";
+ this.div.style.marginTop = "3px";
+ this.div.style.marginLeft = "3px";
+ this.div.style.marginBottom = "3px";
+ this.div.style.fontSize = "smaller";
+ this.div.style.color = "white";
+ this.div.style.backgroundColor = "transparent";
+
+ OpenLayers.Event.observe(this.div, "mouseup",
+ OpenLayers.Function.bindAsEventListener(this.mouseUp, this));
+ OpenLayers.Event.observe(this.div, "click",
+ this.ignoreEvent);
+ OpenLayers.Event.observe(this.div, "mousedown",
+ OpenLayers.Function.bindAsEventListener(this.mouseDown, this));
+ OpenLayers.Event.observe(this.div, "dblclick", this.ignoreEvent);
+
+
+ // layers list div
+ this.layersDiv = document.createElement("div");
+ this.layersDiv.id = this.id + "_layersDiv";
+ this.layersDiv.style.paddingTop = "5px";
+ this.layersDiv.style.paddingLeft = "10px";
+ this.layersDiv.style.paddingBottom = "5px";
+ this.layersDiv.style.paddingRight = "75px";
+ this.layersDiv.style.backgroundColor = this.activeColor;
+
+ // had to set width/height to get transparency in IE to work.
+ // thanks -- http://jszen.blogspot.com/2005/04/ie6-opacity-filter-caveat.html
+ //
+ this.layersDiv.style.width = "100%";
+ this.layersDiv.style.height = "100%";
+
+
+ this.baseLbl = document.createElement("div");
+ this.baseLbl.innerHTML = OpenLayers.i18n("baseLayer");
+ this.baseLbl.style.marginTop = "3px";
+ this.baseLbl.style.marginLeft = "3px";
+ this.baseLbl.style.marginBottom = "3px";
+
+ this.baseLayersDiv = document.createElement("div");
+ this.baseLayersDiv.style.paddingLeft = "10px";
+ /*OpenLayers.Event.observe(this.baseLayersDiv, "click",
+ OpenLayers.Function.bindAsEventListener(this.onLayerClick, this));
+ */
+
+
+ this.dataLbl = document.createElement("div");
+ this.dataLbl.innerHTML = OpenLayers.i18n("overlays");
+ this.dataLbl.style.marginTop = "3px";
+ this.dataLbl.style.marginLeft = "3px";
+ this.dataLbl.style.marginBottom = "3px";
+
+ this.dataLayersDiv = document.createElement("div");
+ this.dataLayersDiv.style.paddingLeft = "10px";
+
+ if (this.ascending) {
+ this.layersDiv.appendChild(this.baseLbl);
+ this.layersDiv.appendChild(this.baseLayersDiv);
+ this.layersDiv.appendChild(this.dataLbl);
+ this.layersDiv.appendChild(this.dataLayersDiv);
+ } else {
+ this.layersDiv.appendChild(this.dataLbl);
+ this.layersDiv.appendChild(this.dataLayersDiv);
+ this.layersDiv.appendChild(this.baseLbl);
+ this.layersDiv.appendChild(this.baseLayersDiv);
+ }
+
+ this.div.appendChild(this.layersDiv);
+
+ OpenLayers.Rico.Corner.round(this.div, {corners: "tl bl",
+ bgColor: "transparent",
+ color: this.activeColor,
+ blend: false});
+
+ OpenLayers.Rico.Corner.changeOpacity(this.layersDiv, 0.75);
+
+ var imgLocation = OpenLayers.Util.getImagesLocation();
+ var sz = new OpenLayers.Size(18,18);
+
+ // maximize button div
+ var img = imgLocation + 'layer-switcher-maximize.png';
+ this.maximizeDiv = OpenLayers.Util.createAlphaImageDiv(
+ "OpenLayers_Control_MaximizeDiv",
+ null,
+ sz,
+ img,
+ "absolute");
+ this.maximizeDiv.style.top = "5px";
+ this.maximizeDiv.style.right = "0px";
+ this.maximizeDiv.style.left = "";
+ this.maximizeDiv.style.display = "none";
+ OpenLayers.Event.observe(this.maximizeDiv, "click",
+ OpenLayers.Function.bindAsEventListener(this.maximizeControl, this)
+ );
+
+ this.div.appendChild(this.maximizeDiv);
+
+ // minimize button div
+ var img = imgLocation + 'layer-switcher-minimize.png';
+ var sz = new OpenLayers.Size(18,18);
+ this.minimizeDiv = OpenLayers.Util.createAlphaImageDiv(
+ "OpenLayers_Control_MinimizeDiv",
+ null,
+ sz,
+ img,
+ "absolute");
+ this.minimizeDiv.style.top = "5px";
+ this.minimizeDiv.style.right = "0px";
+ this.minimizeDiv.style.left = "";
+ this.minimizeDiv.style.display = "none";
+ OpenLayers.Event.observe(this.minimizeDiv, "click",
+ OpenLayers.Function.bindAsEventListener(this.minimizeControl, this)
+ );
+
+ this.div.appendChild(this.minimizeDiv);
+ },
+
+ /**
+ * Method: ignoreEvent
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ ignoreEvent: function(evt) {
+ OpenLayers.Event.stop(evt);
+ },
+
+ /**
+ * Method: mouseDown
+ * Register a local 'mouseDown' flag so that we'll know whether or not
+ * to ignore a mouseUp event
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ mouseDown: function(evt) {
+ this.isMouseDown = true;
+ this.ignoreEvent(evt);
+ },
+
+ /**
+ * Method: mouseUp
+ * If the 'isMouseDown' flag has been set, that means that the drag was
+ * started from within the LayerSwitcher control, and thus we can
+ * ignore the mouseup. Otherwise, let the Event continue.
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ mouseUp: function(evt) {
+ if (this.isMouseDown) {
+ this.isMouseDown = false;
+ this.ignoreEvent(evt);
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Control.LayerSwitcher"
+});
+/* ======================================================================
+ OpenLayers/Control/MouseDefaults.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
+ */
+
+/**
+ * Class: OpenLayers.Control.MouseDefaults
+ *
+ * Inherits from:
+ * - <OpenLayers.Control>
+ */
+OpenLayers.Control.MouseDefaults = OpenLayers.Class(OpenLayers.Control, {
+
+ /** WARNING WARNING WARNING!!!
+ This class is DEPRECATED in 2.4 and will be removed by 3.0.
+ If you need this functionality, use Control.Navigation instead!!! */
+
+ /**
+ * Property: performedDrag
+ * {Boolean}
+ */
+ performedDrag: false,
+
+ /**
+ * Property: wheelObserver
+ * {Function}
+ */
+ wheelObserver: null,
+
+ /**
+ * Constructor: OpenLayers.Control.MouseDefaults
+ */
+ initialize: function() {
+ OpenLayers.Control.prototype.initialize.apply(this, arguments);
+ },
+
+ /**
+ * APIMethod: destroy
+ */
+ destroy: function() {
+
+ if (this.handler) {
+ this.handler.destroy();
+ }
+ this.handler = null;
+
+ this.map.events.un({
+ "click": this.defaultClick,
+ "dblclick": this.defaultDblClick,
+ "mousedown": this.defaultMouseDown,
+ "mouseup": this.defaultMouseUp,
+ "mousemove": this.defaultMouseMove,
+ "mouseout": this.defaultMouseOut,
+ scope: this
+ });
+
+ //unregister mousewheel events specifically on the window and document
+ OpenLayers.Event.stopObserving(window, "DOMMouseScroll",
+ this.wheelObserver);
+ OpenLayers.Event.stopObserving(window, "mousewheel",
+ this.wheelObserver);
+ OpenLayers.Event.stopObserving(document, "mousewheel",
+ this.wheelObserver);
+ this.wheelObserver = null;
+
+ OpenLayers.Control.prototype.destroy.apply(this, arguments);
+ },
+
+ /**
+ * Method: draw
+ */
+ draw: function() {
+ this.map.events.on({
+ "click": this.defaultClick,
+ "dblclick": this.defaultDblClick,
+ "mousedown": this.defaultMouseDown,
+ "mouseup": this.defaultMouseUp,
+ "mousemove": this.defaultMouseMove,
+ "mouseout": this.defaultMouseOut,
+ scope: this
+ });
+
+ this.registerWheelEvents();
+
+ },
+
+ /**
+ * Method: registerWheelEvents
+ */
+ registerWheelEvents: function() {
+
+ this.wheelObserver = OpenLayers.Function.bindAsEventListener(
+ this.onWheelEvent, this
+ );
+
+ //register mousewheel events specifically on the window and document
+ OpenLayers.Event.observe(window, "DOMMouseScroll", this.wheelObserver);
+ OpenLayers.Event.observe(window, "mousewheel", this.wheelObserver);
+ OpenLayers.Event.observe(document, "mousewheel", this.wheelObserver);
+ },
+
+ /**
+ * Method: defaultClick
+ *
+ * Parameters:
+ * evt - {Event}
+ *
+ * Returns:
+ * {Boolean}
+ */
+ defaultClick: function (evt) {
+ if (!OpenLayers.Event.isLeftClick(evt)) {
+ return;
+ }
+ var notAfterDrag = !this.performedDrag;
+ this.performedDrag = false;
+ return notAfterDrag;
+ },
+
+ /**
+ * Method: defaultDblClick
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ defaultDblClick: function (evt) {
+ var newCenter = this.map.getLonLatFromViewPortPx( evt.xy );
+ this.map.setCenter(newCenter, this.map.zoom + 1);
+ OpenLayers.Event.stop(evt);
+ return false;
+ },
+
+ /**
+ * Method: defaultMouseDown
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ defaultMouseDown: function (evt) {
+ if (!OpenLayers.Event.isLeftClick(evt)) {
+ return;
+ }
+ this.mouseDragStart = evt.xy.clone();
+ this.performedDrag = false;
+ if (evt.shiftKey) {
+ this.map.div.style.cursor = "crosshair";
+ this.zoomBox = OpenLayers.Util.createDiv('zoomBox',
+ this.mouseDragStart,
+ null,
+ null,
+ "absolute",
+ "2px solid red");
+ this.zoomBox.style.backgroundColor = "white";
+ this.zoomBox.style.filter = "alpha(opacity=50)"; // IE
+ this.zoomBox.style.opacity = "0.50";
+ this.zoomBox.style.fontSize = "1px";
+ this.zoomBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
+ this.map.viewPortDiv.appendChild(this.zoomBox);
+ }
+ document.onselectstart=function() { return false; };
+ OpenLayers.Event.stop(evt);
+ },
+
+ /**
+ * Method: defaultMouseMove
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ defaultMouseMove: function (evt) {
+ // record the mouse position, used in onWheelEvent
+ this.mousePosition = evt.xy.clone();
+
+ if (this.mouseDragStart != null) {
+ if (this.zoomBox) {
+ var deltaX = Math.abs(this.mouseDragStart.x - evt.xy.x);
+ var deltaY = Math.abs(this.mouseDragStart.y - evt.xy.y);
+ this.zoomBox.style.width = Math.max(1, deltaX) + "px";
+ this.zoomBox.style.height = Math.max(1, deltaY) + "px";
+ if (evt.xy.x < this.mouseDragStart.x) {
+ this.zoomBox.style.left = evt.xy.x+"px";
+ }
+ if (evt.xy.y < this.mouseDragStart.y) {
+ this.zoomBox.style.top = evt.xy.y+"px";
+ }
+ } else {
+ var deltaX = this.mouseDragStart.x - evt.xy.x;
+ var deltaY = this.mouseDragStart.y - evt.xy.y;
+ var size = this.map.getSize();
+ var newXY = new OpenLayers.Pixel(size.w / 2 + deltaX,
+ size.h / 2 + deltaY);
+ var newCenter = this.map.getLonLatFromViewPortPx( newXY );
+ this.map.setCenter(newCenter, null, true);
+ this.mouseDragStart = evt.xy.clone();
+ this.map.div.style.cursor = "move";
+ }
+ this.performedDrag = true;
+ }
+ },
+
+ /**
+ * Method: defaultMouseUp
+ *
+ * Parameters:
+ * evt - {<OpenLayers.Event>}
+ */
+ defaultMouseUp: function (evt) {
+ if (!OpenLayers.Event.isLeftClick(evt)) {
+ return;
+ }
+ if (this.zoomBox) {
+ this.zoomBoxEnd(evt);
+ } else {
+ if (this.performedDrag) {
+ this.map.setCenter(this.map.center);
+ }
+ }
+ document.onselectstart=null;
+ this.mouseDragStart = null;
+ this.map.div.style.cursor = "";
+ },
+
+ /**
+ * Method: defaultMouseOut
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ defaultMouseOut: function (evt) {
+ if (this.mouseDragStart != null &&
+ OpenLayers.Util.mouseLeft(evt, this.map.div)) {
+ if (this.zoomBox) {
+ this.removeZoomBox();
+ }
+ this.mouseDragStart = null;
+ }
+ },
+
+
+ /**
+ * Method: defaultWheelUp
+ * User spun scroll wheel up
+ *
+ */
+ defaultWheelUp: function(evt) {
+ if (this.map.getZoom() <= this.map.getNumZoomLevels()) {
+ this.map.setCenter(this.map.getLonLatFromPixel(evt.xy),
+ this.map.getZoom() + 1);
+ }
+ },
+
+ /**
+ * Method: defaultWheelDown
+ * User spun scroll wheel down
+ */
+ defaultWheelDown: function(evt) {
+ if (this.map.getZoom() > 0) {
+ this.map.setCenter(this.map.getLonLatFromPixel(evt.xy),
+ this.map.getZoom() - 1);
+ }
+ },
+
+ /**
+ * Method: zoomBoxEnd
+ * Zoombox function.
+ */
+ zoomBoxEnd: function(evt) {
+ if (this.mouseDragStart != null) {
+ if (Math.abs(this.mouseDragStart.x - evt.xy.x) > 5 ||
+ Math.abs(this.mouseDragStart.y - evt.xy.y) > 5) {
+ var start = this.map.getLonLatFromViewPortPx( this.mouseDragStart );
+ var end = this.map.getLonLatFromViewPortPx( evt.xy );
+ var top = Math.max(start.lat, end.lat);
+ var bottom = Math.min(start.lat, end.lat);
+ var left = Math.min(start.lon, end.lon);
+ var right = Math.max(start.lon, end.lon);
+ var bounds = new OpenLayers.Bounds(left, bottom, right, top);
+ this.map.zoomToExtent(bounds);
+ } else {
+ var end = this.map.getLonLatFromViewPortPx( evt.xy );
+ this.map.setCenter(new OpenLayers.LonLat(
+ (end.lon),
+ (end.lat)
+ ), this.map.getZoom() + 1);
+ }
+ this.removeZoomBox();
+ }
+ },
+
+ /**
+ * Method: removeZoomBox
+ * Remove the zoombox from the screen and nullify our reference to it.
+ */
+ removeZoomBox: function() {
+ this.map.viewPortDiv.removeChild(this.zoomBox);
+ this.zoomBox = null;
+ },
+
+
+/**
+ * Mouse ScrollWheel code thanks to http://adomas.org/javascript-mouse-wheel/
+ */
+
+
+ /**
+ * Method: onWheelEvent
+ * Catch the wheel event and handle it xbrowserly
+ *
+ * Parameters:
+ * e - {Event}
+ */
+ onWheelEvent: function(e){
+
+ // first determine whether or not the wheeling was inside the map
+ var inMap = false;
+ var elem = OpenLayers.Event.element(e);
+ while(elem != null) {
+ if (this.map && elem == this.map.div) {
+ inMap = true;
+ break;
+ }
+ elem = elem.parentNode;
+ }
+
+ if (inMap) {
+
+ var delta = 0;
+ if (!e) {
+ e = window.event;
+ }
+ if (e.wheelDelta) {
+ delta = e.wheelDelta/120;
+ if (window.opera && window.opera.version() < 9.2) {
+ delta = -delta;
+ }
+ } else if (e.detail) {
+ delta = -e.detail / 3;
+ }
+ if (delta) {
+ // add the mouse position to the event because mozilla has a bug
+ // with clientX and clientY (see https://bugzilla.mozilla.org/show_bug.cgi?id=352179)
+ // getLonLatFromViewPortPx(e) returns wrong values
+ e.xy = this.mousePosition;
+
+ if (delta < 0) {
+ this.defaultWheelDown(e);
+ } else {
+ this.defaultWheelUp(e);
+ }
+ }
+
+ //only wheel the map, not the window
+ OpenLayers.Event.stop(e);
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Control.MouseDefaults"
+});
+/* ======================================================================
OpenLayers/Control/MousePosition.js
====================================================================== */
@@ -5630,7 +8726,7 @@
* APIProperty: numDigits
* {Integer}
*/
- numdigits: 5,
+ numDigits: 5,
/**
* APIProperty: granularity
@@ -5734,7 +8830,7 @@
* lonLat - {<OpenLayers.LonLat>} Location to display
*/
formatOutput: function(lonLat) {
- var digits = parseInt(this.numdigits);
+ var digits = parseInt(this.numDigits);
var newHtml =
this.prefix +
lonLat.lon.toFixed(digits) +
@@ -5755,6 +8851,408 @@
CLASS_NAME: "OpenLayers.Control.MousePosition"
});
/* ======================================================================
+ OpenLayers/Control/NavigationHistory.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
+ */
+
+/**
+ * Class: OpenLayers.Control.NavigationHistory
+ * A navigation history control. This is a meta-control, that creates two
+ * dependent controls: <previous> and <next>. Call the trigger method
+ * on the <previous> and <next> controls to restore previous and next
+ * history states. The previous and next controls will become active
+ * when there are available states to restore and will become deactive
+ * when there are no states to restore.
+ *
+ * Inherits from:
+ * - <OpenLayers.Control>
+ */
+OpenLayers.Control.NavigationHistory = OpenLayers.Class(OpenLayers.Control, {
+
+ /**
+ * Property: type
+ * {String} Note that this control is not intended to be added directly
+ * to a control panel. Instead, add the sub-controls previous and
+ * next. These sub-controls are button type controls that activate
+ * and deactivate themselves. If this parent control is added to
+ * a panel, it will act as a toggle.
+ */
+ type: OpenLayers.Control.TYPE_TOGGLE,
+
+ /**
+ * APIProperty: previous
+ * {<OpenLayers.Control>} A button type control whose trigger method restores
+ * the previous state managed by this control.
+ */
+ previous: null,
+
+ /**
+ * APIProperty: previousOptions
+ * {Object} Set this property on the options argument of the constructor
+ * to set optional properties on the <previous> control.
+ */
+ previousOptions: null,
+
+ /**
+ * APIProperty: next
+ * {<OpenLayers.Control>} A button type control whose trigger method restores
+ * the next state managed by this control.
+ */
+ next: null,
+
+ /**
+ * APIProperty: nextOptions
+ * {Object} Set this property on the options argument of the constructor
+ * to set optional properties on the <next> control.
+ */
+ nextOptions: null,
+
+ /**
+ * APIProperty: limit
+ * {Integer} Optional limit on the number of history items to retain. If
+ * null, there is no limit. Default is 50.
+ */
+ limit: 50,
+
+ /**
+ * Property: activateOnDraw
+ * {Boolean} Activate the control when it is first added to the map.
+ * Default is true.
+ */
+ activateOnDraw: true,
+
+ /**
+ * Property: clearOnDeactivate
+ * {Boolean} Clear the history when the control is deactivated. Default
+ * is false.
+ */
+ clearOnDeactivate: false,
+
+ /**
+ * Property: registry
+ * {Object} An object with keys corresponding to event types. Values
+ * are functions that return an object representing the current state.
+ */
+ registry: null,
+
+ /**
+ * Property: nextStack
+ * {Array} Array of items in the history.
+ */
+ nextStack: null,
+
+ /**
+ * Property: previousStack
+ * {Array} List of items in the history. First item represents the current
+ * state.
+ */
+ previousStack: null,
+
+ /**
+ * Property: listeners
+ * {Object} An object containing properties corresponding to event types.
+ * This object is used to configure the control and is modified on
+ * construction.
+ */
+ listeners: null,
+
+ /**
+ * Property: restoring
+ * {Boolean} Currently restoring a history state. This is set to true
+ * before calling restore and set to false after restore returns.
+ */
+ restoring: false,
+
+ /**
+ * Constructor: OpenLayers.Control.NavigationHistory
+ *
+ * Parameters:
+ * options - {Object} An optional object whose properties will be used
+ * to extend the control.
+ */
+ initialize: function(options) {
+ OpenLayers.Control.prototype.initialize.apply(this, [options]);
+
+ this.registry = OpenLayers.Util.extend({
+ "moveend": function() {
+ return {
+ center: this.map.getCenter(),
+ resolution: this.map.getResolution()
+ };
+ }
+ }, this.registry);
+
+ this.clear();
+
+ var previousOptions = {
+ trigger: OpenLayers.Function.bind(this.previousTrigger, this),
+ displayClass: this.displayClass + " " + this.displayClass + "Previous"
+ };
+ OpenLayers.Util.extend(previousOptions, this.previousOptions);
+ this.previous = new OpenLayers.Control.Button(previousOptions);
+
+ var nextOptions = {
+ trigger: OpenLayers.Function.bind(this.nextTrigger, this),
+ displayClass: this.displayClass + " " + this.displayClass + "Next"
+ };
+ OpenLayers.Util.extend(nextOptions, this.nextOptions);
+ this.next = new OpenLayers.Control.Button(nextOptions);
+
+ },
+
+ /**
+ * Method: onPreviousChange
+ * Called when the previous history stack changes.
+ *
+ * Parameters:
+ * state - {Object} An object representing the state to be restored
+ * if previous is triggered again or null if no previous states remain.
+ * length - {Integer} The number of remaining previous states that can
+ * be restored.
+ */
+ onPreviousChange: function(state, length) {
+ if(state && !this.previous.active) {
+ this.previous.activate();
+ } else if(!state && this.previous.active) {
+ this.previous.deactivate();
+ }
+ },
+
+ /**
+ * Method: onNextChange
+ * Called when the next history stack changes.
+ *
+ * Parameters:
+ * state - {Object} An object representing the state to be restored
+ * if next is triggered again or null if no next states remain.
+ * length - {Integer} The number of remaining next states that can
+ * be restored.
+ */
+ onNextChange: function(state, length) {
+ if(state && !this.next.active) {
+ this.next.activate();
+ } else if(!state && this.next.active) {
+ this.next.deactivate();
+ }
+ },
+
+ /**
+ * APIMethod: destroy
+ * Destroy the control.
+ */
+ destroy: function() {
+ OpenLayers.Control.prototype.destroy.apply(this);
+ this.previous.destroy();
+ this.next.destroy();
+ this.deactivate();
+ for(var prop in this) {
+ this[prop] = null;
+ }
+ },
+
+ /**
+ * Method: setMap
+ * Set the map property for the control and <previous> and <next> child
+ * controls.
+ *
+ * Parameters:
+ * map - {<OpenLayers.Map>}
+ */
+ setMap: function(map) {
+ this.map = map;
+ this.next.setMap(map);
+ this.previous.setMap(map);
+ },
+
+ /**
+ * Method: draw
+ * Called when the control is added to the map.
+ */
+ draw: function() {
+ OpenLayers.Control.prototype.draw.apply(this, arguments);
+ this.next.draw();
+ this.previous.draw();
+ if(this.activateOnDraw) {
+ this.activate();
+ }
+ },
+
+ /**
+ * Method: previousTrigger
+ * Restore the previous state. If no items are in the previous history
+ * stack, this has no effect.
+ *
+ * Returns:
+ * {Object} Item representing state that was restored. Undefined if no
+ * items are in the previous history stack.
+ */
+ previousTrigger: function() {
+ var current = this.previousStack.shift();
+ var state = this.previousStack.shift();
+ if(state != undefined) {
+ this.nextStack.unshift(current);
+ this.previousStack.unshift(state);
+ this.restoring = true;
+ this.restore(state);
+ this.restoring = false;
+ this.onNextChange(this.nextStack[0], this.nextStack.length);
+ this.onPreviousChange(
+ this.previousStack[1], this.previousStack.length - 1
+ );
+ } else {
+ this.previousStack.unshift(current);
+ }
+ return state;
+ },
+
+ /**
+ * APIMethod: nextTrigger
+ * Restore the next state. If no items are in the next history
+ * stack, this has no effect. The next history stack is populated
+ * as states are restored from the previous history stack.
+ *
+ * Returns:
+ * {Object} Item representing state that was restored. Undefined if no
+ * items are in the next history stack.
+ */
+ nextTrigger: function() {
+ var state = this.nextStack.shift();
+ if(state != undefined) {
+ this.previousStack.unshift(state);
+ this.restoring = true;
+ this.restore(state);
+ this.restoring = false;
+ this.onNextChange(this.nextStack[0], this.nextStack.length);
+ this.onPreviousChange(
+ this.previousStack[1], this.previousStack.length - 1
+ );
+ }
+ return state;
+ },
+
+ /**
+ * APIMethod: clear
+ * Clear history.
+ */
+ clear: function() {
+ this.previousStack = [];
+ this.nextStack = [];
+ },
+
+ /**
+ * Method: restore
+ * Update the state with the given object.
+ *
+ * Parameters:
+ * state - {Object} An object representing the state to restore.
+ */
+ restore: function(state) {
+ var zoom = this.map.getZoomForResolution(state.resolution);
+ this.map.setCenter(state.center, zoom);
+ },
+
+ /**
+ * Method: setListeners
+ * Sets functions to be registered in the listeners object.
+ */
+ setListeners: function() {
+ this.listeners = {};
+ for(var type in this.registry) {
+ this.listeners[type] = OpenLayers.Function.bind(function() {
+ if(!this.restoring) {
+ var state = this.registry[type].apply(this, arguments);
+ this.previousStack.unshift(state);
+ if(this.previousStack.length > 1) {
+ this.onPreviousChange(
+ this.previousStack[1], this.previousStack.length - 1
+ );
+ }
+ if(this.previousStack.length > (this.limit + 1)) {
+ this.previousStack.pop();
+ }
+ if(this.nextStack.length > 0) {
+ this.nextStack = [];
+ this.onNextChange(null, 0);
+ }
+ }
+ return true;
+ }, this);
+ }
+ },
+
+ /**
+ * APIMethod: activate
+ * Activate the control. This registers any listeners.
+ *
+ * Returns:
+ * {Boolean} Control successfully activated.
+ */
+ activate: function() {
+ var activated = false;
+ if(this.map) {
+ if(OpenLayers.Control.prototype.activate.apply(this)) {
+ if(this.listeners == null) {
+ this.setListeners();
+ }
+ for(var type in this.listeners) {
+ this.map.events.register(type, this, this.listeners[type]);
+ }
+ activated = true;
+ if(this.previousStack.length == 0) {
+ this.initStack();
+ }
+ }
+ }
+ return activated;
+ },
+
+ /**
+ * Method: initStack
+ * Called after the control is activated if the previous history stack is
+ * empty.
+ */
+ initStack: function() {
+ if(this.map.getCenter()) {
+ this.listeners.moveend();
+ }
+ },
+
+ /**
+ * APIMethod: deactivate
+ * Deactivate the control. This unregisters any listeners.
+ *
+ * Returns:
+ * {Boolean} Control successfully deactivated.
+ */
+ deactivate: function() {
+ var deactivated = false;
+ if(this.map) {
+ if(OpenLayers.Control.prototype.deactivate.apply(this)) {
+ for(var type in this.listeners) {
+ this.map.events.unregister(
+ type, this, this.listeners[type]
+ );
+ }
+ if(this.clearOnDeactivate) {
+ this.clear();
+ }
+ deactivated = true;
+ }
+ }
+ return deactivated;
+ },
+
+ CLASS_NAME: "OpenLayers.Control.NavigationHistory"
+});
+
+/* ======================================================================
OpenLayers/Control/PanZoom.js
====================================================================== */
@@ -5871,7 +9369,7 @@
_addButton:function(id, img, xy, sz) {
var imgLocation = OpenLayers.Util.getImagesLocation() + img;
var btn = OpenLayers.Util.createAlphaImageDiv(
- "OpenLayers_Control_PanZoom_" + id,
+ this.id + "_" + id,
xy, sz, imgLocation, "absolute");
//we want to add the outer div
@@ -5959,6 +9457,373 @@
*/
OpenLayers.Control.PanZoom.Y = 4;
/* ======================================================================
+ OpenLayers/Control/Panel.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
+ */
+
+/**
+ * Class: OpenLayers.Control.Panel
+ *
+ * Inherits from:
+ * - <OpenLayers.Control>
+ */
+OpenLayers.Control.Panel = OpenLayers.Class(OpenLayers.Control, {
+ /**
+ * Property: controls
+ * {Array(<OpenLayers.Control>)}
+ */
+ controls: null,
+
+ /**
+ * APIProperty: defaultControl
+ * <OpenLayers.Control> The control which is activated when the control is
+ * activated (turned on), which also happens at instantiation.
+ */
+ defaultControl: null,
+
+ /**
+ * Constructor: OpenLayers.Control.Panel
+ * Create a new control panel.
+ *
+ * Parameters:
+ * options - {Object} An optional object whose properties will be used
+ * to extend the control.
+ */
+ initialize: function(options) {
+ OpenLayers.Control.prototype.initialize.apply(this, [options]);
+ this.controls = [];
+ },
+
+ /**
+ * APIMethod: destroy
+ */
+ destroy: function() {
+ OpenLayers.Control.prototype.destroy.apply(this, arguments);
+ for(var i = this.controls.length - 1 ; i >= 0; i--) {
+ if(this.controls[i].events) {
+ this.controls[i].events.un({
+ "activate": this.redraw,
+ "deactivate": this.redraw,
+ scope: this
+ });
+ }
+ OpenLayers.Event.stopObservingElement(this.controls[i].panel_div);
+ this.controls[i].panel_div = null;
+ }
+ },
+
+ /**
+ * APIMethod: activate
+ */
+ activate: function() {
+ if (OpenLayers.Control.prototype.activate.apply(this, arguments)) {
+ for(var i=0, len=this.controls.length; i<len; i++) {
+ if (this.controls[i] == this.defaultControl) {
+ this.controls[i].activate();
+ }
+ }
+ this.redraw();
+ return true;
+ } else {
+ return false;
+ }
+ },
+
+ /**
+ * APIMethod: deactivate
+ */
+ deactivate: function() {
+ if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {
+ for(var i=0, len=this.controls.length; i<len; i++) {
+ this.controls[i].deactivate();
+ }
+ return true;
+ } else {
+ return false;
+ }
+ },
+
+ /**
+ * Method: draw
+ *
+ * Returns:
+ * {DOMElement}
+ */
+ draw: function() {
+ OpenLayers.Control.prototype.draw.apply(this, arguments);
+ for (var i=0, len=this.controls.length; i<len; i++) {
+ this.map.addControl(this.controls[i]);
+ this.controls[i].deactivate();
+ this.controls[i].events.on({
+ "activate": this.redraw,
+ "deactivate": this.redraw,
+ scope: this
+ });
+ }
+ this.activate();
+ return this.div;
+ },
+
+ /**
+ * Method: redraw
+ */
+ redraw: function() {
+ this.div.innerHTML = "";
+ if (this.active) {
+ for (var i=0, len=this.controls.length; i<len; i++) {
+ var element = this.controls[i].panel_div;
+ if (this.controls[i].active) {
+ element.className = this.controls[i].displayClass + "ItemActive";
+ } else {
+ element.className = this.controls[i].displayClass + "ItemInactive";
+ }
+ this.div.appendChild(element);
+ }
+ }
+ },
+
+ /**
+ * APIMethod: activateControl
+ *
+ * Parameters:
+ * control - {<OpenLayers.Control>}
+ */
+ activateControl: function (control) {
+ if (!this.active) { return false; }
+ if (control.type == OpenLayers.Control.TYPE_BUTTON) {
+ control.trigger();
+ this.redraw();
+ return;
+ }
+ if (control.type == OpenLayers.Control.TYPE_TOGGLE) {
+ if (control.active) {
+ control.deactivate();
+ } else {
+ control.activate();
+ }
+ this.redraw();
+ return;
+ }
+ for (var i=0, len=this.controls.length; i<len; i++) {
+ if (this.controls[i] != control) {
+ if (this.controls[i].type != OpenLayers.Control.TYPE_TOGGLE) {
+ this.controls[i].deactivate();
+ }
+ }
+ }
+ control.activate();
+ },
+
+ /**
+ * APIMethod: addControls
+ * To build a toolbar, you add a set of controls to it. addControls
+ * lets you add a single control or a list of controls to the
+ * Control Panel.
+ *
+ * Parameters:
+ * controls - {<OpenLayers.Control>}
+ */
+ addControls: function(controls) {
+ if (!(controls instanceof Array)) {
+ controls = [controls];
+ }
+ this.controls = this.controls.concat(controls);
+
+ // Give each control a panel_div which will be used later.
+ // Access to this div is via the panel_div attribute of the
+ // control added to the panel.
+ // Also, stop mousedowns and clicks, but don't stop mouseup,
+ // since they need to pass through.
+ for (var i=0, len=controls.length; i<len; i++) {
+ var element = document.createElement("div");
+ var textNode = document.createTextNode(" ");
+ controls[i].panel_div = element;
+ if (controls[i].title != "") {
+ controls[i].panel_div.title = controls[i].title;
+ }
+ OpenLayers.Event.observe(controls[i].panel_div, "click",
+ OpenLayers.Function.bind(this.onClick, this, controls[i]));
+ OpenLayers.Event.observe(controls[i].panel_div, "mousedown",
+ OpenLayers.Function.bindAsEventListener(OpenLayers.Event.stop));
+ }
+
+ if (this.map) { // map.addControl() has already been called on the panel
+ for (var i=0, len=controls.length; i<len; i++) {
+ this.map.addControl(controls[i]);
+ controls[i].deactivate();
+ controls[i].events.on({
+ "activate": this.redraw,
+ "deactivate": this.redraw,
+ scope: this
+ });
+ }
+ this.redraw();
+ }
+ },
+
+ /**
+ * Method: onClick
+ */
+ onClick: function (ctrl, evt) {
+ OpenLayers.Event.stop(evt ? evt : window.event);
+ this.activateControl(ctrl);
+ },
+
+ /**
+ * APIMethod: getControlsBy
+ * Get a list of controls with properties matching the given criteria.
+ *
+ * Parameter:
+ * property - {String} A control property to be matched.
+ * match - {String | Object} A string to match. Can also be a regular
+ * expression literal or object. In addition, it can be any object
+ * with a method named test. For reqular expressions or other, if
+ * match.test(control[property]) evaluates to true, the control will be
+ * included in the array returned. If no controls are found, an empty
+ * array is returned.
+ *
+ * Returns:
+ * {Array(<OpenLayers.Control>)} A list of controls matching the given criteria.
+ * An empty array is returned if no matches are found.
+ */
+ getControlsBy: function(property, match) {
+ var test = (typeof match.test == "function");
+ var found = OpenLayers.Array.filter(this.controls, function(item) {
+ return item[property] == match || (test && match.test(item[property]));
+ });
+ return found;
+ },
+
+ /**
+ * APIMethod: getControlsByName
+ * Get a list of contorls with names matching the given name.
+ *
+ * Parameter:
+ * match - {String | Object} A control name. The name can also be a regular
+ * expression literal or object. In addition, it can be any object
+ * with a method named test. For reqular expressions or other, if
+ * name.test(control.name) evaluates to true, the control will be included
+ * in the list of controls returned. If no controls are found, an empty
+ * array is returned.
+ *
+ * Returns:
+ * {Array(<OpenLayers.Control>)} A list of controls matching the given name.
+ * An empty array is returned if no matches are found.
+ */
+ getControlsByName: function(match) {
+ return this.getControlsBy("name", match);
+ },
+
+ /**
+ * APIMethod: getControlsByClass
+ * Get a list of controls of a given type (CLASS_NAME).
+ *
+ * Parameter:
+ * match - {String | Object} A control class name. The type can also be a
+ * regular expression literal or object. In addition, it can be any
+ * object with a method named test. For reqular expressions or other,
+ * if type.test(control.CLASS_NAME) evaluates to true, the control will
+ * be included in the list of controls returned. If no controls are
+ * found, an empty array is returned.
+ *
+ * Returns:
+ * {Array(<OpenLayers.Control>)} A list of controls matching the given type.
+ * An empty array is returned if no matches are found.
+ */
+ getControlsByClass: function(match) {
+ return this.getControlsBy("CLASS_NAME", match);
+ },
+
+ CLASS_NAME: "OpenLayers.Control.Panel"
+});
+
+/* ======================================================================
+ OpenLayers/Control/Scale.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
+ */
+
+/**
+ * Class: OpenLayers.Control.Scale
+ * Display a small scale indicator on the map.
+ * Inherits from:
+ * - <OpenLayers.Control>
+ */
+OpenLayers.Control.Scale = OpenLayers.Class(OpenLayers.Control, {
+
+ /**
+ * Parameter: element
+ * {DOMElement}
+ */
+ element: null,
+
+ /**
+ * Constructor: OpenLayers.Control.Scale
+ *
+ * Parameters:
+ * element - {DOMElement}
+ * options - {Object}
+ */
+ initialize: function(element, options) {
+ OpenLayers.Control.prototype.initialize.apply(this, [options]);
+ this.element = OpenLayers.Util.getElement(element);
+ },
+
+ /**
+ * Method: draw
+ *
+ * Returns:
+ * {DOMElement}
+ */
+ draw: function() {
+ OpenLayers.Control.prototype.draw.apply(this, arguments);
+ if (!this.element) {
+ this.element = document.createElement("div");
+ this.div.appendChild(this.element);
+ }
+ this.map.events.register( 'moveend', this, this.updateScale);
+ this.updateScale();
+ return this.div;
+ },
+
+ /**
+ * Method: updateScale
+ */
+ updateScale: function() {
+ var scale = this.map.getScale();
+ if (!scale) {
+ return;
+ }
+
+ if (scale >= 9500 && scale <= 950000) {
+ scale = Math.round(scale / 1000) + "K";
+ } else if (scale >= 950000) {
+ scale = Math.round(scale / 1000000) + "M";
+ } else {
+ scale = Math.round(scale);
+ }
+
+ this.element.innerHTML = OpenLayers.i18n("scale", {'scaleDenom':scale});
+ },
+
+ CLASS_NAME: "OpenLayers.Control.Scale"
+});
+
+/* ======================================================================
OpenLayers/Control/ScaleLine.js
====================================================================== */
@@ -6117,7 +9982,7 @@
return;
}
- var curMapUnits = this.map.units;
+ var curMapUnits = this.map.getUnits();
var inches = OpenLayers.INCHES_PER_UNIT;
// convert maxWidth to map units
@@ -6163,6 +10028,45 @@
});
/* ======================================================================
+ OpenLayers/Control/ZoomToMaxExtent.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
+ */
+
+/**
+ * Class: OpenLayers.Control.ZoomToMaxExtent
+ * Imlements a very simple button control. Designed to be used with a
+ * <OpenLayers.Control.Panel>.
+ *
+ * Inherits from:
+ * - <OpenLayers.Control>
+ */
+OpenLayers.Control.ZoomToMaxExtent = OpenLayers.Class(OpenLayers.Control, {
+ /**
+ * Property: type
+ * TYPE_BUTTON.
+ */
+ type: OpenLayers.Control.TYPE_BUTTON,
+
+ /*
+ * Method: trigger
+ * Do the zoom.
+ */
+ trigger: function() {
+ if (this.map) {
+ this.map.zoomToMaxExtent();
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Control.ZoomToMaxExtent"
+});
+/* ======================================================================
OpenLayers/Events.js
====================================================================== */
@@ -6273,6 +10177,21 @@
},
/**
+ * Method: isRightClick
+ * Determine whether event was caused by a right mouse click.
+ *
+ * Parameters:
+ * event - {Event}
+ *
+ * Returns:
+ * {Boolean}
+ */
+ isRightClick: function(event) {
+ return (((event.which) && (event.which == 3)) ||
+ ((event.button) && (event.button == 2)));
+ },
+
+ /**
* Method: stop
* Stops an event from propagating.
*
@@ -6520,7 +10439,7 @@
BROWSER_EVENTS: [
"mouseover", "mouseout",
"mousedown", "mouseup", "mousemove",
- "click", "dblclick",
+ "click", "dblclick", "rightclick", "dblrightclick",
"resize", "focus", "blur"
],
@@ -6560,6 +10479,34 @@
*/
fallThrough: null,
+ /**
+ * APIProperty: includeXY
+ * {Boolean} Should the .xy property automatically be created for browser
+ * mouse events? In general, this should be false. If it is true, then
+ * mouse events will automatically generate a '.xy' property on the
+ * event object that is passed. (Prior to OpenLayers 2.7, this was true
+ * by default.) Otherwise, you can call the getMousePosition on the
+ * relevant events handler on the object available via the 'evt.object'
+ * property of the evt object. So, for most events, you can call:
+ * function named(evt) {
+ * this.xy = this.object.events.getMousePosition(evt)
+ * }
+ *
+ * This option typically defaults to false for performance reasons:
+ * when creating an events object whose primary purpose is to manage
+ * relatively positioned mouse events within a div, it may make
+ * sense to set it to true.
+ *
+ * This option is also used to control whether the events object caches
+ * offsets. If this is false, it will not: the reason for this is that
+ * it is only expected to be called many times if the includeXY property
+ * is set to true. If you set this to true, you are expected to clear
+ * the offset cache manually (using this.clearMouseCache()) if:
+ * the border of the element changes
+ * the location of the element in the page changes
+ */
+ includeXY: false,
+
/**
* Constructor: OpenLayers.Events
* Construct an OpenLayers.Events object.
@@ -6570,11 +10517,12 @@
* eventTypes - {Array(String)} Array of custom application events
* fallThrough - {Boolean} Allow events to fall through after these have
* been handled?
+ * options - {Object} Options for the events object.
*/
- initialize: function (object, element, eventTypes, fallThrough) {
+ initialize: function (object, element, eventTypes, fallThrough, options) {
+ OpenLayers.Util.extend(this, options);
this.object = object;
this.element = element;
- this.eventTypes = eventTypes;
this.fallThrough = fallThrough;
this.listeners = {};
@@ -6586,9 +10534,10 @@
// if eventTypes is specified, create a listeners list for each
// custom application event.
- if (this.eventTypes != null) {
- for (var i = 0; i < this.eventTypes.length; i++) {
- this.addEventType(this.eventTypes[i]);
+ this.eventTypes = [];
+ if (eventTypes != null) {
+ for (var i=0, len=eventTypes.length; i<len; i++) {
+ this.addEventType(eventTypes[i]);
}
}
@@ -6625,6 +10574,7 @@
*/
addEventType: function(eventName) {
if (!this.listeners[eventName]) {
+ this.eventTypes.push(eventName);
this.listeners[eventName] = [];
}
},
@@ -6636,7 +10586,7 @@
* element - {HTMLDOMElement} a DOM element to attach browser events to
*/
attachToElement: function (element) {
- for (var i = 0; i < this.BROWSER_EVENTS.length; i++) {
+ for (var i=0, len=this.BROWSER_EVENTS.length; i<len; i++) {
var eventType = this.BROWSER_EVENTS[i];
// every browser event has a corresponding application event
@@ -6700,16 +10650,14 @@
*/
register: function (type, obj, func) {
- if (func != null &&
- ((this.eventTypes && OpenLayers.Util.indexOf(this.eventTypes, type) != -1) ||
- OpenLayers.Util.indexOf(this.BROWSER_EVENTS, type) != -1)) {
+ if ( (func != null) &&
+ (OpenLayers.Util.indexOf(this.eventTypes, type) != -1) ) {
+
if (obj == null) {
obj = this.object;
}
var listeners = this.listeners[type];
- if (listeners != null) {
- listeners.push( {obj: obj, func: func} );
- }
+ listeners.push( {obj: obj, func: func} );
}
},
@@ -6778,7 +10726,7 @@
}
var listeners = this.listeners[type];
if (listeners != null) {
- for (var i = 0; i < listeners.length; i++) {
+ for (var i=0, len=listeners.length; i<len; i++) {
if (listeners[i].obj == obj && listeners[i].func == func) {
listeners.splice(i, 1);
break;
@@ -6832,7 +10780,7 @@
this.listeners[type].slice() : null;
if ((listeners != null) && (listeners.length > 0)) {
var continueChain;
- for (var i = 0; i < listeners.length; i++) {
+ for (var i=0, len=listeners.length; i<len; i++) {
var callback = listeners[i];
// bind the context to callback.obj
continueChain = callback.func.apply(callback.obj, [evt]);
@@ -6860,11 +10808,25 @@
* evt - {Event}
*/
handleBrowserEvent: function (evt) {
- evt.xy = this.getMousePosition(evt);
+ if (this.includeXY) {
+ evt.xy = this.getMousePosition(evt);
+ }
this.triggerEvent(evt.type, evt);
},
/**
+ * APIMethod: clearMouseCache
+ * Clear cached data about the mouse position. This should be called any
+ * time the element that events are registered on changes position
+ * within the page.
+ */
+ clearMouseCache: function() {
+ this.element.scrolls = null;
+ this.element.lefttop = null;
+ this.element.offsets = null;
+ },
+
+ /**
* Method: getMousePosition
*
* Parameters:
@@ -6875,26 +10837,137 @@
* for offsets
*/
getMousePosition: function (evt) {
- if (!this.element.offsets) {
- this.element.offsets = OpenLayers.Util.pagePosition(this.element);
- this.element.offsets[0] += (document.documentElement.scrollLeft
+ if (!this.includeXY) {
+ this.clearMouseCache();
+ } else if (!this.element.hasScrollEvent) {
+ OpenLayers.Event.observe(window, 'scroll',
+ OpenLayers.Function.bind(this.clearMouseCache, this));
+ this.element.hasScrollEvent = true;
+ }
+
+ if (!this.element.scrolls) {
+ this.element.scrolls = [];
+ this.element.scrolls[0] = (document.documentElement.scrollLeft
|| document.body.scrollLeft);
- this.element.offsets[1] += (document.documentElement.scrollTop
+ this.element.scrolls[1] = (document.documentElement.scrollTop
|| document.body.scrollTop);
}
+
+ if (!this.element.lefttop) {
+ this.element.lefttop = [];
+ this.element.lefttop[0] = (document.documentElement.clientLeft || 0);
+ this.element.lefttop[1] = (document.documentElement.clientTop || 0);
+ }
+
+ if (!this.element.offsets) {
+ this.element.offsets = OpenLayers.Util.pagePosition(this.element);
+ this.element.offsets[0] += this.element.scrolls[0];
+ this.element.offsets[1] += this.element.scrolls[1];
+ }
return new OpenLayers.Pixel(
- (evt.clientX + (document.documentElement.scrollLeft
- || document.body.scrollLeft)) - this.element.offsets[0]
- - (document.documentElement.clientLeft || 0),
- (evt.clientY + (document.documentElement.scrollTop
- || document.body.scrollTop)) - this.element.offsets[1]
- - (document.documentElement.clientTop || 0)
+ (evt.clientX + this.element.scrolls[0]) - this.element.offsets[0]
+ - this.element.lefttop[0],
+ (evt.clientY + this.element.scrolls[1]) - this.element.offsets[1]
+ - this.element.lefttop[1]
);
},
CLASS_NAME: "OpenLayers.Events"
});
/* ======================================================================
+ OpenLayers/Format.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/Util.js
+ */
+
+/**
+ * Class: OpenLayers.Format
+ * Base class for format reading/writing a variety of formats. Subclasses
+ * of OpenLayers.Format are expected to have read and write methods.
+ */
+OpenLayers.Format = OpenLayers.Class({
+
+ /**
+ * APIProperty: externalProjection
+ * {<OpenLayers.Projection>} When passed a externalProjection and
+ * internalProjection, the format will reproject the geometries it
+ * reads or writes. The externalProjection is the projection used by
+ * the content which is passed into read or which comes out of write.
+ * In order to reproject, a projection transformation function for the
+ * specified projections must be available. This support may be
+ * provided via proj4js or via a custom transformation function. See
+ * {<OpenLayers.Projection.addTransform>} for more information on
+ * custom transformations.
+ */
+ externalProjection: null,
+
+ /**
+ * APIProperty: internalProjection
+ * {<OpenLayers.Projection>} When passed a externalProjection and
+ * internalProjection, the format will reproject the geometries it
+ * reads or writes. The internalProjection is the projection used by
+ * the geometries which are returned by read or which are passed into
+ * write. In order to reproject, a projection transformation function
+ * for the specified projections must be available. This support may be
+ * provided via proj4js or via a custom transformation function. See
+ * {<OpenLayers.Projection.addTransform>} for more information on
+ * custom transformations.
+ */
+ internalProjection: null,
+
+ /**
+ * Constructor: OpenLayers.Format
+ * Instances of this class are not useful. See one of the subclasses.
+ *
+ * Parameters:
+ * options - {Object} An optional object with properties to set on the
+ * format
+ *
+ * Returns:
+ * An instance of OpenLayers.Format
+ */
+ initialize: function(options) {
+ OpenLayers.Util.extend(this, options);
+ },
+
+ /**
+ * Method: read
+ * Read data from a string, and return an object whose type depends on the
+ * subclass.
+ *
+ * Parameters:
+ * data - {string} Data to read/parse.
+ *
+ * Returns:
+ * Depends on the subclass
+ */
+ read: function(data) {
+ OpenLayers.Console.userError(OpenLayers.i18n("readNotImplemented"));
+ },
+
+ /**
+ * Method: write
+ * Accept an object, and return a string.
+ *
+ * Parameters:
+ * object - {Object} Object to be serialized
+ *
+ * Returns:
+ * {String} A string representation of the object.
+ */
+ write: function(object) {
+ OpenLayers.Console.userError(OpenLayers.i18n("writeNotImplemented"));
+ },
+
+ CLASS_NAME: "OpenLayers.Format"
+});
+/* ======================================================================
OpenLayers/Lang/en.js
====================================================================== */
@@ -6975,7 +11048,7 @@
"To get rid of this message, select a new BaseLayer " +
"in the layer switcher in the upper-right corner.<br><br>" +
"Most likely, this is because the ${layerLib} library " +
- "script was either not correctly included.<br><br>" +
+ "script was not correctly included.<br><br>" +
"Developers: For help getting this working correctly, " +
"<a href='http://trac.openlayers.org/wiki/${layerLib}' " +
"target='_blank'>click here</a>",
@@ -7019,6 +11092,193 @@
'end': ''
};
/* ======================================================================
+ OpenLayers/Popup/Anchored.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/Popup.js
+ */
+
+/**
+ * Class: OpenLayers.Popup.Anchored
+ *
+ * Inherits from:
+ * - <OpenLayers.Popup>
+ */
+OpenLayers.Popup.Anchored =
+ OpenLayers.Class(OpenLayers.Popup, {
+
+ /**
+ * Parameter: relativePosition
+ * {String} Relative position of the popup ("br", "tr", "tl" or "bl").
+ */
+ relativePosition: null,
+
+ /**
+ * Parameter: anchor
+ * {Object} Object to which we'll anchor the popup. Must expose a
+ * 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>).
+ */
+ anchor: null,
+
+ /**
+ * Constructor: OpenLayers.Popup.Anchored
+ *
+ * Parameters:
+ * id - {String}
+ * lonlat - {<OpenLayers.LonLat>}
+ * contentSize - {<OpenLayers.Size>}
+ * contentHTML - {String}
+ * anchor - {Object} Object which must expose a 'size' <OpenLayers.Size>
+ * and 'offset' <OpenLayers.Pixel> (generally an <OpenLayers.Icon>).
+ * closeBox - {Boolean}
+ * closeBoxCallback - {Function} Function to be called on closeBox click.
+ */
+ initialize:function(id, lonlat, contentSize, contentHTML, anchor, closeBox,
+ closeBoxCallback) {
+ var newArguments = [
+ id, lonlat, contentSize, contentHTML, closeBox, closeBoxCallback
+ ];
+ OpenLayers.Popup.prototype.initialize.apply(this, newArguments);
+
+ this.anchor = (anchor != null) ? anchor
+ : { size: new OpenLayers.Size(0,0),
+ offset: new OpenLayers.Pixel(0,0)};
+ },
+
+ /**
+ * APIMethod: destroy
+ */
+ destroy: function() {
+ this.anchor = null;
+ this.relativePosition = null;
+
+ OpenLayers.Popup.prototype.destroy.apply(this, arguments);
+ },
+
+ /**
+ * APIMethod: show
+ * Overridden from Popup since user might hide popup and then show() it
+ * in a new location (meaning we might want to update the relative
+ * position on the show)
+ */
+ show: function() {
+ this.updatePosition();
+ OpenLayers.Popup.prototype.show.apply(this, arguments);
+ },
+
+ /**
+ * Method: moveTo
+ * Since the popup is moving to a new px, it might need also to be moved
+ * relative to where the marker is. We first calculate the new
+ * relativePosition, and then we calculate the new px where we will
+ * put the popup, based on the new relative position.
+ *
+ * If the relativePosition has changed, we must also call
+ * updateRelativePosition() to make any visual changes to the popup
+ * which are associated with putting it in a new relativePosition.
+ *
+ * Parameters:
+ * px - {<OpenLayers.Pixel>}
+ */
+ moveTo: function(px) {
+ var oldRelativePosition = this.relativePosition;
+ this.relativePosition = this.calculateRelativePosition(px);
+
+ var newPx = this.calculateNewPx(px);
+
+ var newArguments = new Array(newPx);
+ OpenLayers.Popup.prototype.moveTo.apply(this, newArguments);
+
+ //if this move has caused the popup to change its relative position,
+ // we need to make the appropriate cosmetic changes.
+ if (this.relativePosition != oldRelativePosition) {
+ this.updateRelativePosition();
+ }
+ },
+
+ /**
+ * APIMethod: setSize
+ *
+ * Parameters:
+ * contentSize - {<OpenLayers.Size>} the new size for the popup's
+ * contents div (in pixels).
+ */
+ setSize:function(contentSize) {
+ OpenLayers.Popup.prototype.setSize.apply(this, arguments);
+
+ if ((this.lonlat) && (this.map)) {
+ var px = this.map.getLayerPxFromLonLat(this.lonlat);
+ this.moveTo(px);
+ }
+ },
+
+ /**
+ * Method: calculateRelativePosition
+ *
+ * Parameters:
+ * px - {<OpenLayers.Pixel>}
+ *
+ * Returns:
+ * {String} The relative position ("br" "tr" "tl" "bl") at which the popup
+ * should be placed.
+ */
+ calculateRelativePosition:function(px) {
+ var lonlat = this.map.getLonLatFromLayerPx(px);
+
+ var extent = this.map.getExtent();
+ var quadrant = extent.determineQuadrant(lonlat);
+
+ return OpenLayers.Bounds.oppositeQuadrant(quadrant);
+ },
+
+ /**
+ * Method: updateRelativePosition
+ * The popup has been moved to a new relative location, so we may want to
+ * make some cosmetic adjustments to it.
+ *
+ * Note that in the classic Anchored popup, there is nothing to do
+ * here, since the popup looks exactly the same in all four positions.
+ * Subclasses such as the AnchoredBubble and Framed, however, will
+ * want to do something special here.
+ */
+ updateRelativePosition: function() {
+ //to be overridden by subclasses
+ },
+
+ /**
+ * Method: calculateNewPx
+ *
+ * Parameters:
+ * px - {<OpenLayers.Pixel>}
+ *
+ * Returns:
+ * {<OpenLayers.Pixel>} The the new px position of the popup on the screen
+ * relative to the passed-in px.
+ */
+ calculateNewPx:function(px) {
+ var newPx = px.offset(this.anchor.offset);
+
+ //use contentSize if size is not already set
+ var size = this.size || this.contentSize;
+
+ var top = (this.relativePosition.charAt(0) == 't');
+ newPx.y += (top) ? -size.h : this.anchor.size.h;
+
+ var left = (this.relativePosition.charAt(1) == 'l');
+ newPx.x += (left) ? -size.w : this.anchor.size.w;
+
+ return newPx;
+ },
+
+ CLASS_NAME: "OpenLayers.Popup.Anchored"
+});
+/* ======================================================================
OpenLayers/Projection.js
====================================================================== */
@@ -7198,6 +11458,1740 @@
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 = rendered && this.drawGeometry(
+ geometry.components[i], style, featureId);
+ }
+ 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 currentNode = OpenLayers.Util.getElement(id);
+
+ // Create a new node, or use the current one if it's
+ // already there.
+ var newNode;
+ if (!currentNode) {
+ var nodeType = this.getNodeType(geometry, style);
+ newNode = this.createNode(nodeType, id);
+ } else {
+ newNode = currentNode;
+ }
+
+ // Set the data for the node, then draw it.
+ newNode._featureId = featureId;
+ newNode._geometry = geometry;
+ newNode._geometryClass = geometry.CLASS_NAME;
+ newNode._style = style;
+
+ var drawResult = this.drawGeometryNode(newNode, geometry, style);
+ if(drawResult === false) {
+ return false;
+ }
+
+ newNode = 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(newNode) : null;
+
+ if(insert) {
+ this.root.insertBefore(newNode, insert);
+ } else {
+ this.root.appendChild(newNode);
+ }
+
+ this.postDraw(newNode);
+
+ 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);
+
+ 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: 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
+ ====================================================================== */
+
+// 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
+//
+// 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.
+
+/**
+ * @requires OpenLayers/Request.js
+ */
+
+(function () {
+
+ // Save reference to earlier defined object implementation (if any)
+ var oXMLHttpRequest = window.XMLHttpRequest;
+
+ // Define on browser type
+ var bGecko = !!window.controllers,
+ bIE = window.document.all && !window.opera;
+
+ // Constructor
+ function cXMLHttpRequest() {
+ this._object = oXMLHttpRequest ? new oXMLHttpRequest : new window.ActiveXObject('Microsoft.XMLHTTP');
+ };
+
+ // BUGFIX: Firefox with Firebug installed would break pages if not executed
+ if (bGecko && oXMLHttpRequest.wrapped)
+ cXMLHttpRequest.wrapped = oXMLHttpRequest.wrapped;
+
+ // Constants
+ cXMLHttpRequest.UNSENT = 0;
+ cXMLHttpRequest.OPENED = 1;
+ cXMLHttpRequest.HEADERS_RECEIVED = 2;
+ cXMLHttpRequest.LOADING = 3;
+ cXMLHttpRequest.DONE = 4;
+
+ // Public Properties
+ cXMLHttpRequest.prototype.readyState = cXMLHttpRequest.UNSENT;
+ cXMLHttpRequest.prototype.responseText = "";
+ cXMLHttpRequest.prototype.responseXML = null;
+ cXMLHttpRequest.prototype.status = 0;
+ cXMLHttpRequest.prototype.statusText = "";
+
+ // Instance-level Events Handlers
+ cXMLHttpRequest.prototype.onreadystatechange = null;
+
+ // Class-level Events Handlers
+ cXMLHttpRequest.onreadystatechange = null;
+ cXMLHttpRequest.onopen = null;
+ cXMLHttpRequest.onsend = null;
+ cXMLHttpRequest.onabort = null;
+
+ // Public Methods
+ cXMLHttpRequest.prototype.open = function(sMethod, sUrl, bAsync, sUser, sPassword) {
+
+ // Save async parameter for fixing Gecko bug with missing readystatechange in synchronous requests
+ this._async = bAsync;
+
+ // Set the onreadystatechange handler
+ var oRequest = this,
+ nState = this.readyState;
+
+ // BUGFIX: IE - memory leak on page unload (inter-page leak)
+ if (bIE) {
+ var fOnUnload = function() {
+ if (oRequest._object.readyState != cXMLHttpRequest.DONE)
+ fCleanTransport(oRequest);
+ };
+ if (bAsync)
+ window.attachEvent("onunload", fOnUnload);
+ }
+
+ this._object.onreadystatechange = function() {
+ if (bGecko && !bAsync)
+ return;
+
+ // Synchronize state
+ oRequest.readyState = oRequest._object.readyState;
+
+ //
+ fSynchronizeValues(oRequest);
+
+ // BUGFIX: Firefox fires unneccesary DONE when aborting
+ if (oRequest._aborted) {
+ // Reset readyState to UNSENT
+ oRequest.readyState = cXMLHttpRequest.UNSENT;
+
+ // Return now
+ return;
+ }
+
+ if (oRequest.readyState == cXMLHttpRequest.DONE) {
+ //
+ fCleanTransport(oRequest);
+// Uncomment this block if you need a fix for IE cache
+/*
+ // BUGFIX: IE - cache issue
+ if (!oRequest._object.getResponseHeader("Date")) {
+ // Save object to cache
+ oRequest._cached = oRequest._object;
+
+ // Instantiate a new transport object
+ cXMLHttpRequest.call(oRequest);
+
+ // Re-send request
+ oRequest._object.open(sMethod, sUrl, bAsync, sUser, sPassword);
+ oRequest._object.setRequestHeader("If-Modified-Since", oRequest._cached.getResponseHeader("Last-Modified") || new window.Date(0));
+ // Copy headers set
+ if (oRequest._headers)
+ for (var sHeader in oRequest._headers)
+ if (typeof oRequest._headers[sHeader] == "string") // Some frameworks prototype objects with functions
+ oRequest._object.setRequestHeader(sHeader, oRequest._headers[sHeader]);
+
+ oRequest._object.onreadystatechange = function() {
+ // Synchronize state
+ oRequest.readyState = oRequest._object.readyState;
+
+ if (oRequest._aborted) {
+ //
+ oRequest.readyState = cXMLHttpRequest.UNSENT;
+
+ // Return
+ return;
+ }
+
+ if (oRequest.readyState == cXMLHttpRequest.DONE) {
+ // Clean Object
+ fCleanTransport(oRequest);
+
+ // get cached request
+ if (oRequest.status == 304)
+ oRequest._object = oRequest._cached;
+
+ //
+ delete oRequest._cached;
+
+ //
+ fSynchronizeValues(oRequest);
+
+ //
+ fReadyStateChange(oRequest);
+
+ // BUGFIX: IE - memory leak in interrupted
+ if (bIE && bAsync)
+ window.detachEvent("onunload", fOnUnload);
+ }
+ };
+ oRequest._object.send(null);
+
+ // Return now - wait untill re-sent request is finished
+ return;
+ };
+*/
+ // BUGFIX: IE - memory leak in interrupted
+ if (bIE && bAsync)
+ window.detachEvent("onunload", fOnUnload);
+ }
+
+ // BUGFIX: Some browsers (Internet Explorer, Gecko) fire OPEN readystate twice
+ if (nState != oRequest.readyState)
+ fReadyStateChange(oRequest);
+
+ nState = oRequest.readyState;
+ };
+
+ // Add method sniffer
+ if (cXMLHttpRequest.onopen)
+ cXMLHttpRequest.onopen.apply(this, arguments);
+
+ this._object.open(sMethod, sUrl, bAsync, sUser, sPassword);
+
+ // BUGFIX: Gecko - missing readystatechange calls in synchronous requests
+ if (!bAsync && bGecko) {
+ this.readyState = cXMLHttpRequest.OPENED;
+
+ fReadyStateChange(this);
+ }
+ };
+ cXMLHttpRequest.prototype.send = function(vData) {
+ // Add method sniffer
+ if (cXMLHttpRequest.onsend)
+ cXMLHttpRequest.onsend.apply(this, arguments);
+
+ // BUGFIX: Safari - fails sending documents created/modified dynamically, so an explicit serialization required
+ // BUGFIX: IE - rewrites any custom mime-type to "text/xml" in case an XMLNode is sent
+ // BUGFIX: Gecko - fails sending Element (this is up to the implementation either to standard)
+ if (vData && vData.nodeType) {
+ vData = window.XMLSerializer ? new window.XMLSerializer().serializeToString(vData) : vData.xml;
+ if (!this._headers["Content-Type"])
+ this._object.setRequestHeader("Content-Type", "application/xml");
+ }
+
+ this._object.send(vData);
+
+ // BUGFIX: Gecko - missing readystatechange calls in synchronous requests
+ if (bGecko && !this._async) {
+ this.readyState = cXMLHttpRequest.OPENED;
+
+ // Synchronize state
+ fSynchronizeValues(this);
+
+ // Simulate missing states
+ while (this.readyState < cXMLHttpRequest.DONE) {
+ this.readyState++;
+ fReadyStateChange(this);
+ // Check if we are aborted
+ if (this._aborted)
+ return;
+ }
+ }
+ };
+ cXMLHttpRequest.prototype.abort = function() {
+ // Add method sniffer
+ if (cXMLHttpRequest.onabort)
+ cXMLHttpRequest.onabort.apply(this, arguments);
+
+ // BUGFIX: Gecko - unneccesary DONE when aborting
+ if (this.readyState > cXMLHttpRequest.UNSENT)
+ this._aborted = true;
+
+ this._object.abort();
+
+ // BUGFIX: IE - memory leak
+ fCleanTransport(this);
+ };
+ cXMLHttpRequest.prototype.getAllResponseHeaders = function() {
+ return this._object.getAllResponseHeaders();
+ };
+ cXMLHttpRequest.prototype.getResponseHeader = function(sName) {
+ return this._object.getResponseHeader(sName);
+ };
+ cXMLHttpRequest.prototype.setRequestHeader = function(sName, sValue) {
+ // BUGFIX: IE - cache issue
+ if (!this._headers)
+ this._headers = {};
+ this._headers[sName] = sValue;
+
+ return this._object.setRequestHeader(sName, sValue);
+ };
+ cXMLHttpRequest.prototype.toString = function() {
+ return '[' + "object" + ' ' + "XMLHttpRequest" + ']';
+ };
+ cXMLHttpRequest.toString = function() {
+ return '[' + "XMLHttpRequest" + ']';
+ };
+
+ // Helper function
+ function fReadyStateChange(oRequest) {
+ // Execute onreadystatechange
+ if (oRequest.onreadystatechange)
+ oRequest.onreadystatechange.apply(oRequest);
+
+ // Sniffing code
+ if (cXMLHttpRequest.onreadystatechange)
+ cXMLHttpRequest.onreadystatechange.apply(oRequest);
+ };
+
+ function fGetDocument(oRequest) {
+ var oDocument = oRequest.responseXML;
+ // Try parsing responseText
+ if (bIE && oDocument && !oDocument.documentElement && oRequest.getResponseHeader("Content-Type").match(/[^\/]+\/[^\+]+\+xml/)) {
+ oDocument = new ActiveXObject('Microsoft.XMLDOM');
+ oDocument.loadXML(oRequest.responseText);
+ }
+ // Check if there is no error in document
+ if (oDocument)
+ if ((bIE && oDocument.parseError != 0) || (oDocument.documentElement && oDocument.documentElement.tagName == "parsererror"))
+ return null;
+ return oDocument;
+ };
+
+ function fSynchronizeValues(oRequest) {
+ try { oRequest.responseText = oRequest._object.responseText; } catch (e) {}
+ try { oRequest.responseXML = fGetDocument(oRequest._object); } catch (e) {}
+ try { oRequest.status = oRequest._object.status; } catch (e) {}
+ try { oRequest.statusText = oRequest._object.statusText; } catch (e) {}
+ };
+
+ function fCleanTransport(oRequest) {
+ // BUGFIX: IE - memory leak (on-page leak)
+ oRequest._object.onreadystatechange = new window.Function;
+
+ // Delete private properties
+ delete oRequest._headers;
+ };
+
+ // Internet Explorer 5.0 (missing apply)
+ if (!window.Function.prototype.apply) {
+ window.Function.prototype.apply = function(oRequest, oArguments) {
+ if (!oArguments)
+ oArguments = [];
+ oRequest.__func = this;
+ oRequest.__func(oArguments[0], oArguments[1], oArguments[2], oArguments[3], oArguments[4]);
+ delete oRequest.__func;
+ };
+ };
+
+ // Register new object with window
+ /**
+ * Class: OpenLayers.Request.XMLHttpRequest
+ * Standard-compliant (W3C) cross-browser implementation of the
+ * XMLHttpRequest object. From
+ * http://code.google.com/p/xmlhttprequest/.
+ */
+ OpenLayers.Request.XMLHttpRequest = cXMLHttpRequest;
+})();
+/* ======================================================================
+ OpenLayers/Strategy/Fixed.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/Strategy.js
+ */
+
+/**
+ * Class: OpenLayers.Strategy.Fixed
+ * A simple strategy that requests features once and never requests new data.
+ *
+ * Inherits from:
+ * - <OpenLayers.Strategy>
+ */
+OpenLayers.Strategy.Fixed = OpenLayers.Class(OpenLayers.Strategy, {
+
+ /**
+ * Constructor: OpenLayers.Strategy.Fixed
+ * Create a new Fixed strategy.
+ *
+ * Parameters:
+ * options - {Object} Optional object whose properties will be set on the
+ * instance.
+ */
+ initialize: function(options) {
+ OpenLayers.Strategy.prototype.initialize.apply(this, [options]);
+ },
+
+ /**
+ * APIMethod: destroy
+ * Clean up the strategy.
+ */
+ destroy: function() {
+ OpenLayers.Strategy.prototype.destroy.apply(this, arguments);
+ },
+
+ /**
+ * Method: activate
+ * Activate the strategy: reads all features from the protocol and add them
+ * to the layer.
+ *
+ * Returns:
+ * {Boolean} True if the strategy was successfully activated or false if
+ * the strategy was already active.
+ */
+ activate: function() {
+ if(OpenLayers.Strategy.prototype.activate.apply(this, arguments)) {
+ this.layer.protocol.read({
+ callback: this.merge,
+ scope: this
+ });
+ return true;
+ }
+ return false;
+ },
+
+ /**
+ * Method: merge
+ * Add all features to the layer.
+ */
+ merge: function(resp) {
+ var features = resp.features;
+ if (features && features.length > 0) {
+ this.layer.addFeatures(features);
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Strategy.Fixed"
+});
+/* ======================================================================
OpenLayers/Tile.js
====================================================================== */
@@ -7282,39 +13276,7 @@
* {Boolean} Is the tile loading?
*/
isLoading: false,
-
- /**
- * Property: isBackBuffer
- * {Boolean} Is this tile a back buffer tile?
- */
- isBackBuffer: false,
-
- /**
- * Property: lastRatio
- * {Float} Used in transition code only. This is the previous ratio
- * of the back buffer tile resolution to the map resolution. Compared
- * with the current ratio to determine if zooming occurred.
- */
- lastRatio: 1,
-
- /**
- * Property: isFirstDraw
- * {Boolean} Is this the first time the tile is being drawn?
- * This is used to force resetBackBuffer to synchronize
- * the backBufferTile with the foreground tile the first time
- * the foreground tile loads so that if the user zooms
- * before the layer has fully loaded, the backBufferTile for
- * tiles that have been loaded can be used.
- */
- isFirstDraw: true,
- /**
- * Property: backBufferTile
- * {<OpenLayers.Tile>} A clone of the tile used to create transition
- * effects when the tile is moved or changes resolution.
- */
- backBufferTile: null,
-
/** TBD 3.0 -- remove 'url' from the list of parameters to the constructor.
* there is no need for the base tile class to have a url.
*
@@ -7360,13 +13322,6 @@
* Nullify references to prevent circular references and memory leaks.
*/
destroy:function() {
- if (OpenLayers.Util.indexOf(this.layer.SUPPORTED_TRANSITIONS,
- this.layer.transitionEffect) != -1) {
- this.layer.events.unregister("loadend", this, this.resetBackBuffer);
- this.events.unregister('loadend', this, this.resetBackBuffer);
- } else {
- this.events.unregister('loadend', this, this.showTile);
- }
this.layer = null;
this.bounds = null;
this.size = null;
@@ -7374,12 +13329,6 @@
this.events.destroy();
this.events = null;
-
- /* clean up the backBufferTile if it exists */
- if (this.backBufferTile) {
- this.backBufferTile.destroy();
- this.backBufferTile = null;
- }
},
/**
@@ -7424,50 +13373,12 @@
// The only case where we *wouldn't* want to draw the tile is if the
// tile is outside its layer's maxExtent.
- var drawTile = (withinMaxExtent || this.layer.displayOutsideMaxExtent);
-
- if (OpenLayers.Util.indexOf(this.layer.SUPPORTED_TRANSITIONS, this.layer.transitionEffect) != -1) {
- if (drawTile) {
- //we use a clone of this tile to create a double buffer for visual
- //continuity. The backBufferTile is used to create transition
- //effects while the tile in the grid is repositioned and redrawn
- if (!this.backBufferTile) {
- this.backBufferTile = this.clone();
- this.backBufferTile.hide();
- // this is important. It allows the backBuffer to place itself
- // appropriately in the DOM. The Image subclass needs to put
- // the backBufferTile behind the main tile so the tiles can
- // load over top and display as soon as they are loaded.
- this.backBufferTile.isBackBuffer = true;
-
- // potentially end any transition effects when the tile loads
- this.events.register('loadend', this, this.resetBackBuffer);
-
- // clear transition back buffer tile only after all tiles in
- // this layer have loaded to avoid visual glitches
- this.layer.events.register("loadend", this, this.resetBackBuffer);
- }
- // run any transition effects
- this.startTransition();
- } else {
- // if we aren't going to draw the tile, then the backBuffer should
- // be hidden too!
- if (this.backBufferTile) {
- this.backBufferTile.clear();
- }
- }
- } else {
- if (drawTile && this.isFirstDraw) {
- this.events.register('loadend', this, this.showTile);
- this.isFirstDraw = false;
- }
- }
- this.shouldDraw = drawTile;
-
+ this.shouldDraw = (withinMaxExtent || this.layer.displayOutsideMaxExtent);
+
//clear tile's contents and mark as not drawn
this.clear();
- return drawTile;
+ return this.shouldDraw;
},
/**
@@ -7538,49 +13449,6 @@
topLeft.lat);
return bounds;
},
-
- /**
- * Method: startTransition
- * Prepare the tile for a transition effect. To be
- * implemented by subclasses.
- */
- startTransition: function() {},
-
- /**
- * Method: resetBackBuffer
- * Triggered by two different events, layer loadend, and tile loadend.
- * In any of these cases, we check to see if we can hide the
- * backBufferTile yet and update its parameters to match the
- * foreground tile.
- *
- * Basic logic:
- * - If the backBufferTile hasn't been drawn yet, reset it
- * - If layer is still loading, show foreground tile but don't hide
- * the backBufferTile yet
- * - If layer is done loading, reset backBuffer tile and show
- * foreground tile
- */
- resetBackBuffer: function() {
- this.showTile();
- if (this.backBufferTile &&
- (this.isFirstDraw || !this.layer.numLoadingTiles)) {
- this.isFirstDraw = false;
- // check to see if the backBufferTile is within the max extents
- // before rendering it
- var maxExtent = this.layer.maxExtent;
- var withinMaxExtent = (maxExtent &&
- this.bounds.intersectsBounds(maxExtent, false));
- if (withinMaxExtent) {
- this.backBufferTile.position = this.position;
- this.backBufferTile.bounds = this.bounds;
- this.backBufferTile.size = this.size;
- this.backBufferTile.imageSize = this.layer.imageSize || this.size;
- this.backBufferTile.imageOffset = this.layer.imageOffset;
- this.backBufferTile.resolution = this.layer.getResolution();
- this.backBufferTile.renderTile();
- }
- }
- },
/**
* Method: showTile
@@ -7607,6 +13475,2440 @@
CLASS_NAME: "OpenLayers.Tile"
});
/* ======================================================================
+ OpenLayers/Ajax.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/Request/XMLHttpRequest.js
+ */
+
+OpenLayers.ProxyHost = "";
+//OpenLayers.ProxyHost = "examples/proxy.cgi?url=";
+
+/**
+ * Ajax reader for OpenLayers
+ *
+ * @uri url to do remote XML http get
+ * @param {String} 'get' format params (x=y&a=b...)
+ * @who object to handle callbacks for this request
+ * @complete the function to be called on success
+ * @failure the function to be called on failure
+ *
+ * example usage from a caller:
+ *
+ * caps: function(request) {
+ * -blah-
+ * },
+ *
+ * OpenLayers.loadURL(url,params,this,caps);
+ *
+ * Notice the above example does not provide an error handler; a default empty
+ * handler is provided which merely logs the error if a failure handler is not
+ * supplied
+ *
+ */
+
+
+/**
+ * Function: OpenLayers.nullHandler
+ * @param {} request
+ */
+OpenLayers.nullHandler = function(request) {
+ OpenLayers.Console.userError(OpenLayers.i18n("unhandledRequest", {'statusText':request.statusText}));
+};
+
+/**
+ * Function: loadURL
+ * Background load a document. For more flexibility in using XMLHttpRequest,
+ * see the <OpenLayers.Request> methods.
+ *
+ * Parameters:
+ * uri - {String} URI of source doc
+ * params - {String} or {Object} GET params. Either a string in the form
+ * "?hello=world&foo=bar" (do not forget the leading question mark)
+ * or an object in the form {'hello': 'world', 'foo': 'bar}
+ * caller - {Object} object which gets callbacks
+ * onComplete - {Function} Optional callback for success. The callback
+ * will be called with this set to caller and will receive the request
+ * object as an argument. Note that if you do not specify an onComplete
+ * function, <OpenLayers.nullHandler> will be called (which pops up a
+ * user friendly error message dialog).
+ * onFailure - {Function} Optional callback for failure. In the event of
+ * a failure, the callback will be called with this set to caller and will
+ * receive the request object as an argument. Note that if you do not
+ * specify an onComplete function, <OpenLayers.nullHandler> will be called
+ * (which pops up a user friendly error message dialog).
+ *
+ * Returns:
+ * {<OpenLayers.Request.XMLHttpRequest>} The request object. To abort loading,
+ * call request.abort().
+ */
+OpenLayers.loadURL = function(uri, params, caller,
+ onComplete, onFailure) {
+
+ if(typeof params == 'string') {
+ params = OpenLayers.Util.getParameters(params);
+ }
+ var success = (onComplete) ? onComplete : OpenLayers.nullHandler;
+ var failure = (onFailure) ? onFailure : OpenLayers.nullHandler;
+
+ return OpenLayers.Request.GET({
+ url: uri, params: params,
+ success: success, failure: failure, scope: caller
+ });
+};
+
+/**
+ * Function: parseXMLString
+ * Parse XML into a doc structure
+ *
+ * Parameters:
+ * text - {String}
+ *
+ * Returns:
+ * {?} Parsed AJAX Responsev
+ */
+OpenLayers.parseXMLString = function(text) {
+
+ //MS sucks, if the server is bad it dies
+ var index = text.indexOf('<');
+ if (index > 0) {
+ text = text.substring(index);
+ }
+
+ var ajaxResponse = OpenLayers.Util.Try(
+ function() {
+ var xmldom = new ActiveXObject('Microsoft.XMLDOM');
+ xmldom.loadXML(text);
+ return xmldom;
+ },
+ function() {
+ return new DOMParser().parseFromString(text, 'text/xml');
+ },
+ function() {
+ var req = new XMLHttpRequest();
+ req.open("GET", "data:" + "text/xml" +
+ ";charset=utf-8," + encodeURIComponent(text), false);
+ if (req.overrideMimeType) {
+ req.overrideMimeType("text/xml");
+ }
+ req.send(null);
+ return req.responseXML;
+ }
+ );
+
+ return ajaxResponse;
+};
+
+
+/**
+ * Namespace: OpenLayers.Ajax
+ */
+OpenLayers.Ajax = {
+
+ /**
+ * Method: emptyFunction
+ */
+ emptyFunction: function () {},
+
+ /**
+ * Method: getTransport
+ *
+ * Returns:
+ * {Object} Transport mechanism for whichever browser we're in, or false if
+ * none available.
+ */
+ getTransport: function() {
+ return OpenLayers.Util.Try(
+ function() {return new XMLHttpRequest();},
+ function() {return new ActiveXObject('Msxml2.XMLHTTP');},
+ function() {return new ActiveXObject('Microsoft.XMLHTTP');}
+ ) || false;
+ },
+
+ /**
+ * Property: activeRequestCount
+ * {Integer}
+ */
+ activeRequestCount: 0
+};
+
+/**
+ * Namespace: OpenLayers.Ajax.Responders
+ * {Object}
+ */
+OpenLayers.Ajax.Responders = {
+
+ /**
+ * Property: responders
+ * {Array}
+ */
+ responders: [],
+
+ /**
+ * Method: register
+ *
+ * Parameters:
+ * responderToAdd - {?}
+ */
+ register: function(responderToAdd) {
+ for (var i = 0; i < this.responders.length; i++){
+ if (responderToAdd == this.responders[i]){
+ return;
+ }
+ }
+ this.responders.push(responderToAdd);
+ },
+
+ /**
+ * Method: unregister
+ *
+ * Parameters:
+ * responderToRemove - {?}
+ */
+ unregister: function(responderToRemove) {
+ OpenLayers.Util.removeItem(this.reponders, responderToRemove);
+ },
+
+ /**
+ * Method: dispatch
+ *
+ * Parameters:
+ * callback - {?}
+ * request - {?}
+ * transport - {?}
+ */
+ dispatch: function(callback, request, transport) {
+ var responder;
+ for (var i = 0; i < this.responders.length; i++) {
+ responder = this.responders[i];
+
+ if (responder[callback] &&
+ typeof responder[callback] == 'function') {
+ try {
+ responder[callback].apply(responder,
+ [request, transport]);
+ } catch (e) {}
+ }
+ }
+ }
+};
+
+OpenLayers.Ajax.Responders.register({
+ /**
+ * Function: onCreate
+ */
+ onCreate: function() {
+ OpenLayers.Ajax.activeRequestCount++;
+ },
+
+ /**
+ * Function: onComplete
+ */
+ onComplete: function() {
+ OpenLayers.Ajax.activeRequestCount--;
+ }
+});
+
+/**
+ * Class: OpenLayers.Ajax.Base
+ */
+OpenLayers.Ajax.Base = OpenLayers.Class({
+
+ /**
+ * Constructor: OpenLayers.Ajax.Base
+ *
+ * Parameters:
+ * options - {Object}
+ */
+ initialize: function(options) {
+ this.options = {
+ method: 'post',
+ asynchronous: true,
+ contentType: 'application/xml',
+ parameters: ''
+ };
+ OpenLayers.Util.extend(this.options, options || {});
+
+ this.options.method = this.options.method.toLowerCase();
+
+ if (typeof this.options.parameters == 'string') {
+ this.options.parameters =
+ OpenLayers.Util.getParameters(this.options.parameters);
+ }
+ }
+});
+
+/**
+ * Class: OpenLayers.Ajax.Request
+ * *Deprecated*. Use <OpenLayers.Request> method instead.
+ *
+ * Inherit:
+ * - <OpenLayers.Ajax.Base>
+ */
+OpenLayers.Ajax.Request = OpenLayers.Class(OpenLayers.Ajax.Base, {
+
+ /**
+ * Property: _complete
+ *
+ * {Boolean}
+ */
+ _complete: false,
+
+ /**
+ * Constructor: OpenLayers.Ajax.Request
+ *
+ * Parameters:
+ * url - {String}
+ * options - {Object}
+ */
+ initialize: function(url, options) {
+ OpenLayers.Ajax.Base.prototype.initialize.apply(this, [options]);
+
+ if (OpenLayers.ProxyHost && OpenLayers.String.startsWith(url, "http")) {
+ url = OpenLayers.ProxyHost + encodeURIComponent(url);
+ }
+
+ this.transport = OpenLayers.Ajax.getTransport();
+ this.request(url);
+ },
+
+ /**
+ * Method: request
+ *
+ * Parameters:
+ * url - {String}
+ */
+ request: function(url) {
+ this.url = url;
+ this.method = this.options.method;
+ var params = OpenLayers.Util.extend({}, this.options.parameters);
+
+ if (this.method != 'get' && this.method != 'post') {
+ // simulate other verbs over post
+ params['_method'] = this.method;
+ this.method = 'post';
+ }
+
+ this.parameters = params;
+
+ if (params = OpenLayers.Util.getParameterString(params)) {
+ // when GET, append parameters to URL
+ if (this.method == 'get') {
+ this.url += ((this.url.indexOf('?') > -1) ? '&' : '?') + params;
+ } else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
+ params += '&_=';
+ }
+ }
+ try {
+ var response = new OpenLayers.Ajax.Response(this);
+ if (this.options.onCreate) {
+ this.options.onCreate(response);
+ }
+
+ OpenLayers.Ajax.Responders.dispatch('onCreate',
+ this,
+ response);
+
+ this.transport.open(this.method.toUpperCase(),
+ this.url,
+ this.options.asynchronous);
+
+ if (this.options.asynchronous) {
+ window.setTimeout(
+ OpenLayers.Function.bind(this.respondToReadyState, this, 1),
+ 10);
+ }
+
+ this.transport.onreadystatechange =
+ OpenLayers.Function.bind(this.onStateChange, this);
+ this.setRequestHeaders();
+
+ this.body = this.method == 'post' ?
+ (this.options.postBody || params) : null;
+ this.transport.send(this.body);
+
+ // Force Firefox to handle ready state 4 for synchronous requests
+ if (!this.options.asynchronous &&
+ this.transport.overrideMimeType) {
+ this.onStateChange();
+ }
+ } catch (e) {
+ this.dispatchException(e);
+ }
+ },
+
+ /**
+ * Method: onStateChange
+ */
+ onStateChange: function() {
+ var readyState = this.transport.readyState;
+ if (readyState > 1 && !((readyState == 4) && this._complete)) {
+ this.respondToReadyState(this.transport.readyState);
+ }
+ },
+
+ /**
+ * Method: setRequestHeaders
+ */
+ setRequestHeaders: function() {
+ var headers = {
+ 'X-Requested-With': 'XMLHttpRequest',
+ 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*',
+ 'OpenLayers': true
+ };
+
+ if (this.method == 'post') {
+ headers['Content-type'] = this.options.contentType +
+ (this.options.encoding ? '; charset=' + this.options.encoding : '');
+
+ /* Force "Connection: close" for older Mozilla browsers to work
+ * around a bug where XMLHttpRequest sends an incorrect
+ * Content-length header. See Mozilla Bugzilla #246651.
+ */
+ if (this.transport.overrideMimeType &&
+ (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005) {
+ headers['Connection'] = 'close';
+ }
+ }
+ // user-defined headers
+ if (typeof this.options.requestHeaders == 'object') {
+ var extras = this.options.requestHeaders;
+
+ if (typeof extras.push == 'function') {
+ for (var i = 0, length = extras.length; i < length; i += 2) {
+ headers[extras[i]] = extras[i+1];
+ }
+ } else {
+ for (var i in extras) {
+ headers[i] = extras[i];
+ }
+ }
+ }
+
+ for (var name in headers) {
+ this.transport.setRequestHeader(name, headers[name]);
+ }
+ },
+
+ /**
+ * Method: success
+ *
+ * Returns:
+ * {Boolean} -
+ */
+ success: function() {
+ var status = this.getStatus();
+ return !status || (status >=200 && status < 300);
+ },
+
+ /**
+ * Method: getStatus
+ *
+ * Returns:
+ * {Integer} - Status
+ */
+ getStatus: function() {
+ try {
+ return this.transport.status || 0;
+ } catch (e) {
+ return 0;
+ }
+ },
+
+ /**
+ * Method: respondToReadyState
+ *
+ * Parameters:
+ * readyState - {?}
+ */
+ respondToReadyState: function(readyState) {
+ var state = OpenLayers.Ajax.Request.Events[readyState];
+ var response = new OpenLayers.Ajax.Response(this);
+
+ if (state == 'Complete') {
+ try {
+ this._complete = true;
+ (this.options['on' + response.status] ||
+ this.options['on' + (this.success() ? 'Success' : 'Failure')] ||
+ OpenLayers.Ajax.emptyFunction)(response);
+ } catch (e) {
+ this.dispatchException(e);
+ }
+
+ var contentType = response.getHeader('Content-type');
+ }
+
+ try {
+ (this.options['on' + state] ||
+ OpenLayers.Ajax.emptyFunction)(response);
+ OpenLayers.Ajax.Responders.dispatch('on' + state,
+ this,
+ response);
+ } catch (e) {
+ this.dispatchException(e);
+ }
+
+ if (state == 'Complete') {
+ // avoid memory leak in MSIE: clean up
+ this.transport.onreadystatechange = OpenLayers.Ajax.emptyFunction;
+ }
+ },
+
+ /**
+ * Method: getHeader
+ *
+ * Parameters:
+ * name - {String} Header name
+ *
+ * Returns:
+ * {?} - response header for the given name
+ */
+ getHeader: function(name) {
+ try {
+ return this.transport.getResponseHeader(name);
+ } catch (e) {
+ return null;
+ }
+ },
+
+ /**
+ * Method: dispatchException
+ * If the optional onException function is set, execute it
+ * and then dispatch the call to any other listener registered
+ * for onException.
+ *
+ * If no optional onException function is set, we suspect that
+ * the user may have also not used
+ * OpenLayers.Ajax.Responders.register to register a listener
+ * for the onException call. To make sure that something
+ * gets done with this exception, only dispatch the call if there
+ * are listeners.
+ *
+ * If you explicitly want to swallow exceptions, set
+ * request.options.onException to an empty function (function(){})
+ * or register an empty function with <OpenLayers.Ajax.Responders>
+ * for onException.
+ *
+ * Parameters:
+ * exception - {?}
+ */
+ dispatchException: function(exception) {
+ var handler = this.options.onException;
+ if(handler) {
+ // call options.onException and alert any other listeners
+ handler(this, exception);
+ OpenLayers.Ajax.Responders.dispatch('onException', this, exception);
+ } else {
+ // check if there are any other listeners
+ var listener = false;
+ var responders = OpenLayers.Ajax.Responders.responders;
+ for (var i = 0; i < responders.length; i++) {
+ if(responders[i].onException) {
+ listener = true;
+ break;
+ }
+ }
+ if(listener) {
+ // call all listeners
+ OpenLayers.Ajax.Responders.dispatch('onException', this, exception);
+ } else {
+ // let the exception through
+ throw exception;
+ }
+ }
+ }
+});
+
+/**
+ * Property: Events
+ * {Array(String)}
+ */
+OpenLayers.Ajax.Request.Events =
+ ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
+
+/**
+ * Class: OpenLayers.Ajax.Response
+ */
+OpenLayers.Ajax.Response = OpenLayers.Class({
+
+ /**
+ * Property: status
+ *
+ * {Integer}
+ */
+ status: 0,
+
+
+ /**
+ * Property: statusText
+ *
+ * {String}
+ */
+ statusText: '',
+
+ /**
+ * Constructor: OpenLayers.Ajax.Response
+ *
+ * Parameters:
+ * request - {Object}
+ */
+ initialize: function(request) {
+ this.request = request;
+ var transport = this.transport = request.transport,
+ readyState = this.readyState = transport.readyState;
+
+ if ((readyState > 2 &&
+ !(!!(window.attachEvent && !window.opera))) ||
+ readyState == 4) {
+ this.status = this.getStatus();
+ this.statusText = this.getStatusText();
+ this.responseText = transport.responseText == null ?
+ '' : String(transport.responseText);
+ }
+
+ if(readyState == 4) {
+ var xml = transport.responseXML;
+ this.responseXML = xml === undefined ? null : xml;
+ }
+ },
+
+ /**
+ * Method: getStatus
+ */
+ getStatus: OpenLayers.Ajax.Request.prototype.getStatus,
+
+ /**
+ * Method: getStatustext
+ *
+ * Returns:
+ * {String} - statusText
+ */
+ getStatusText: function() {
+ try {
+ return this.transport.statusText || '';
+ } catch (e) {
+ return '';
+ }
+ },
+
+ /**
+ * Method: getHeader
+ */
+ getHeader: OpenLayers.Ajax.Request.prototype.getHeader,
+
+ /**
+ * Method: getResponseHeader
+ *
+ * Returns:
+ * {?} - response header for given name
+ */
+ getResponseHeader: function(name) {
+ return this.transport.getResponseHeader(name);
+ }
+});
+
+
+/**
+ * Function: getElementsByTagNameNS
+ *
+ * Parameters:
+ * parentnode - {?}
+ * nsuri - {?}
+ * nsprefix - {?}
+ * tagname - {?}
+ *
+ * Returns:
+ * {?}
+ */
+OpenLayers.Ajax.getElementsByTagNameNS = function(parentnode, nsuri,
+ nsprefix, tagname) {
+ var elem = null;
+ if (parentnode.getElementsByTagNameNS) {
+ elem = parentnode.getElementsByTagNameNS(nsuri, tagname);
+ } else {
+ elem = parentnode.getElementsByTagName(nsprefix + ':' + tagname);
+ }
+ return elem;
+};
+
+
+/**
+ * Function: serializeXMLToString
+ * Wrapper function around XMLSerializer, which doesn't exist/work in
+ * IE/Safari. We need to come up with a way to serialize in those browser:
+ * for now, these browsers will just fail. #535, #536
+ *
+ * Parameters:
+ * xmldom {XMLNode} xml dom to serialize
+ *
+ * Returns:
+ * {?}
+ */
+OpenLayers.Ajax.serializeXMLToString = function(xmldom) {
+ var serializer = new XMLSerializer();
+ var data = serializer.serializeToString(xmldom);
+ return data;
+};
+/* ======================================================================
+ OpenLayers/Control/MouseToolbar.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/Control/MouseDefaults.js
+ */
+
+/**
+ * Class: OpenLayers.Control.MouseToolbar
+ * This class is DEPRECATED in 2.4 and will be removed by 3.0.
+ * If you need this functionality, use Control.NavToolbar instead!!!
+ */
+OpenLayers.Control.MouseToolbar = OpenLayers.Class(
+ OpenLayers.Control.MouseDefaults, {
+
+ /**
+ * Property: mode
+ */
+ mode: null,
+ /**
+ * Property: buttons
+ */
+ buttons: null,
+
+ /**
+ * APIProperty: direction
+ * {String} 'vertical' or 'horizontal'
+ */
+ direction: "vertical",
+
+ /**
+ * Property: buttonClicked
+ * {String}
+ */
+ buttonClicked: null,
+
+ /**
+ * Constructor: OpenLayers.Control.MouseToolbar
+ *
+ * Parameters:
+ * position - {<OpenLayers.Pixel>}
+ * direction - {String}
+ */
+ initialize: function(position, direction) {
+ OpenLayers.Control.prototype.initialize.apply(this, arguments);
+ this.position = new OpenLayers.Pixel(OpenLayers.Control.MouseToolbar.X,
+ OpenLayers.Control.MouseToolbar.Y);
+ if (position) {
+ this.position = position;
+ }
+ if (direction) {
+ this.direction = direction;
+ }
+ this.measureDivs = [];
+ },
+
+ /**
+ * APIMethod: destroy
+ */
+ destroy: function() {
+ for( var btnId in this.buttons) {
+ var btn = this.buttons[btnId];
+ btn.map = null;
+ btn.events.destroy();
+ }
+ OpenLayers.Control.MouseDefaults.prototype.destroy.apply(this,
+ arguments);
+ },
+
+ /**
+ * Method: draw
+ */
+ draw: function() {
+ OpenLayers.Control.prototype.draw.apply(this, arguments);
+ OpenLayers.Control.MouseDefaults.prototype.draw.apply(this, arguments);
+ this.buttons = {};
+ var sz = new OpenLayers.Size(28,28);
+ var centered = new OpenLayers.Pixel(OpenLayers.Control.MouseToolbar.X,0);
+ this._addButton("zoombox", "drag-rectangle-off.png", "drag-rectangle-on.png", centered, sz, "Shift->Drag to zoom to area");
+ centered = centered.add((this.direction == "vertical" ? 0 : sz.w), (this.direction == "vertical" ? sz.h : 0));
+ this._addButton("pan", "panning-hand-off.png", "panning-hand-on.png", centered, sz, "Drag the map to pan.");
+ centered = centered.add((this.direction == "vertical" ? 0 : sz.w), (this.direction == "vertical" ? sz.h : 0));
+ this.switchModeTo("pan");
+
+ return this.div;
+ },
+
+ /**
+ * Method: _addButton
+ */
+ _addButton:function(id, img, activeImg, xy, sz, title) {
+ var imgLocation = OpenLayers.Util.getImagesLocation() + img;
+ var activeImgLocation = OpenLayers.Util.getImagesLocation() + activeImg;
+ // var btn = new ol.AlphaImage("_"+id, imgLocation, xy, sz);
+ var btn = OpenLayers.Util.createAlphaImageDiv(
+ "OpenLayers_Control_MouseToolbar_" + id,
+ xy, sz, imgLocation, "absolute");
+
+ //we want to add the outer div
+ this.div.appendChild(btn);
+ btn.imgLocation = imgLocation;
+ btn.activeImgLocation = activeImgLocation;
+
+ btn.events = new OpenLayers.Events(this, btn, null, true);
+ btn.events.on({
+ "mousedown": this.buttonDown,
+ "mouseup": this.buttonUp,
+ "dblclick": OpenLayers.Event.stop,
+ scope: this
+ });
+ btn.action = id;
+ btn.title = title;
+ btn.alt = title;
+ btn.map = this.map;
+
+ //we want to remember/reference the outer div
+ this.buttons[id] = btn;
+ return btn;
+ },
+
+ /**
+ * Method: buttonDown
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ buttonDown: function(evt) {
+ if (!OpenLayers.Event.isLeftClick(evt)) {
+ return;
+ }
+ this.buttonClicked = evt.element.action;
+ OpenLayers.Event.stop(evt);
+ },
+
+ /**
+ * Method: buttonUp
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ buttonUp: function(evt) {
+ if (!OpenLayers.Event.isLeftClick(evt)) {
+ return;
+ }
+ if (this.buttonClicked != null) {
+ if (this.buttonClicked == evt.element.action) {
+ this.switchModeTo(evt.element.action);
+ }
+ OpenLayers.Event.stop(evt);
+ this.buttonClicked = null;
+ }
+ },
+
+ /**
+ * Method: defaultDblClick
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ defaultDblClick: function (evt) {
+ this.switchModeTo("pan");
+ this.performedDrag = false;
+ var newCenter = this.map.getLonLatFromViewPortPx( evt.xy );
+ this.map.setCenter(newCenter, this.map.zoom + 1);
+ OpenLayers.Event.stop(evt);
+ return false;
+ },
+
+ /**
+ * Method: defaultMouseDown
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ defaultMouseDown: function (evt) {
+ if (!OpenLayers.Event.isLeftClick(evt)) {
+ return;
+ }
+ this.mouseDragStart = evt.xy.clone();
+ this.performedDrag = false;
+ this.startViaKeyboard = false;
+ if (evt.shiftKey && this.mode !="zoombox") {
+ this.switchModeTo("zoombox");
+ this.startViaKeyboard = true;
+ } else if (evt.altKey && this.mode !="measure") {
+ this.switchModeTo("measure");
+ } else if (!this.mode) {
+ this.switchModeTo("pan");
+ }
+
+ switch (this.mode) {
+ case "zoombox":
+ this.map.div.style.cursor = "crosshair";
+ this.zoomBox = OpenLayers.Util.createDiv('zoomBox',
+ this.mouseDragStart,
+ null,
+ null,
+ "absolute",
+ "2px solid red");
+ this.zoomBox.style.backgroundColor = "white";
+ this.zoomBox.style.filter = "alpha(opacity=50)"; // IE
+ this.zoomBox.style.opacity = "0.50";
+ this.zoomBox.style.fontSize = "1px";
+ this.zoomBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
+ this.map.viewPortDiv.appendChild(this.zoomBox);
+ this.performedDrag = true;
+ break;
+ case "measure":
+ var distance = "";
+ if (this.measureStart) {
+ var measureEnd = this.map.getLonLatFromViewPortPx(this.mouseDragStart);
+ distance = OpenLayers.Util.distVincenty(this.measureStart, measureEnd);
+ distance = Math.round(distance * 100) / 100;
+ distance = distance + "km";
+ this.measureStartBox = this.measureBox;
+ }
+ this.measureStart = this.map.getLonLatFromViewPortPx(this.mouseDragStart);;
+ this.measureBox = OpenLayers.Util.createDiv(null,
+ this.mouseDragStart.add(
+ -2-parseInt(this.map.layerContainerDiv.style.left),
+ -2-parseInt(this.map.layerContainerDiv.style.top)),
+ null,
+ null,
+ "absolute");
+ this.measureBox.style.width="4px";
+ this.measureBox.style.height="4px";
+ this.measureBox.style.fontSize = "1px";
+ this.measureBox.style.backgroundColor="red";
+ this.measureBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
+ this.map.layerContainerDiv.appendChild(this.measureBox);
+ if (distance) {
+ this.measureBoxDistance = OpenLayers.Util.createDiv(null,
+ this.mouseDragStart.add(
+ -2-parseInt(this.map.layerContainerDiv.style.left),
+ 2-parseInt(this.map.layerContainerDiv.style.top)),
+ null,
+ null,
+ "absolute");
+
+ this.measureBoxDistance.innerHTML = distance;
+ this.measureBoxDistance.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
+ this.map.layerContainerDiv.appendChild(this.measureBoxDistance);
+ this.measureDivs.push(this.measureBoxDistance);
+ }
+ this.measureBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
+ this.map.layerContainerDiv.appendChild(this.measureBox);
+ this.measureDivs.push(this.measureBox);
+ break;
+ default:
+ this.map.div.style.cursor = "move";
+ break;
+ }
+ document.onselectstart = function() { return false; };
+ OpenLayers.Event.stop(evt);
+ },
+
+ /**
+ * Method: switchModeTo
+ *
+ * Parameters:
+ * mode - {String}
+ */
+ switchModeTo: function(mode) {
+ if (mode != this.mode) {
+
+
+ if (this.mode && this.buttons[this.mode]) {
+ OpenLayers.Util.modifyAlphaImageDiv(this.buttons[this.mode], null, null, null, this.buttons[this.mode].imgLocation);
+ }
+ if (this.mode == "measure" && mode != "measure") {
+ for(var i=0, len=this.measureDivs.length; i<len; i++) {
+ if (this.measureDivs[i]) {
+ this.map.layerContainerDiv.removeChild(this.measureDivs[i]);
+ }
+ }
+ this.measureDivs = [];
+ this.measureStart = null;
+ }
+ this.mode = mode;
+ if (this.buttons[mode]) {
+ OpenLayers.Util.modifyAlphaImageDiv(this.buttons[mode], null, null, null, this.buttons[mode].activeImgLocation);
+ }
+ switch (this.mode) {
+ case "zoombox":
+ this.map.div.style.cursor = "crosshair";
+ break;
+ default:
+ this.map.div.style.cursor = "";
+ break;
+ }
+
+ }
+ },
+
+ /**
+ * Method: leaveMode
+ */
+ leaveMode: function() {
+ this.switchModeTo("pan");
+ },
+
+ /**
+ * Method: defaultMouseMove
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ defaultMouseMove: function (evt) {
+ if (this.mouseDragStart != null) {
+ switch (this.mode) {
+ case "zoombox":
+ var deltaX = Math.abs(this.mouseDragStart.x - evt.xy.x);
+ var deltaY = Math.abs(this.mouseDragStart.y - evt.xy.y);
+ this.zoomBox.style.width = Math.max(1, deltaX) + "px";
+ this.zoomBox.style.height = Math.max(1, deltaY) + "px";
+ if (evt.xy.x < this.mouseDragStart.x) {
+ this.zoomBox.style.left = evt.xy.x+"px";
+ }
+ if (evt.xy.y < this.mouseDragStart.y) {
+ this.zoomBox.style.top = evt.xy.y+"px";
+ }
+ break;
+ default:
+ var deltaX = this.mouseDragStart.x - evt.xy.x;
+ var deltaY = this.mouseDragStart.y - evt.xy.y;
+ var size = this.map.getSize();
+ var newXY = new OpenLayers.Pixel(size.w / 2 + deltaX,
+ size.h / 2 + deltaY);
+ var newCenter = this.map.getLonLatFromViewPortPx( newXY );
+ this.map.setCenter(newCenter, null, true);
+ this.mouseDragStart = evt.xy.clone();
+ }
+ this.performedDrag = true;
+ }
+ },
+
+ /**
+ * Method: defaultMouseUp
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ defaultMouseUp: function (evt) {
+ if (!OpenLayers.Event.isLeftClick(evt)) {
+ return;
+ }
+ switch (this.mode) {
+ case "zoombox":
+ this.zoomBoxEnd(evt);
+ if (this.startViaKeyboard) {
+ this.leaveMode();
+ }
+ break;
+ case "pan":
+ if (this.performedDrag) {
+ this.map.setCenter(this.map.center);
+ }
+ }
+ document.onselectstart = null;
+ this.mouseDragStart = null;
+ this.map.div.style.cursor = "default";
+ },
+
+ /**
+ * Method: defaultMouseOut
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ defaultMouseOut: function (evt) {
+ if (this.mouseDragStart != null
+ && OpenLayers.Util.mouseLeft(evt, this.map.div)) {
+ if (this.zoomBox) {
+ this.removeZoomBox();
+ if (this.startViaKeyboard) {
+ this.leaveMode();
+ }
+ }
+ this.mouseDragStart = null;
+ this.map.div.style.cursor = "default";
+ }
+ },
+
+ /**
+ * Method: defaultClick
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ defaultClick: function (evt) {
+ if (this.performedDrag) {
+ this.performedDrag = false;
+ return false;
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Control.MouseToolbar"
+});
+
+OpenLayers.Control.MouseToolbar.X = 6;
+OpenLayers.Control.MouseToolbar.Y = 300;
+/* ======================================================================
+ OpenLayers/Control/PanZoomBar.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/PanZoom.js
+ */
+
+/**
+ * Class: OpenLayers.Control.PanZoomBar
+ *
+ * Inherits from:
+ * - <OpenLayers.Control.PanZoom>
+ */
+OpenLayers.Control.PanZoomBar = OpenLayers.Class(OpenLayers.Control.PanZoom, {
+
+ /**
+ * APIProperty: zoomStopWidth
+ */
+ zoomStopWidth: 18,
+
+ /**
+ * APIProperty: zoomStopHeight
+ */
+ zoomStopHeight: 11,
+
+ /**
+ * Property: slider
+ */
+ slider: null,
+
+ /**
+ * Property: sliderEvents
+ * {<OpenLayers.Events>}
+ */
+ sliderEvents: null,
+
+ /**
+ * Property: zoomBarDiv
+ * {DOMElement}
+ */
+ zoomBarDiv: null,
+
+ /**
+ * Property: divEvents
+ * {<OpenLayers.Events>}
+ */
+ divEvents: null,
+
+ /**
+ * Property: zoomWorldIcon
+ * {Boolean}
+ */
+ zoomWorldIcon: false,
+
+ /**
+ * Constructor: OpenLayers.Control.PanZoomBar
+ */
+ initialize: function() {
+ OpenLayers.Control.PanZoom.prototype.initialize.apply(this, arguments);
+ },
+
+ /**
+ * APIMethod: destroy
+ */
+ destroy: function() {
+
+ this.div.removeChild(this.slider);
+ this.slider = null;
+
+ this.sliderEvents.destroy();
+ this.sliderEvents = null;
+
+ this.div.removeChild(this.zoombarDiv);
+ this.zoomBarDiv = null;
+
+ this.divEvents.destroy();
+ this.divEvents = null;
+
+ this.map.events.un({
+ "zoomend": this.moveZoomBar,
+ "changebaselayer": this.redraw,
+ scope: this
+ });
+
+ OpenLayers.Control.PanZoom.prototype.destroy.apply(this, arguments);
+ },
+
+ /**
+ * Method: setMap
+ *
+ * Parameters:
+ * map - {<OpenLayers.Map>}
+ */
+ setMap: function(map) {
+ OpenLayers.Control.PanZoom.prototype.setMap.apply(this, arguments);
+ this.map.events.register("changebaselayer", this, this.redraw);
+ },
+
+ /**
+ * Method: redraw
+ * clear the div and start over.
+ */
+ redraw: function() {
+ if (this.div != null) {
+ this.div.innerHTML = "";
+ }
+ this.draw();
+ },
+
+ /**
+ * Method: draw
+ *
+ * Parameters:
+ * px - {<OpenLayers.Pixel>}
+ */
+ draw: function(px) {
+ // initialize our internal div
+ OpenLayers.Control.prototype.draw.apply(this, arguments);
+ px = this.position.clone();
+
+ // place the controls
+ this.buttons = [];
+
+ var sz = new OpenLayers.Size(18,18);
+ var centered = new OpenLayers.Pixel(px.x+sz.w/2, px.y);
+ var wposition = sz.w;
+
+ if (this.zoomWorldIcon) {
+ centered = new OpenLayers.Pixel(px.x+sz.w, px.y);
+ }
+
+ this._addButton("panup", "north-mini.png", centered, sz);
+ px.y = centered.y+sz.h;
+ this._addButton("panleft", "west-mini.png", px, sz);
+ if (this.zoomWorldIcon) {
+ this._addButton("zoomworld", "zoom-world-mini.png", px.add(sz.w, 0), sz);
+
+ wposition *= 2;
+ }
+ this._addButton("panright", "east-mini.png", px.add(wposition, 0), sz);
+ this._addButton("pandown", "south-mini.png", centered.add(0, sz.h*2), sz);
+ this._addButton("zoomin", "zoom-plus-mini.png", centered.add(0, sz.h*3+5), sz);
+ centered = this._addZoomBar(centered.add(0, sz.h*4 + 5));
+ this._addButton("zoomout", "zoom-minus-mini.png", centered, sz);
+ return this.div;
+ },
+
+ /**
+ * Method: _addZoomBar
+ *
+ * Parameters:
+ * location - {<OpenLayers.Pixel>} where zoombar drawing is to start.
+ */
+ _addZoomBar:function(centered) {
+ var imgLocation = OpenLayers.Util.getImagesLocation();
+
+ var id = this.id + "_" + this.map.id;
+ var zoomsToEnd = this.map.getNumZoomLevels() - 1 - this.map.getZoom();
+ var slider = OpenLayers.Util.createAlphaImageDiv(id,
+ centered.add(-1, zoomsToEnd * this.zoomStopHeight),
+ new OpenLayers.Size(20,9),
+ imgLocation+"slider.png",
+ "absolute");
+ this.slider = slider;
+
+ this.sliderEvents = new OpenLayers.Events(this, slider, null, true,
+ {includeXY: true});
+ this.sliderEvents.on({
+ "mousedown": this.zoomBarDown,
+ "mousemove": this.zoomBarDrag,
+ "mouseup": this.zoomBarUp,
+ "dblclick": this.doubleClick,
+ "click": this.doubleClick
+ });
+
+ var sz = new OpenLayers.Size();
+ sz.h = this.zoomStopHeight * this.map.getNumZoomLevels();
+ sz.w = this.zoomStopWidth;
+ var div = null;
+
+ if (OpenLayers.Util.alphaHack()) {
+ var id = this.id + "_" + this.map.id;
+ div = OpenLayers.Util.createAlphaImageDiv(id, centered,
+ new OpenLayers.Size(sz.w,
+ this.zoomStopHeight),
+ imgLocation + "zoombar.png",
+ "absolute", null, "crop");
+ div.style.height = sz.h + "px";
+ } else {
+ div = OpenLayers.Util.createDiv(
+ 'OpenLayers_Control_PanZoomBar_Zoombar' + this.map.id,
+ centered,
+ sz,
+ imgLocation+"zoombar.png");
+ }
+
+ this.zoombarDiv = div;
+
+ this.divEvents = new OpenLayers.Events(this, div, null, true,
+ {includeXY: true});
+ this.divEvents.on({
+ "mousedown": this.divClick,
+ "mousemove": this.passEventToSlider,
+ "dblclick": this.doubleClick,
+ "click": this.doubleClick
+ });
+
+ this.div.appendChild(div);
+
+ this.startTop = parseInt(div.style.top);
+ this.div.appendChild(slider);
+
+ this.map.events.register("zoomend", this, this.moveZoomBar);
+
+ centered = centered.add(0,
+ this.zoomStopHeight * this.map.getNumZoomLevels());
+ return centered;
+ },
+
+ /*
+ * Method: passEventToSlider
+ * This function is used to pass events that happen on the div, or the map,
+ * through to the slider, which then does its moving thing.
+ *
+ * Parameters:
+ * evt - {<OpenLayers.Event>}
+ */
+ passEventToSlider:function(evt) {
+ this.sliderEvents.handleBrowserEvent(evt);
+ },
+
+ /*
+ * Method: divClick
+ * Picks up on clicks directly on the zoombar div
+ * and sets the zoom level appropriately.
+ */
+ divClick: function (evt) {
+ if (!OpenLayers.Event.isLeftClick(evt)) {
+ return;
+ }
+ var y = evt.xy.y;
+ var top = OpenLayers.Util.pagePosition(evt.object)[1];
+ var levels = (y - top)/this.zoomStopHeight;
+ if(!this.map.fractionalZoom) {
+ levels = Math.floor(levels);
+ }
+ var zoom = (this.map.getNumZoomLevels() - 1) - levels;
+ zoom = Math.min(Math.max(zoom, 0), this.map.getNumZoomLevels() - 1);
+ this.map.zoomTo(zoom);
+ OpenLayers.Event.stop(evt);
+ },
+
+ /*
+ * Method: zoomBarDown
+ * event listener for clicks on the slider
+ *
+ * Parameters:
+ * evt - {<OpenLayers.Event>}
+ */
+ zoomBarDown:function(evt) {
+ if (!OpenLayers.Event.isLeftClick(evt)) {
+ return;
+ }
+ this.map.events.on({
+ "mousemove": this.passEventToSlider,
+ "mouseup": this.passEventToSlider,
+ scope: this
+ });
+ this.mouseDragStart = evt.xy.clone();
+ this.zoomStart = evt.xy.clone();
+ this.div.style.cursor = "move";
+ // reset the div offsets just in case the div moved
+ this.zoombarDiv.offsets = null;
+ OpenLayers.Event.stop(evt);
+ },
+
+ /*
+ * Method: zoomBarDrag
+ * This is what happens when a click has occurred, and the client is
+ * dragging. Here we must ensure that the slider doesn't go beyond the
+ * bottom/top of the zoombar div, as well as moving the slider to its new
+ * visual location
+ *
+ * Parameters:
+ * evt - {<OpenLayers.Event>}
+ */
+ zoomBarDrag:function(evt) {
+ if (this.mouseDragStart != null) {
+ var deltaY = this.mouseDragStart.y - evt.xy.y;
+ var offsets = OpenLayers.Util.pagePosition(this.zoombarDiv);
+ if ((evt.clientY - offsets[1]) > 0 &&
+ (evt.clientY - offsets[1]) < parseInt(this.zoombarDiv.style.height) - 2) {
+ var newTop = parseInt(this.slider.style.top) - deltaY;
+ this.slider.style.top = newTop+"px";
+ }
+ this.mouseDragStart = evt.xy.clone();
+ OpenLayers.Event.stop(evt);
+ }
+ },
+
+ /*
+ * Method: zoomBarUp
+ * Perform cleanup when a mouseup event is received -- discover new zoom
+ * level and switch to it.
+ *
+ * Parameters:
+ * evt - {<OpenLayers.Event>}
+ */
+ zoomBarUp:function(evt) {
+ if (!OpenLayers.Event.isLeftClick(evt)) {
+ return;
+ }
+ if (this.zoomStart) {
+ this.div.style.cursor="";
+ this.map.events.un({
+ "mouseup": this.passEventToSlider,
+ "mousemove": this.passEventToSlider,
+ scope: this
+ });
+ var deltaY = this.zoomStart.y - evt.xy.y;
+ var zoomLevel = this.map.zoom;
+ if (this.map.fractionalZoom) {
+ zoomLevel += deltaY/this.zoomStopHeight;
+ zoomLevel = Math.min(Math.max(zoomLevel, 0),
+ this.map.getNumZoomLevels() - 1);
+ } else {
+ zoomLevel += Math.round(deltaY/this.zoomStopHeight);
+ }
+ this.map.zoomTo(zoomLevel);
+ this.moveZoomBar();
+ this.mouseDragStart = null;
+ OpenLayers.Event.stop(evt);
+ }
+ },
+
+ /*
+ * Method: moveZoomBar
+ * Change the location of the slider to match the current zoom level.
+ */
+ moveZoomBar:function() {
+ var newTop =
+ ((this.map.getNumZoomLevels()-1) - this.map.getZoom()) *
+ this.zoomStopHeight + this.startTop + 1;
+ this.slider.style.top = newTop + "px";
+ },
+
+ CLASS_NAME: "OpenLayers.Control.PanZoomBar"
+});
+/* ======================================================================
+ OpenLayers/Control/Permalink.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/Control/ArgParser.js
+ */
+
+/**
+ * Class: OpenLayers.Control.Permalink
+ *
+ * Inherits from:
+ * - <OpenLayers.Control>
+ */
+OpenLayers.Control.Permalink = OpenLayers.Class(OpenLayers.Control, {
+
+ /**
+ * APIProperty: argParserClass
+ * {Class} The ArgParser control class (not instance) to use with this
+ * control.
+ */
+ argParserClass: OpenLayers.Control.ArgParser,
+
+ /**
+ * Property: element
+ * {DOMElement}
+ */
+ element: null,
+
+ /**
+ * APIProperty: base
+ * {String}
+ */
+ base: '',
+
+ /**
+ * APIProperty: displayProjection
+ * {<OpenLayers.Projection>} Requires proj4js support. Projection used
+ * when creating the coordinates in the link. This will reproject the
+ * map coordinates into display coordinates. If you are using this
+ * functionality, the permalink which is last added to the map will
+ * determine the coordinate type which is read from the URL, which
+ * means you should not add permalinks with different
+ * displayProjections to the same map.
+ */
+ displayProjection: null,
+
+ /**
+ * Constructor: OpenLayers.Control.Permalink
+ *
+ * Parameters:
+ * element - {DOMElement}
+ * base - {String}
+ * options - {Object} options to the control.
+ */
+ initialize: function(element, base, options) {
+ OpenLayers.Control.prototype.initialize.apply(this, [options]);
+ this.element = OpenLayers.Util.getElement(element);
+ this.base = base || document.location.href;
+ },
+
+ /**
+ * APIMethod: destroy
+ */
+ destroy: function() {
+ if (this.element.parentNode == this.div) {
+ this.div.removeChild(this.element);
+ }
+ this.element = null;
+
+ this.map.events.unregister('moveend', this, this.updateLink);
+
+ OpenLayers.Control.prototype.destroy.apply(this, arguments);
+ },
+
+ /**
+ * Method: setMap
+ * Set the map property for the control.
+ *
+ * Parameters:
+ * map - {<OpenLayers.Map>}
+ */
+ setMap: function(map) {
+ OpenLayers.Control.prototype.setMap.apply(this, arguments);
+
+ //make sure we have an arg parser attached
+ for(var i=0, len=this.map.controls.length; i<len; i++) {
+ var control = this.map.controls[i];
+ if (control.CLASS_NAME == this.argParserClass.CLASS_NAME) {
+
+ // If a permalink is added to the map, and an ArgParser already
+ // exists, we override the displayProjection to be the one
+ // on the permalink.
+ if (control.displayProjection != this.displayProjection) {
+ this.displayProjection = control.displayProjection;
+ }
+
+ break;
+ }
+ }
+ if (i == this.map.controls.length) {
+ this.map.addControl(new this.argParserClass(
+ { 'displayProjection': this.displayProjection }));
+ }
+
+ },
+
+ /**
+ * Method: draw
+ *
+ * Returns:
+ * {DOMElement}
+ */
+ draw: function() {
+ OpenLayers.Control.prototype.draw.apply(this, arguments);
+
+ if (!this.element) {
+ this.div.className = this.displayClass;
+ this.element = document.createElement("a");
+ this.element.innerHTML = OpenLayers.i18n("permalink");
+ this.element.href="";
+ this.div.appendChild(this.element);
+ }
+ this.map.events.on({
+ 'moveend': this.updateLink,
+ 'changelayer': this.updateLink,
+ 'changebaselayer': this.updateLink,
+ scope: this
+ });
+
+ // Make it so there is at least a link even though the map may not have
+ // moved yet.
+ this.updateLink();
+
+ return this.div;
+ },
+
+ /**
+ * Method: updateLink
+ */
+ updateLink: function() {
+ var href = this.base;
+ if (href.indexOf('?') != -1) {
+ href = href.substring( 0, href.indexOf('?') );
+ }
+
+ href += '?' + OpenLayers.Util.getParameterString(this.createParams());
+ this.element.href = href;
+ },
+
+ /**
+ * APIMethod: createParams
+ * Creates the parameters that need to be encoded into the permalink url.
+ *
+ * Parameters:
+ * center - {<OpenLayers.LonLat>} center to encode in the permalink.
+ * Defaults to the current map center.
+ * zoom - {Integer} zoom level to encode in the permalink. Defaults to the
+ * current map zoom level.
+ * layers - {Array(<OpenLayers.Layer>)} layers to encode in the permalink.
+ * Defaults to the current map layers.
+ *
+ * Returns:
+ * {Object} Hash of parameters that will be url-encoded into the
+ * permalink.
+ */
+ createParams: function(center, zoom, layers) {
+ center = center || this.map.getCenter();
+ zoom = zoom || this.map.getZoom();
+ layers = layers || this.map.layers;
+
+ var params = OpenLayers.Util.getParameters(this.base);
+
+ // If there's still no center, map is not initialized yet.
+ // Break out of this function, and simply return the params from the
+ // base link.
+ if (center) {
+
+ params.zoom = this.map.getZoom();
+ var lat = center.lat;
+ var lon = center.lon;
+
+ if (this.displayProjection) {
+ var mapPosition = OpenLayers.Projection.transform(
+ { x: lon, y: lat },
+ this.map.getProjectionObject(),
+ this.displayProjection );
+ lon = mapPosition.x;
+ lat = mapPosition.y;
+ }
+ params.lat = Math.round(lat*100000)/100000;
+ params.lon = Math.round(lon*100000)/100000;
+
+ params.layers = '';
+ for (var i=0, len=this.map.layers.length; i<len; i++) {
+ var layer = this.map.layers[i];
+
+ if (layer.isBaseLayer) {
+ params.layers += (layer == this.map.baseLayer) ? "B" : "0";
+ } else {
+ params.layers += (layer.getVisibility()) ? "T" : "F";
+ }
+ }
+ }
+
+ return params;
+ },
+
+ CLASS_NAME: "OpenLayers.Control.Permalink"
+});
+/* ======================================================================
+ OpenLayers/Format/JSON.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. */
+
+/**
+ * Note:
+ * This work draws heavily from the public domain JSON serializer/deserializer
+ * at http://www.json.org/json.js. Rewritten so that it doesn't modify
+ * basic data prototypes.
+ */
+
+/**
+ * @requires OpenLayers/Format.js
+ */
+
+/**
+ * Class: OpenLayers.Format.JSON
+ * A parser to read/write JSON safely. Create a new instance with the
+ * <OpenLayers.Format.JSON> constructor.
+ *
+ * Inherits from:
+ * - <OpenLayers.Format>
+ */
+OpenLayers.Format.JSON = OpenLayers.Class(OpenLayers.Format, {
+
+ /**
+ * APIProperty: indent
+ * {String} For "pretty" printing, the indent string will be used once for
+ * each indentation level.
+ */
+ indent: " ",
+
+ /**
+ * APIProperty: space
+ * {String} For "pretty" printing, the space string will be used after
+ * the ":" separating a name/value pair.
+ */
+ space: " ",
+
+ /**
+ * APIProperty: newline
+ * {String} For "pretty" printing, the newline string will be used at the
+ * end of each name/value pair or array item.
+ */
+ newline: "\n",
+
+ /**
+ * Property: level
+ * {Integer} For "pretty" printing, this is incremented/decremented during
+ * serialization.
+ */
+ level: 0,
+
+ /**
+ * Property: pretty
+ * {Boolean} Serialize with extra whitespace for structure. This is set
+ * by the <write> method.
+ */
+ pretty: false,
+
+ /**
+ * Constructor: OpenLayers.Format.JSON
+ * Create a new parser for JSON.
+ *
+ * Parameters:
+ * options - {Object} An optional object whose properties will be set on
+ * this instance.
+ */
+ initialize: function(options) {
+ OpenLayers.Format.prototype.initialize.apply(this, [options]);
+ },
+
+ /**
+ * APIMethod: read
+ * Deserialize a json string.
+ *
+ * Parameters:
+ * json - {String} A JSON string
+ * filter - {Function} A function which will be called for every key and
+ * value at every level of the final result. Each value will be
+ * replaced by the result of the filter function. This can be used to
+ * reform generic objects into instances of classes, or to transform
+ * date strings into Date objects.
+ *
+ * Returns:
+ * {Object} An object, array, string, or number .
+ */
+ read: function(json, filter) {
+ /**
+ * Parsing happens in three stages. In the first stage, we run the text
+ * against a regular expression which looks for non-JSON
+ * characters. We are especially concerned with '()' and 'new'
+ * because they can cause invocation, and '=' because it can cause
+ * mutation. But just to be safe, we will reject all unexpected
+ * characters.
+ */
+ try {
+ if (/^[\],:{}\s]*$/.test(json.replace(/\\["\\\/bfnrtu]/g, '@').
+ replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
+ replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
+
+ /**
+ * In the second stage we use the eval function to compile the
+ * text into a JavaScript structure. The '{' operator is
+ * subject to a syntactic ambiguity in JavaScript - it can
+ * begin a block or an object literal. We wrap the text in
+ * parens to eliminate the ambiguity.
+ */
+ var object = eval('(' + json + ')');
+
+ /**
+ * In the optional third stage, we recursively walk the new
+ * structure, passing each name/value pair to a filter
+ * function for possible transformation.
+ */
+ if(typeof filter === 'function') {
+ function walk(k, v) {
+ if(v && typeof v === 'object') {
+ for(var i in v) {
+ if(v.hasOwnProperty(i)) {
+ v[i] = walk(i, v[i]);
+ }
+ }
+ }
+ return filter(k, v);
+ }
+ object = walk('', object);
+ }
+ return object;
+ }
+ } catch(e) {
+ // Fall through if the regexp test fails.
+ }
+ return null;
+ },
+
+ /**
+ * APIMethod: write
+ * Serialize an object into a JSON string.
+ *
+ * Parameters:
+ * value - {String} The object, array, string, number, boolean or date
+ * to be serialized.
+ * pretty - {Boolean} Structure the output with newlines and indentation.
+ * Default is false.
+ *
+ * Returns:
+ * {String} The JSON string representation of the input value.
+ */
+ write: function(value, pretty) {
+ this.pretty = !!pretty;
+ var json = null;
+ var type = typeof value;
+ if(this.serialize[type]) {
+ json = this.serialize[type].apply(this, [value]);
+ }
+ return json;
+ },
+
+ /**
+ * Method: writeIndent
+ * Output an indentation string depending on the indentation level.
+ *
+ * Returns:
+ * {String} An appropriate indentation string.
+ */
+ writeIndent: function() {
+ var pieces = [];
+ if(this.pretty) {
+ for(var i=0; i<this.level; ++i) {
+ pieces.push(this.indent);
+ }
+ }
+ return pieces.join('');
+ },
+
+ /**
+ * Method: writeNewline
+ * Output a string representing a newline if in pretty printing mode.
+ *
+ * Returns:
+ * {String} A string representing a new line.
+ */
+ writeNewline: function() {
+ return (this.pretty) ? this.newline : '';
+ },
+
+ /**
+ * Method: writeSpace
+ * Output a string representing a space if in pretty printing mode.
+ *
+ * Returns:
+ * {String} A space.
+ */
+ writeSpace: function() {
+ return (this.pretty) ? this.space : '';
+ },
+
+ /**
+ * Property: serialize
+ * Object with properties corresponding to the serializable data types.
+ * Property values are functions that do the actual serializing.
+ */
+ serialize: {
+ /**
+ * Method: serialize.object
+ * Transform an object into a JSON string.
+ *
+ * Parameters:
+ * object - {Object} The object to be serialized.
+ *
+ * Returns:
+ * {String} A JSON string representing the object.
+ */
+ 'object': function(object) {
+ // three special objects that we want to treat differently
+ if(object == null) {
+ return "null";
+ }
+ if(object.constructor == Date) {
+ return this.serialize.date.apply(this, [object]);
+ }
+ if(object.constructor == Array) {
+ return this.serialize.array.apply(this, [object]);
+ }
+ var pieces = ['{'];
+ this.level += 1;
+ var key, keyJSON, valueJSON;
+
+ var addComma = false;
+ for(key in object) {
+ if(object.hasOwnProperty(key)) {
+ // recursive calls need to allow for sub-classing
+ keyJSON = OpenLayers.Format.JSON.prototype.write.apply(this,
+ [key, this.pretty]);
+ valueJSON = OpenLayers.Format.JSON.prototype.write.apply(this,
+ [object[key], this.pretty]);
+ if(keyJSON != null && valueJSON != null) {
+ if(addComma) {
+ pieces.push(',');
+ }
+ pieces.push(this.writeNewline(), this.writeIndent(),
+ keyJSON, ':', this.writeSpace(), valueJSON);
+ addComma = true;
+ }
+ }
+ }
+
+ this.level -= 1;
+ pieces.push(this.writeNewline(), this.writeIndent(), '}');
+ return pieces.join('');
+ },
+
+ /**
+ * Method: serialize.array
+ * Transform an array into a JSON string.
+ *
+ * Parameters:
+ * array - {Array} The array to be serialized
+ *
+ * Returns:
+ * {String} A JSON string representing the array.
+ */
+ 'array': function(array) {
+ var json;
+ var pieces = ['['];
+ this.level += 1;
+
+ for(var i=0, len=array.length; i<len; ++i) {
+ // recursive calls need to allow for sub-classing
+ json = OpenLayers.Format.JSON.prototype.write.apply(this,
+ [array[i], this.pretty]);
+ if(json != null) {
+ if(i > 0) {
+ pieces.push(',');
+ }
+ pieces.push(this.writeNewline(), this.writeIndent(), json);
+ }
+ }
+
+ this.level -= 1;
+ pieces.push(this.writeNewline(), this.writeIndent(), ']');
+ return pieces.join('');
+ },
+
+ /**
+ * Method: serialize.string
+ * Transform a string into a JSON string.
+ *
+ * Parameters:
+ * string - {String} The string to be serialized
+ *
+ * Returns:
+ * {String} A JSON string representing the string.
+ */
+ 'string': function(string) {
+ // If the string contains no control characters, no quote characters, and no
+ // backslash characters, then we can simply slap some quotes around it.
+ // Otherwise we must also replace the offending characters with safe
+ // sequences.
+ var m = {
+ '\b': '\\b',
+ '\t': '\\t',
+ '\n': '\\n',
+ '\f': '\\f',
+ '\r': '\\r',
+ '"' : '\\"',
+ '\\': '\\\\'
+ };
+ if(/["\\\x00-\x1f]/.test(string)) {
+ return '"' + string.replace(/([\x00-\x1f\\"])/g, function(a, b) {
+ var c = m[b];
+ if(c) {
+ return c;
+ }
+ c = b.charCodeAt();
+ return '\\u00' +
+ Math.floor(c / 16).toString(16) +
+ (c % 16).toString(16);
+ }) + '"';
+ }
+ return '"' + string + '"';
+ },
+
+ /**
+ * Method: serialize.number
+ * Transform a number into a JSON string.
+ *
+ * Parameters:
+ * number - {Number} The number to be serialized.
+ *
+ * Returns:
+ * {String} A JSON string representing the number.
+ */
+ 'number': function(number) {
+ return isFinite(number) ? String(number) : "null";
+ },
+
+ /**
+ * Method: serialize.boolean
+ * Transform a boolean into a JSON string.
+ *
+ * Parameters:
+ * bool - {Boolean} The boolean to be serialized.
+ *
+ * Returns:
+ * {String} A JSON string representing the boolean.
+ */
+ 'boolean': function(bool) {
+ return String(bool);
+ },
+
+ /**
+ * Method: serialize.object
+ * Transform a date into a JSON string.
+ *
+ * Parameters:
+ * date - {Date} The date to be serialized.
+ *
+ * Returns:
+ * {String} A JSON string representing the date.
+ */
+ 'date': function(date) {
+ function format(number) {
+ // Format integers to have at least two digits.
+ return (number < 10) ? '0' + number : number;
+ }
+ return '"' + date.getFullYear() + '-' +
+ format(date.getMonth() + 1) + '-' +
+ format(date.getDate()) + 'T' +
+ format(date.getHours()) + ':' +
+ format(date.getMinutes()) + ':' +
+ format(date.getSeconds()) + '"';
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Format.JSON"
+
+});
+/* ======================================================================
+ OpenLayers/Format/XML.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/Format.js
+ */
+
+/**
+ * Class: OpenLayers.Format.XML
+ * Read and write XML. For cross-browser XML generation, use methods on an
+ * instance of the XML format class instead of on <code>document<end>.
+ * The DOM creation and traversing methods exposed here all mimic the
+ * W3C XML DOM methods. Create a new parser with the
+ * <OpenLayers.Format.XML> constructor.
+ *
+ * Inherits from:
+ * - <OpenLayers.Format>
+ */
+OpenLayers.Format.XML = OpenLayers.Class(OpenLayers.Format, {
+
+ /**
+ * Property: xmldom
+ * {XMLDom} If this browser uses ActiveX, this will be set to a XMLDOM
+ * object. It is not intended to be a browser sniffing property.
+ * Instead, the xmldom property is used instead of <code>document<end>
+ * where namespaced node creation methods are not supported. In all
+ * other browsers, this remains null.
+ */
+ xmldom: null,
+
+ /**
+ * Constructor: OpenLayers.Format.XML
+ * Construct an XML parser. The parser is used to read and write XML.
+ * Reading XML from a string returns a DOM element. Writing XML from
+ * a DOM element returns a string.
+ *
+ * Parameters:
+ * options - {Object} Optional object whose properties will be set on
+ * the object.
+ */
+ initialize: function(options) {
+ if(window.ActiveXObject) {
+ this.xmldom = new ActiveXObject("Microsoft.XMLDOM");
+ }
+ OpenLayers.Format.prototype.initialize.apply(this, [options]);
+ },
+
+ /**
+ * APIMethod: read
+ * Deserialize a XML string and return a DOM node.
+ *
+ * Parameters:
+ * text - {String} A XML string
+
+ * Returns:
+ * {DOMElement} A DOM node
+ */
+ read: function(text) {
+ var index = text.indexOf('<');
+ if(index > 0) {
+ text = text.substring(index);
+ }
+ var node = OpenLayers.Util.Try(
+ OpenLayers.Function.bind((
+ function() {
+ var xmldom;
+ /**
+ * Since we want to be able to call this method on the prototype
+ * itself, this.xmldom may not exist even if in IE.
+ */
+ if(window.ActiveXObject && !this.xmldom) {
+ xmldom = new ActiveXObject("Microsoft.XMLDOM");
+ } else {
+ xmldom = this.xmldom;
+
+ }
+ xmldom.loadXML(text);
+ return xmldom;
+ }
+ ), this),
+ function() {
+ return new DOMParser().parseFromString(text, 'text/xml');
+ },
+ function() {
+ var req = new XMLHttpRequest();
+ req.open("GET", "data:" + "text/xml" +
+ ";charset=utf-8," + encodeURIComponent(text), false);
+ if(req.overrideMimeType) {
+ req.overrideMimeType("text/xml");
+ }
+ req.send(null);
+ return req.responseXML;
+ }
+ );
+ return node;
+ },
+
+ /**
+ * APIMethod: write
+ * Serialize a DOM node into a XML string.
+ *
+ * Parameters:
+ * node - {DOMElement} A DOM node.
+ *
+ * Returns:
+ * {String} The XML string representation of the input node.
+ */
+ write: function(node) {
+ var data;
+ if(this.xmldom) {
+ data = node.xml;
+ } else {
+ var serializer = new XMLSerializer();
+ if (node.nodeType == 1) {
+ // Add nodes to a document before serializing. Everything else
+ // is serialized as is. This may need more work. See #1218 .
+ var doc = document.implementation.createDocument("", "", null);
+ if (doc.importNode) {
+ node = doc.importNode(node, true);
+ }
+ doc.appendChild(node);
+ data = serializer.serializeToString(doc);
+ } else {
+ data = serializer.serializeToString(node);
+ }
+ }
+ return data;
+ },
+
+ /**
+ * APIMethod: createElementNS
+ * Create a new element with namespace. This node can be appended to
+ * another node with the standard node.appendChild method. For
+ * cross-browser support, this method must be used instead of
+ * document.createElementNS.
+ *
+ * Parameters:
+ * uri - {String} Namespace URI for the element.
+ * name - {String} The qualified name of the element (prefix:localname).
+ *
+ * Returns:
+ * {Element} A DOM element with namespace.
+ */
+ createElementNS: function(uri, name) {
+ var element;
+ if(this.xmldom) {
+ if(typeof uri == "string") {
+ element = this.xmldom.createNode(1, name, uri);
+ } else {
+ element = this.xmldom.createNode(1, name, "");
+ }
+ } else {
+ element = document.createElementNS(uri, name);
+ }
+ return element;
+ },
+
+ /**
+ * APIMethod: createTextNode
+ * Create a text node. This node can be appended to another node with
+ * the standard node.appendChild method. For cross-browser support,
+ * this method must be used instead of document.createTextNode.
+ *
+ * Parameters:
+ * text - {String} The text of the node.
+ *
+ * Returns:
+ * {DOMElement} A DOM text node.
+ */
+ createTextNode: function(text) {
+ var node;
+ if(this.xmldom) {
+ node = this.xmldom.createTextNode(text);
+ } else {
+ node = document.createTextNode(text);
+ }
+ return node;
+ },
+
+ /**
+ * APIMethod: getElementsByTagNameNS
+ * Get a list of elements on a node given the namespace URI and local name.
+ * To return all nodes in a given namespace, use '*' for the name
+ * argument. To return all nodes of a given (local) name, regardless
+ * of namespace, use '*' for the uri argument.
+ *
+ * Parameters:
+ * node - {Element} Node on which to search for other nodes.
+ * uri - {String} Namespace URI.
+ * name - {String} Local name of the tag (without the prefix).
+ *
+ * Returns:
+ * {NodeList} A node list or array of elements.
+ */
+ getElementsByTagNameNS: function(node, uri, name) {
+ var elements = [];
+ if(node.getElementsByTagNameNS) {
+ elements = node.getElementsByTagNameNS(uri, name);
+ } else {
+ // brute force method
+ var allNodes = node.getElementsByTagName("*");
+ var potentialNode, fullName;
+ for(var i=0, len=allNodes.length; i<len; ++i) {
+ potentialNode = allNodes[i];
+ fullName = (potentialNode.prefix) ?
+ (potentialNode.prefix + ":" + name) : name;
+ if((name == "*") || (fullName == potentialNode.nodeName)) {
+ if((uri == "*") || (uri == potentialNode.namespaceURI)) {
+ elements.push(potentialNode);
+ }
+ }
+ }
+ }
+ return elements;
+ },
+
+ /**
+ * APIMethod: getAttributeNodeNS
+ * Get an attribute node given the namespace URI and local name.
+ *
+ * Parameters:
+ * node - {Element} Node on which to search for attribute nodes.
+ * uri - {String} Namespace URI.
+ * name - {String} Local name of the attribute (without the prefix).
+ *
+ * Returns:
+ * {DOMElement} An attribute node or null if none found.
+ */
+ getAttributeNodeNS: function(node, uri, name) {
+ var attributeNode = null;
+ if(node.getAttributeNodeNS) {
+ attributeNode = node.getAttributeNodeNS(uri, name);
+ } else {
+ var attributes = node.attributes;
+ var potentialNode, fullName;
+ for(var i=0, len=attributes.length; i<len; ++i) {
+ potentialNode = attributes[i];
+ if(potentialNode.namespaceURI == uri) {
+ fullName = (potentialNode.prefix) ?
+ (potentialNode.prefix + ":" + name) : name;
+ if(fullName == potentialNode.nodeName) {
+ attributeNode = potentialNode;
+ break;
+ }
+ }
+ }
+ }
+ return attributeNode;
+ },
+
+ /**
+ * APIMethod: getAttributeNS
+ * Get an attribute value given the namespace URI and local name.
+ *
+ * Parameters:
+ * node - {Element} Node on which to search for an attribute.
+ * uri - {String} Namespace URI.
+ * name - {String} Local name of the attribute (without the prefix).
+ *
+ * Returns:
+ * {String} An attribute value or and empty string if none found.
+ */
+ getAttributeNS: function(node, uri, name) {
+ var attributeValue = "";
+ if(node.getAttributeNS) {
+ attributeValue = node.getAttributeNS(uri, name) || "";
+ } else {
+ var attributeNode = this.getAttributeNodeNS(node, uri, name);
+ if(attributeNode) {
+ attributeValue = attributeNode.nodeValue;
+ }
+ }
+ return attributeValue;
+ },
+
+ /**
+ * APIMethod: getChildValue
+ * Get the value of the first child node if it exists, or return an
+ * optional default string. Returns an empty string if no first child
+ * exists and no default value is supplied.
+ *
+ * Parameters:
+ * node - {DOMElement} The element used to look for a first child value.
+ * def - {String} Optional string to return in the event that no
+ * first child value exists.
+ *
+ * Returns:
+ * {String} The value of the first child of the given node.
+ */
+ getChildValue: function(node, def) {
+ var value;
+ if (node && node.firstChild && node.firstChild.nodeValue) {
+ value = node.firstChild.nodeValue;
+ } else {
+ value = (def != undefined) ? def : "";
+ }
+ return value;
+ },
+
+ /**
+ * APIMethod: concatChildValues
+ * Concatenate the value of all child nodes if any exist, or return an
+ * optional default string. Returns an empty string if no children
+ * exist and no default value is supplied. Not optimized for large
+ * numbers of child nodes.
+ *
+ * Parameters:
+ * node - {DOMElement} The element used to look for child values.
+ * def - {String} Optional string to return in the event that no
+ * child exist.
+ *
+ * Returns:
+ * {String} The concatenated value of all child nodes of the given node.
+ */
+ concatChildValues: function(node, def) {
+ var value = "";
+ var child = node.firstChild;
+ var childValue;
+ while(child) {
+ childValue = child.nodeValue;
+ if(childValue) {
+ value += childValue;
+ }
+ child = child.nextSibling;
+ }
+ if(value == "" && def != undefined) {
+ value = def;
+ }
+ return value;
+ },
+
+ /**
+ * APIMethod: hasAttributeNS
+ * Determine whether a node has a particular attribute matching the given
+ * name and namespace.
+ *
+ * Parameters:
+ * node - {Element} Node on which to search for an attribute.
+ * uri - {String} Namespace URI.
+ * name - {String} Local name of the attribute (without the prefix).
+ *
+ * Returns:
+ * {Boolean} The node has an attribute matching the name and namespace.
+ */
+ hasAttributeNS: function(node, uri, name) {
+ var found = false;
+ if(node.hasAttributeNS) {
+ found = node.hasAttributeNS(uri, name);
+ } else {
+ found = !!this.getAttributeNodeNS(node, uri, name);
+ }
+ return found;
+ },
+
+ /**
+ * APIMethod: setAttributeNS
+ * Adds a new attribute or changes the value of an attribute with the given
+ * namespace and name.
+ *
+ * Parameters:
+ * node - {Element} Element node on which to set the attribute.
+ * uri - {String} Namespace URI for the attribute.
+ * name - {String} Qualified name (prefix:localname) for the attribute.
+ * value - {String} Attribute value.
+ */
+ setAttributeNS: function(node, uri, name, value) {
+ if(node.setAttributeNS) {
+ node.setAttributeNS(uri, name, value);
+ } else {
+ if(this.xmldom) {
+ if(uri) {
+ var attribute = node.ownerDocument.createNode(
+ 2, name, uri
+ );
+ attribute.nodeValue = value;
+ node.setAttributeNode(attribute);
+ } else {
+ node.setAttribute(name, value);
+ }
+ } else {
+ throw "setAttributeNS not implemented";
+ }
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Format.XML"
+
+});
+/* ======================================================================
OpenLayers/Handler.js
====================================================================== */
@@ -7768,7 +16070,7 @@
}
// register for event handlers defined on this class.
var events = OpenLayers.Events.prototype.BROWSER_EVENTS;
- for (var i = 0; i < events.length; i++) {
+ for (var i=0, len=events.length; i<len; i++) {
if (this[events[i]]) {
this.register(events[i], this[events[i]]);
}
@@ -7790,7 +16092,7 @@
}
// unregister event handlers defined on this class.
var events = OpenLayers.Events.prototype.BROWSER_EVENTS;
- for (var i = 0; i < events.length; i++) {
+ for (var i=0, len=events.length; i<len; i++) {
if (this[events[i]]) {
this.unregister(events[i], this[events[i]]);
}
@@ -7924,7 +16226,13 @@
* Constant: Z_INDEX_BASE
* {Object} Base z-indexes for different classes of thing
*/
- Z_INDEX_BASE: { BaseLayer: 100, Overlay: 325, Popup: 750, Control: 1000 },
+ Z_INDEX_BASE: {
+ BaseLayer: 100,
+ Overlay: 325,
+ Feature: 725,
+ Popup: 750,
+ Control: 1000
+ },
/**
* Constant: EVENT_TYPES
@@ -7964,6 +16272,7 @@
* - *movestart* triggered after the start of a drag, pan, or zoom
* - *move* triggered after each drag, pan, or zoom
* - *moveend* triggered after a drag, pan, or zoom completes
+ * - *zoomend* triggered after a zoom completes
* - *popupopen* triggered after a popup opens
* - *popupclose* triggered after a popup opens
* - *addmarker* triggered after a marker has been added
@@ -8097,6 +16406,13 @@
zoom: 0,
/**
+ * Property: panRatio
+ * {Float} The ratio of the current extent within
+ * which panning will tween.
+ */
+ panRatio: 1.5,
+
+ /**
* Property: viewRequestID
* {String} Used to store a unique identifier that changes when the map
* view changes. viewRequestID should be used when adding data
@@ -8304,6 +16620,7 @@
this.id = OpenLayers.Util.createUniqueID("OpenLayers.Map_");
this.div = OpenLayers.Util.getElement(div);
+ OpenLayers.Element.addClass(this.div, 'olMap');
// the viewPortDiv is the outermost div we modify
var id = this.div.id + "_OpenLayers_ViewPort";
@@ -8325,7 +16642,8 @@
this.events = new OpenLayers.Events(this,
this.div,
this.EVENT_TYPES,
- this.fallThrough);
+ this.fallThrough,
+ {includeXY: true});
this.updateSize();
if(this.eventListeners instanceof Object) {
this.events.on(this.eventListeners);
@@ -8354,7 +16672,7 @@
// check existing links for equivalent url
var addNode = true;
var nodes = document.getElementsByTagName('link');
- for(var i=0; i<nodes.length; ++i) {
+ for(var i=0, len=nodes.length; i<len; ++i) {
if(OpenLayers.Util.isEquivalentUrl(nodes.item(i).href,
this.theme)) {
addNode = false;
@@ -8386,7 +16704,7 @@
}
}
- for(var i=0; i < this.controls.length; i++) {
+ for(var i=0, len=this.controls.length; i<len; i++) {
this.addControlToMap(this.controls[i]);
}
@@ -8639,7 +16957,7 @@
*/
getLayer: function(id) {
var foundLayer = null;
- for (var i = 0; i < this.layers.length; i++) {
+ for (var i=0, len=this.layers.length; i<len; i++) {
var layer = this.layers[i];
if (layer.id == id) {
foundLayer = layer;
@@ -8667,7 +16985,7 @@
* Reset each layer's z-index based on layer's array index
*/
resetLayersZIndex: function() {
- for (var i = 0; i < this.layers.length; i++) {
+ for (var i=0, len=this.layers.length; i<len; i++) {
var layer = this.layers[i];
this.setLayerZIndex(layer, i);
}
@@ -8680,7 +16998,7 @@
* layer - {<OpenLayers.Layer>}
*/
addLayer: function (layer) {
- for(var i=0; i < this.layers.length; i++) {
+ for(var i=0, len=this.layers.length; i <len; i++) {
if (this.layers[i] == layer) {
var msg = OpenLayers.i18n('layerAlreadyAdded',
{'layerName':layer.name});
@@ -8724,7 +17042,7 @@
* layers - {Array(<OpenLayers.Layer>)}
*/
addLayers: function (layers) {
- for (var i = 0; i < layers.length; i++) {
+ for (var i=0, len=layers.length; i<len; i++) {
this.addLayer(layers[i]);
}
},
@@ -8775,7 +17093,7 @@
if(this.baseLayer == layer) {
this.baseLayer = null;
if(setNewBaseLayer) {
- for(var i=0; i < this.layers.length; i++) {
+ for(var i=0, len=this.layers.length; i<len; i++) {
var iLayer = this.layers[i];
if (iLayer.isBaseLayer) {
this.setBaseLayer(iLayer);
@@ -8836,7 +17154,7 @@
if (base != idx) {
this.layers.splice(base, 1);
this.layers.splice(idx, 0, layer);
- for (var i = 0; i < this.layers.length; i++) {
+ for (var i=0, len=this.layers.length; i<len; i++) {
this.setLayerZIndex(this.layers[i], i);
}
this.events.triggerEvent("changelayer", {
@@ -8854,7 +17172,7 @@
*
* Paremeters:
* layer - {<OpenLayers.Layer>}
- * idx - {int}
+ * delta - {int}
*/
raiseLayer: function (layer, delta) {
var idx = this.getLayerIndex(layer) + delta;
@@ -8986,7 +17304,7 @@
*/
getControl: function (id) {
var returnControl = null;
- for(var i=0; i < this.controls.length; i++) {
+ for(var i=0, len=this.controls.length; i<len; i++) {
var control = this.controls[i];
if (control.id == id) {
returnControl = control;
@@ -9101,7 +17419,7 @@
*/
updateSize: function() {
// the div might have moved on the page, also
- this.events.element.offsets = null;
+ this.events.clearMouseCache();
var newSize = this.getCurrentSize();
var oldSize = this.getSize();
if (oldSize == null) {
@@ -9113,7 +17431,7 @@
this.size = newSize;
//notify layers of mapresize
- for(var i=0; i < this.layers.length; i++) {
+ for(var i=0, len=this.layers.length; i<len; i++) {
this.layers[i].onMapResize();
}
@@ -9235,11 +17553,7 @@
* false.
*/
pan: function(dx, dy, options) {
- // this should be pushed to applyDefaults and extend
- if (!options) {
- options = {};
- }
- OpenLayers.Util.applyDefaults(options, {
+ options = OpenLayers.Util.applyDefaults(options, {
animate: true,
dragging: false
});
@@ -9270,11 +17584,18 @@
* lonlat - {<OpenLayers.Lonlat>}
*/
panTo: function(lonlat) {
- if (this.panMethod && this.getExtent().containsLonLat(lonlat)) {
+ if (this.panMethod && this.getExtent().scale(this.panRatio).containsLonLat(lonlat)) {
if (!this.panTween) {
this.panTween = new OpenLayers.Tween(this.panMethod);
}
var center = this.getCenter();
+
+ // center will not change, don't do nothing
+ if (lonlat.lon == center.lon &&
+ lonlat.lat == center.lat) {
+ return;
+ }
+
var from = {
lon: center.lon,
lat: center.lat
@@ -9436,7 +17757,7 @@
bounds = this.baseLayer.getExtent();
- for (var i = 0; i < this.layers.length; i++) {
+ for (var i=0, len=this.layers.length; i<len; i++) {
var layer = this.layers[i];
if (!layer.isBaseLayer) {
var inRange = layer.calculateInRange();
@@ -9455,13 +17776,16 @@
}
if (inRange && layer.visibility) {
layer.moveTo(bounds, zoomChanged, dragging);
+ layer.events.triggerEvent("moveend",
+ {"zoomChanged": zoomChanged}
+ );
}
}
}
if (zoomChanged) {
//redraw popups
- for (var i = 0; i < this.popups.length; i++) {
+ for (var i=0, len=this.popups.length; i<len; i++) {
this.popups[i].updatePosition();
}
}
@@ -9591,13 +17915,25 @@
/**
* APIMethod: getMaxExtent
+ *
+ * Parameters:
+ * options - {Object}
*
+ * Allowed Options:
+ * restricted - {Boolean} If true, returns restricted extent (if it is
+ * available.)
+ *
* Returns:
- * {<OpenLayers.Bounds>}
+ * {<OpenLayers.Bounds>} The maxExtent property as set on the current
+ * baselayer, unless the 'restricted' option is set, in which case
+ * the 'restrictedExtent' option from the map is returned (if it
+ * is set).
*/
- getMaxExtent: function () {
+ getMaxExtent: function (options) {
var maxExtent = null;
- if (this.baseLayer != null) {
+ if(options && options.restricted && this.restrictedExtent){
+ maxExtent = this.restrictedExtent;
+ } else if (this.baseLayer != null) {
maxExtent = this.baseLayer.maxExtent;
}
return maxExtent;
@@ -9660,6 +17996,21 @@
return resolution;
},
+ /**
+ * APIMethod: getUnits
+ *
+ * Returns:
+ * {Float} The current units of the map.
+ * If no baselayer is set, returns null.
+ */
+ getUnits: function () {
+ var units = null;
+ if (this.baseLayer != null) {
+ units = this.baseLayer.units;
+ }
+ return units;
+ },
+
/**
* APIMethod: getScale
*
@@ -9792,8 +18143,13 @@
*
* Parameters:
* bounds - {<OpenLayers.Bounds>}
+ * closest - {Boolean} Find the zoom level that most closely fits the
+ * specified bounds. Note that this may result in a zoom that does
+ * not exactly contain the entire extent.
+ * Default is false.
+ *
*/
- zoomToExtent: function(bounds) {
+ zoomToExtent: function(bounds, closest) {
var center = bounds.getCenterLonLat();
if (this.baseLayer.wrapDateLine) {
var maxExtent = this.getMaxExtent();
@@ -9817,15 +18173,28 @@
//
center = bounds.getCenterLonLat().wrapDateLine(maxExtent);
}
- this.setCenter(center, this.getZoomForExtent(bounds));
+ this.setCenter(center, this.getZoomForExtent(bounds, closest));
},
/**
* APIMethod: zoomToMaxExtent
* Zoom to the full extent and recenter.
+ *
+ * Parameters:
+ * options -
+ *
+ * Allowed Options:
+ * restricted - {Boolean} True to zoom to restricted extent if it is
+ * set. Defaults to true.
*/
- zoomToMaxExtent: function() {
- this.zoomToExtent(this.getMaxExtent());
+ zoomToMaxExtent: function(options) {
+ //restricted is true by default
+ var restricted = (options) ? options.restricted : true;
+
+ var maxExtent = this.getMaxExtent({
+ 'restricted': restricted
+ });
+ this.zoomToExtent(maxExtent);
},
/**
@@ -9834,8 +18203,13 @@
*
* Parameters:
* scale - {float}
+ * closest - {Boolean} Find the zoom level that most closely fits the
+ * specified scale. Note that this may result in a zoom that does
+ * not exactly contain the entire extent.
+ * Default is false.
+ *
*/
- zoomToScale: function(scale) {
+ zoomToScale: function(scale, closest) {
var res = OpenLayers.Util.getResolutionFromScale(scale,
this.baseLayer.units);
var size = this.getSize();
@@ -9847,7 +18221,7 @@
center.lat - h_deg / 2,
center.lon + w_deg / 2,
center.lat + h_deg / 2);
- this.zoomToExtent(extent);
+ this.zoomToExtent(extent, closest);
},
/********************************************************/
@@ -10108,9 +18482,9 @@
/**
* Constructor: OpenLayers.Marker
- * Paraemeters:
+ * Parameters:
+ * lonlat - {<OpenLayers.LonLat>} the position of this marker
* icon - {<OpenLayers.Icon>} the icon for this marker
- * lonlat - {<OpenLayers.LonLat>} the position of this marker
*/
initialize: function(lonlat, icon) {
this.lonlat = lonlat;
@@ -10262,6 +18636,2223 @@
/* ======================================================================
+ OpenLayers/Popup/AnchoredBubble.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/Popup/Anchored.js
+ */
+
+/**
+ * Class: OpenLayers.Popup.AnchoredBubble
+ *
+ * Inherits from:
+ * - <OpenLayers.Popup.Anchored>
+ */
+OpenLayers.Popup.AnchoredBubble =
+ OpenLayers.Class(OpenLayers.Popup.Anchored, {
+
+ /**
+ * Property: rounded
+ * {Boolean} Has the popup been rounded yet?
+ */
+ rounded: false,
+
+ /**
+ * Constructor: OpenLayers.Popup.AnchoredBubble
+ *
+ * Parameters:
+ * id - {String}
+ * lonlat - {<OpenLayers.LonLat>}
+ * contentSize - {<OpenLayers.Size>}
+ * contentHTML - {String}
+ * anchor - {Object} Object to which we'll anchor the popup. Must expose
+ * a 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>)
+ * (Note that this is generally an <OpenLayers.Icon>).
+ * closeBox - {Boolean}
+ * closeBoxCallback - {Function} Function to be called on closeBox click.
+ */
+ initialize:function(id, lonlat, contentSize, contentHTML, anchor, closeBox,
+ closeBoxCallback) {
+
+ this.padding = new OpenLayers.Bounds(
+ 0, OpenLayers.Popup.AnchoredBubble.CORNER_SIZE,
+ 0, OpenLayers.Popup.AnchoredBubble.CORNER_SIZE
+ );
+ OpenLayers.Popup.Anchored.prototype.initialize.apply(this, arguments);
+ },
+
+ /**
+ * Method: draw
+ *
+ * Parameters:
+ * px - {<OpenLayers.Pixel>}
+ *
+ * Returns:
+ * {DOMElement} Reference to a div that contains the drawn popup.
+ */
+ draw: function(px) {
+
+ OpenLayers.Popup.Anchored.prototype.draw.apply(this, arguments);
+
+ this.setContentHTML();
+
+ //set the popup color and opacity
+ this.setBackgroundColor();
+ this.setOpacity();
+
+ return this.div;
+ },
+
+ /**
+ * Method: updateRelativePosition
+ * The popup has been moved to a new relative location, in which case
+ * we will want to re-do the rico corners.
+ */
+ updateRelativePosition: function() {
+ this.setRicoCorners();
+ },
+
+ /**
+ * APIMethod: setSize
+ *
+ * Parameters:
+ * contentSize - {<OpenLayers.Size>} the new size for the popup's
+ * contents div (in pixels).
+ */
+ setSize:function(contentSize) {
+ OpenLayers.Popup.Anchored.prototype.setSize.apply(this, arguments);
+
+ this.setRicoCorners();
+ },
+
+ /**
+ * APIMethod: setBackgroundColor
+ *
+ * Parameters:
+ * color - {String}
+ */
+ setBackgroundColor:function(color) {
+ if (color != undefined) {
+ this.backgroundColor = color;
+ }
+
+ if (this.div != null) {
+ if (this.contentDiv != null) {
+ this.div.style.background = "transparent";
+ OpenLayers.Rico.Corner.changeColor(this.groupDiv,
+ this.backgroundColor);
+ }
+ }
+ },
+
+ /**
+ * APIMethod: setOpacity
+ *
+ * Parameters:
+ * opacity - {float}
+ */
+ setOpacity:function(opacity) {
+ OpenLayers.Popup.Anchored.prototype.setOpacity.call(this, opacity);
+
+ if (this.div != null) {
+ if (this.groupDiv != null) {
+ OpenLayers.Rico.Corner.changeOpacity(this.groupDiv,
+ this.opacity);
+ }
+ }
+ },
+
+ /**
+ * Method: setBorder
+ * Always sets border to 0. Bubble Popups can not have a border.
+ *
+ * Parameters:
+ * border - {Integer}
+ */
+ setBorder:function(border) {
+ this.border = 0;
+ },
+
+ /**
+ * Method: setRicoCorners
+ * Update RICO corners according to the popup's current relative postion.
+ */
+ setRicoCorners:function() {
+
+ var corners = this.getCornersToRound(this.relativePosition);
+ var options = {corners: corners,
+ color: this.backgroundColor,
+ bgColor: "transparent",
+ blend: false};
+
+ if (!this.rounded) {
+ OpenLayers.Rico.Corner.round(this.div, options);
+ this.rounded = true;
+ } else {
+ OpenLayers.Rico.Corner.reRound(this.groupDiv, options);
+ //set the popup color and opacity
+ this.setBackgroundColor();
+ this.setOpacity();
+ }
+ },
+
+ /**
+ * Method: getCornersToRound
+ *
+ * Returns:
+ * {String} The proper corners string ("tr tl bl br") for rico to round.
+ */
+ getCornersToRound:function() {
+
+ var corners = ['tl', 'tr', 'bl', 'br'];
+
+ //we want to round all the corners _except_ the opposite one.
+ var corner = OpenLayers.Bounds.oppositeQuadrant(this.relativePosition);
+ OpenLayers.Util.removeItem(corners, corner);
+
+ return corners.join(" ");
+ },
+
+ CLASS_NAME: "OpenLayers.Popup.AnchoredBubble"
+});
+
+/**
+ * Constant: CORNER_SIZE
+ * {Integer} 5. Border space for the RICO corners.
+ */
+OpenLayers.Popup.AnchoredBubble.CORNER_SIZE = 5;
+
+/* ======================================================================
+ OpenLayers/Popup/Framed.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/Popup/Anchored.js
+ */
+
+/**
+ * Class: OpenLayers.Popup.Framed
+ *
+ * Inherits from:
+ * - <OpenLayers.Popup.Anchored>
+ */
+OpenLayers.Popup.Framed =
+ OpenLayers.Class(OpenLayers.Popup.Anchored, {
+
+ /**
+ * Property: imageSrc
+ * {String} location of the image to be used as the popup frame
+ */
+ imageSrc: null,
+
+ /**
+ * Property: imageSize
+ * {<OpenLayers.Size>} Size (measured in pixels) of the image located
+ * by the 'imageSrc' property.
+ */
+ imageSize: null,
+
+ /**
+ * APIProperty: isAlphaImage
+ * {Boolean} The image has some alpha and thus needs to use the alpha
+ * image hack. Note that setting this to true will have no noticeable
+ * effect in FF or IE7 browsers, but will all but crush the ie6
+ * browser.
+ * Default is false.
+ */
+ isAlphaImage: false,
+
+ /**
+ * Property: positionBlocks
+ * {Object} Hash of different position blocks (Object/Hashs). Each block
+ * will be keyed by a two-character 'relativePosition'
+ * code string (ie "tl", "tr", "bl", "br"). Block properties are
+ * 'offset', 'padding' (self-explanatory), and finally the 'blocks'
+ * parameter, which is an array of the block objects.
+ *
+ * Each block object must have 'size', 'anchor', and 'position'
+ * properties.
+ *
+ * Note that positionBlocks should never be modified at runtime.
+ */
+ positionBlocks: null,
+
+ /**
+ * Property: blocks
+ * {Array[Object]} Array of objects, each of which is one "block" of the
+ * popup. Each block has a 'div' and an 'image' property, both of
+ * which are DOMElements, and the latter of which is appended to the
+ * former. These are reused as the popup goes changing positions for
+ * great economy and elegance.
+ */
+ blocks: null,
+
+ /**
+ * APIProperty: fixedRelativePosition
+ * {Boolean} We want the framed popup to work dynamically placed relative
+ * to its anchor but also in just one fixed position. A well designed
+ * framed popup will have the pixels and logic to display itself in
+ * any of the four relative positions, but (understandably), this will
+ * not be the case for all of them. By setting this property to 'true',
+ * framed popup will not recalculate for the best placement each time
+ * it's open, but will always open the same way.
+ * Note that if this is set to true, it is generally advisable to also
+ * set the 'panIntoView' property to true so that the popup can be
+ * scrolled into view (since it will often be offscreen on open)
+ * Default is false.
+ */
+ fixedRelativePosition: false,
+
+ /**
+ * Constructor: OpenLayers.Popup.Framed
+ *
+ * Parameters:
+ * id - {String}
+ * lonlat - {<OpenLayers.LonLat>}
+ * contentSize - {<OpenLayers.Size>}
+ * contentHTML - {String}
+ * anchor - {Object} Object to which we'll anchor the popup. Must expose
+ * a 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>)
+ * (Note that this is generally an <OpenLayers.Icon>).
+ * closeBox - {Boolean}
+ * closeBoxCallback - {Function} Function to be called on closeBox click.
+ */
+ initialize:function(id, lonlat, contentSize, contentHTML, anchor, closeBox,
+ closeBoxCallback) {
+
+ OpenLayers.Popup.Anchored.prototype.initialize.apply(this, arguments);
+
+ if (this.fixedRelativePosition) {
+ //based on our decided relativePostion, set the current padding
+ // this keeps us from getting into trouble
+ this.updateRelativePosition();
+
+ //make calculateRelativePosition always returnt the specified
+ // fiexed position.
+ this.calculateRelativePosition = function(px) {
+ return this.relativePosition;
+ };
+ }
+
+ this.contentDiv.style.position = "absolute";
+ this.contentDiv.style.zIndex = 1;
+
+ if (closeBox) {
+ this.closeDiv.style.zIndex = 1;
+ }
+
+ this.groupDiv.style.position = "absolute";
+ this.groupDiv.style.top = "0px";
+ this.groupDiv.style.left = "0px";
+ this.groupDiv.style.height = "100%";
+ this.groupDiv.style.width = "100%";
+ },
+
+ /**
+ * APIMethod: destroy
+ */
+ destroy: function() {
+ this.imageSrc = null;
+ this.imageSize = null;
+ this.isAlphaImage = null;
+
+ this.fixedRelativePosition = false;
+ this.positionBlocks = null;
+
+ //remove our blocks
+ for(var i = 0; i < this.blocks.length; i++) {
+ var block = this.blocks[i];
+
+ if (block.image) {
+ block.div.removeChild(block.image);
+ }
+ block.image = null;
+
+ if (block.div) {
+ this.groupDiv.removeChild(block.div);
+ }
+ block.div = null;
+ }
+ this.blocks = null;
+
+ OpenLayers.Popup.Anchored.prototype.destroy.apply(this, arguments);
+ },
+
+ /**
+ * APIMethod: setBackgroundColor
+ */
+ setBackgroundColor:function(color) {
+ //does nothing since the framed popup's entire scheme is based on a
+ // an image -- changing the background color makes no sense.
+ },
+
+ /**
+ * APIMethod: setBorder
+ */
+ setBorder:function() {
+ //does nothing since the framed popup's entire scheme is based on a
+ // an image -- changing the popup's border makes no sense.
+ },
+
+ /**
+ * Method: setOpacity
+ * Sets the opacity of the popup.
+ *
+ * Parameters:
+ * opacity - {float} A value between 0.0 (transparent) and 1.0 (solid).
+ */
+ setOpacity:function(opacity) {
+ //does nothing since we suppose that we'll never apply an opacity
+ // to a framed popup
+ },
+
+ /**
+ * APIMethod: setSize
+ * Overridden here, because we need to update the blocks whenever the size
+ * of the popup has changed.
+ *
+ * Parameters:
+ * contentSize - {<OpenLayers.Size>} the new size for the popup's
+ * contents div (in pixels).
+ */
+ setSize:function(contentSize) {
+ OpenLayers.Popup.Anchored.prototype.setSize.apply(this, arguments);
+
+ this.updateBlocks();
+ },
+
+ /**
+ * Method: updateRelativePosition
+ * When the relative position changes, we need to set the new padding
+ * BBOX on the popup, reposition the close div, and update the blocks.
+ */
+ updateRelativePosition: function() {
+
+ //update the padding
+ this.padding = this.positionBlocks[this.relativePosition].padding;
+
+ //update the position of our close box to new padding
+ if (this.closeDiv) {
+ // use the content div's css padding to determine if we should
+ // padd the close div
+ var contentDivPadding = this.getContentDivPadding();
+
+ this.closeDiv.style.right = contentDivPadding.right +
+ this.padding.right + "px";
+ this.closeDiv.style.top = contentDivPadding.top +
+ this.padding.top + "px";
+ }
+
+ this.updateBlocks();
+ },
+
+ /**
+ * Method: calculateNewPx
+ * Besides the standard offset as determined by the Anchored class, our
+ * Framed popups have a special 'offset' property for each of their
+ * positions, which is used to offset the popup relative to its anchor.
+ *
+ * Parameters:
+ * px - {<OpenLayers.Pixel>}
+ *
+ * Returns:
+ * {<OpenLayers.Pixel>} The the new px position of the popup on the screen
+ * relative to the passed-in px.
+ */
+ calculateNewPx:function(px) {
+ var newPx = OpenLayers.Popup.Anchored.prototype.calculateNewPx.apply(
+ this, arguments
+ );
+
+ newPx = newPx.offset(this.positionBlocks[this.relativePosition].offset);
+
+ return newPx;
+ },
+
+ /**
+ * Method: createBlocks
+ */
+ createBlocks: function() {
+ this.blocks = [];
+
+ //since all positions contain the same number of blocks, we can
+ // just pick the first position and use its blocks array to create
+ // our blocks array
+ var firstPosition = null;
+ for(var key in this.positionBlocks) {
+ firstPosition = key;
+ break;
+ }
+
+ var position = this.positionBlocks[firstPosition];
+ for (var i = 0; i < position.blocks.length; i++) {
+
+ var block = {};
+ this.blocks.push(block);
+
+ var divId = this.id + '_FrameDecorationDiv_' + i;
+ block.div = OpenLayers.Util.createDiv(divId,
+ null, null, null, "absolute", null, "hidden", null
+ );
+
+ var imgId = this.id + '_FrameDecorationImg_' + i;
+ var imageCreator =
+ (this.isAlphaImage) ? OpenLayers.Util.createAlphaImageDiv
+ : OpenLayers.Util.createImage;
+
+ block.image = imageCreator(imgId,
+ null, this.imageSize, this.imageSrc,
+ "absolute", null, null, null
+ );
+
+ block.div.appendChild(block.image);
+ this.groupDiv.appendChild(block.div);
+ }
+ },
+
+ /**
+ * Method: updateBlocks
+ * Internal method, called on initialize and when the popup's relative
+ * position has changed. This function takes care of re-positioning
+ * the popup's blocks in their appropropriate places.
+ */
+ updateBlocks: function() {
+ if (!this.blocks) {
+ this.createBlocks();
+ }
+
+ if (this.size && this.relativePosition) {
+ var position = this.positionBlocks[this.relativePosition];
+ for (var i = 0; i < position.blocks.length; i++) {
+
+ var positionBlock = position.blocks[i];
+ var block = this.blocks[i];
+
+ // adjust sizes
+ var l = positionBlock.anchor.left;
+ var b = positionBlock.anchor.bottom;
+ var r = positionBlock.anchor.right;
+ var t = positionBlock.anchor.top;
+
+ //note that we use the isNaN() test here because if the
+ // size object is initialized with a "auto" parameter, the
+ // size constructor calls parseFloat() on the string,
+ // which will turn it into NaN
+ //
+ var w = (isNaN(positionBlock.size.w)) ? this.size.w - (r + l)
+ : positionBlock.size.w;
+
+ var h = (isNaN(positionBlock.size.h)) ? this.size.h - (b + t)
+ : positionBlock.size.h;
+
+ block.div.style.width = w + 'px';
+ block.div.style.height = h + 'px';
+
+ block.div.style.left = (l != null) ? l + 'px' : '';
+ block.div.style.bottom = (b != null) ? b + 'px' : '';
+ block.div.style.right = (r != null) ? r + 'px' : '';
+ block.div.style.top = (t != null) ? t + 'px' : '';
+
+ block.image.style.left = positionBlock.position.x + 'px';
+ block.image.style.top = positionBlock.position.y + 'px';
+ }
+
+ this.contentDiv.style.left = this.padding.left + "px";
+ this.contentDiv.style.top = this.padding.top + "px";
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Popup.Framed"
+});
+/* ======================================================================
+ 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 ar 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: {},
+
+ /**
+ * 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);
+ },
+
+ /**
+ * 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 org = extent.left/resolution + " " +
+ (extent.top/resolution - this.size.h);
+ 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)+xOffset).toFixed();
+ node.style.top = ((geometry.y/resolution)-(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).toFixed(),
+ (bbox.bottom/resolution).toFixed(),
+ (bbox.right/resolution).toFixed(),
+ (bbox.top/resolution).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).toFixed() - radius) + "px";
+ node.style.top = ((geometry.y /resolution).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);
+ y = (comp.y/resolution);
+ 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;
+ y = comp.y / resolution;
+ 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 + "px";
+ node.style.top = geometry.y/resolution + "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;
+ y = comp.y / resolution;
+ 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
====================================================================== */
@@ -10304,14 +20895,45 @@
* the image will be hidden behind the frame.
*/
frame: null,
-
/**
* Property: layerAlphaHack
* {Boolean} True if the png alpha hack needs to be applied on the layer's div.
*/
layerAlphaHack: null,
+
+ /**
+ * Property: isBackBuffer
+ * {Boolean} Is this tile a back buffer tile?
+ */
+ isBackBuffer: false,
+
+ /**
+ * Property: lastRatio
+ * {Float} Used in transition code only. This is the previous ratio
+ * of the back buffer tile resolution to the map resolution. Compared
+ * with the current ratio to determine if zooming occurred.
+ */
+ lastRatio: 1,
+ /**
+ * Property: isFirstDraw
+ * {Boolean} Is this the first time the tile is being drawn?
+ * This is used to force resetBackBuffer to synchronize
+ * the backBufferTile with the foreground tile the first time
+ * the foreground tile loads so that if the user zooms
+ * before the layer has fully loaded, the backBufferTile for
+ * tiles that have been loaded can be used.
+ */
+ isFirstDraw: true,
+
+ /**
+ * Property: backBufferTile
+ * {<OpenLayers.Tile>} A clone of the tile used to create transition
+ * effects when the tile is moved or changes resolution.
+ */
+ backBufferTile: null,
+
/** TBD 3.0 - reorder the parameters to the init function to remove
* URL. the getUrl() function on the layer gets called on
* each draw(), so no need to specify it here.
@@ -10353,12 +20975,22 @@
this.frame.removeChild(this.imgDiv);
this.imgDiv.map = null;
}
+ this.imgDiv.urls = null;
}
this.imgDiv = null;
if ((this.frame != null) && (this.frame.parentNode == this.layer.div)) {
this.layer.div.removeChild(this.frame);
}
this.frame = null;
+
+ /* clean up the backBufferTile if it exists */
+ if (this.backBufferTile) {
+ this.backBufferTile.destroy();
+ this.backBufferTile = null;
+ }
+
+ this.layer.events.unregister("loadend", this, this.resetBackBuffer);
+
OpenLayers.Tile.prototype.destroy.apply(this, arguments);
},
@@ -10401,8 +21033,47 @@
if (this.layer != this.layer.map.baseLayer && this.layer.reproject) {
this.bounds = this.getBoundsFromBaseLayer(this.position);
}
- if (!OpenLayers.Tile.prototype.draw.apply(this, arguments)) {
- return false;
+ var drawTile = OpenLayers.Tile.prototype.draw.apply(this, arguments);
+
+ if (OpenLayers.Util.indexOf(this.layer.SUPPORTED_TRANSITIONS, this.layer.transitionEffect) != -1) {
+ if (drawTile) {
+ //we use a clone of this tile to create a double buffer for visual
+ //continuity. The backBufferTile is used to create transition
+ //effects while the tile in the grid is repositioned and redrawn
+ if (!this.backBufferTile) {
+ this.backBufferTile = this.clone();
+ this.backBufferTile.hide();
+ // this is important. It allows the backBuffer to place itself
+ // appropriately in the DOM. The Image subclass needs to put
+ // the backBufferTile behind the main tile so the tiles can
+ // load over top and display as soon as they are loaded.
+ this.backBufferTile.isBackBuffer = true;
+
+ // potentially end any transition effects when the tile loads
+ this.events.register('loadend', this, this.resetBackBuffer);
+
+ // clear transition back buffer tile only after all tiles in
+ // this layer have loaded to avoid visual glitches
+ this.layer.events.register("loadend", this, this.resetBackBuffer);
+ }
+ // run any transition effects
+ this.startTransition();
+ } else {
+ // if we aren't going to draw the tile, then the backBuffer should
+ // be hidden too!
+ if (this.backBufferTile) {
+ this.backBufferTile.clear();
+ }
+ }
+ } else {
+ if (drawTile && this.isFirstDraw) {
+ this.events.register('loadend', this, this.showTile);
+ this.isFirstDraw = false;
+ }
+ }
+
+ if (!drawTile) {
+ return false;
}
if (this.isLoading) {
@@ -10416,6 +21087,42 @@
return this.renderTile();
},
+ /**
+ * Method: resetBackBuffer
+ * Triggered by two different events, layer loadend, and tile loadend.
+ * In any of these cases, we check to see if we can hide the
+ * backBufferTile yet and update its parameters to match the
+ * foreground tile.
+ *
+ * Basic logic:
+ * - If the backBufferTile hasn't been drawn yet, reset it
+ * - If layer is still loading, show foreground tile but don't hide
+ * the backBufferTile yet
+ * - If layer is done loading, reset backBuffer tile and show
+ * foreground tile
+ */
+ resetBackBuffer: function() {
+ this.showTile();
+ if (this.backBufferTile &&
+ (this.isFirstDraw || !this.layer.numLoadingTiles)) {
+ this.isFirstDraw = false;
+ // check to see if the backBufferTile is within the max extents
+ // before rendering it
+ var maxExtent = this.layer.maxExtent;
+ var withinMaxExtent = (maxExtent &&
+ this.bounds.intersectsBounds(maxExtent, false));
+ if (withinMaxExtent) {
+ this.backBufferTile.position = this.position;
+ this.backBufferTile.bounds = this.bounds;
+ this.backBufferTile.size = this.size;
+ this.backBufferTile.imageSize = this.layer.imageSize || this.size;
+ this.backBufferTile.imageOffset = this.layer.imageOffset;
+ this.backBufferTile.resolution = this.layer.getResolution();
+ this.backBufferTile.renderTile();
+ }
+ }
+ },
+
/**
* Method: renderTile
* Internal function to actually initialize the image tile,
@@ -10428,6 +21135,11 @@
this.imgDiv.viewRequestID = this.layer.map.viewRequestID;
+ // needed for changing to a different serve for onload error
+ if (this.layer.url instanceof Array) {
+ this.imgDiv.urls = this.layer.url.slice();
+ }
+
this.url = this.layer.getURL(this.bounds);
// position the frame
OpenLayers.Util.modifyDOMElement(this.frame,
@@ -10685,6 +21397,201 @@
OpenLayers.Util.getBrowserName() == "safari" ||
OpenLayers.Util.getBrowserName() == "opera");
/* ======================================================================
+ OpenLayers/Tile/WFS.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/Tile.js
+ * @requires OpenLayers/Request/XMLHttpRequest.js
+ */
+
+/**
+ * Class: OpenLayers.Tile.WFS
+ * Instances of OpenLayers.Tile.WFS are used to manage the image tiles
+ * used by various layers. Create a new image tile with the
+ * <OpenLayers.Tile.WFS> constructor.
+ *
+ * Inherits from:
+ * - <OpenLayers.Tile>
+ */
+OpenLayers.Tile.WFS = OpenLayers.Class(OpenLayers.Tile, {
+
+ /**
+ * Property: features
+ * {Array(<OpenLayers.Feature>)} list of features in this tile
+ */
+ features: null,
+
+ /**
+ * Property: url
+ * {String}
+ */
+ url: null,
+
+ /**
+ * Property: request
+ * {<OpenLayers.Request.XMLHttpRequest>}
+ */
+ request: null,
+
+ /** TBD 3.0 - reorder the parameters to the init function to put URL
+ * as last, so we can continue to call tile.initialize()
+ * without changing the arguments.
+ *
+ * Constructor: OpenLayers.Tile.WFS
+ * Constructor for a new <OpenLayers.Tile.WFS> instance.
+ *
+ * Parameters:
+ * layer - {<OpenLayers.Layer>} layer that the tile will go in.
+ * position - {<OpenLayers.Pixel>}
+ * bounds - {<OpenLayers.Bounds>}
+ * url - {<String>}
+ * size - {<OpenLayers.Size>}
+ */
+ initialize: function(layer, position, bounds, url, size) {
+ OpenLayers.Tile.prototype.initialize.apply(this, arguments);
+ this.url = url;
+ this.features = [];
+ },
+
+ /**
+ * APIMethod: destroy
+ * nullify references to prevent circular references and memory leaks
+ */
+ destroy: function() {
+ OpenLayers.Tile.prototype.destroy.apply(this, arguments);
+ this.destroyAllFeatures();
+ this.features = null;
+ this.url = null;
+ if(this.request) {
+ this.request.abort();
+ //this.request.destroy();
+ this.request = null;
+ }
+ },
+
+ /**
+ * Method: clear
+ * Clear the tile of any bounds/position-related data so that it can
+ * be reused in a new location.
+ */
+ clear: function() {
+ this.destroyAllFeatures();
+ },
+
+ /**
+ * Method: draw
+ * Check that a tile should be drawn, and load features for it.
+ */
+ draw:function() {
+ if (OpenLayers.Tile.prototype.draw.apply(this, arguments)) {
+ if (this.isLoading) {
+ //if already loading, send 'reload' instead of 'loadstart'.
+ this.events.triggerEvent("reload");
+ } else {
+ this.isLoading = true;
+ this.events.triggerEvent("loadstart");
+ }
+ this.loadFeaturesForRegion(this.requestSuccess);
+ }
+ },
+
+ /**
+ * Method: loadFeaturesForRegion
+ * Abort any pending requests and issue another request for data.
+ *
+ * Input are function pointers for what to do on success and failure.
+ *
+ * Parameters:
+ * success - {function}
+ * failure - {function}
+ */
+ loadFeaturesForRegion:function(success, failure) {
+ if(this.request) {
+ this.request.abort();
+ }
+ this.request = OpenLayers.Request.GET({
+ url: this.url,
+ success: success,
+ failure: failure,
+ scope: this
+ });
+ },
+
+ /**
+ * Method: requestSuccess
+ * Called on return from request succcess. Adds results via
+ * layer.addFeatures in vector mode, addResults otherwise.
+ *
+ * Parameters:
+ * request - {<OpenLayers.Request.XMLHttpRequest>}
+ */
+ requestSuccess:function(request) {
+ if (this.features) {
+ var doc = request.responseXML;
+ if (!doc || !doc.documentElement) {
+ doc = request.responseText;
+ }
+ if (this.layer.vectorMode) {
+ this.layer.addFeatures(this.layer.formatObject.read(doc));
+ } else {
+ var xml = new OpenLayers.Format.XML();
+ if (typeof doc == "string") {
+ doc = xml.read(doc);
+ }
+ var resultFeatures = xml.getElementsByTagNameNS(
+ doc, "http://www.opengis.net/gml", "featureMember"
+ );
+ this.addResults(resultFeatures);
+ }
+ }
+ if (this.events) {
+ this.events.triggerEvent("loadend");
+ }
+
+ //request produced with success, we can delete the request object.
+ //this.request.destroy();
+ this.request = null;
+ },
+
+ /**
+ * Method: addResults
+ * Construct new feature via layer featureClass constructor, and add to
+ * this.features.
+ *
+ * Parameters:
+ * results - {Object}
+ */
+ addResults: function(results) {
+ for (var i=0; i < results.length; i++) {
+ var feature = new this.layer.featureClass(this.layer,
+ results[i]);
+ this.features.push(feature);
+ }
+ },
+
+
+ /**
+ * Method: destroyAllFeatures
+ * Iterate through and call destroy() on each feature, removing it from
+ * the local array
+ */
+ destroyAllFeatures: function() {
+ while(this.features.length > 0) {
+ var feature = this.features.shift();
+ feature.destroy();
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Tile.WFS"
+ }
+);
+/* ======================================================================
OpenLayers/Control/OverviewMap.js
====================================================================== */
@@ -10778,7 +21685,7 @@
* resolution at which to zoom farther in on the overview map.
*/
maxRatio: 32,
-
+
/**
* APIProperty: mapOptions
* {Object} An object containing any non-default properties to be sent to
@@ -10794,6 +21701,12 @@
handlers: null,
/**
+ * Property: resolutionFactor
+ * {Object}
+ */
+ resolutionFactor: 1,
+
+ /**
* Constructor: OpenLayers.Control.OverviewMap
* Create a new overview map
*
@@ -10930,7 +21843,7 @@
var eventsToStop = ['dblclick','mousedown'];
- for (var i = 0; i < eventsToStop.length; i++) {
+ for (var i=0, len=eventsToStop.length; i<len; i++) {
OpenLayers.Event.observe(this.maximizeDiv,
eventsToStop[i],
@@ -11092,6 +22005,13 @@
Math.max(mapExtent.bottom, maxExtent.bottom),
Math.min(mapExtent.right, maxExtent.right),
Math.min(mapExtent.top, maxExtent.top));
+
+ if (this.ovmap.getProjection() != this.map.getProjection()) {
+ testExtent = testExtent.transform(
+ this.map.getProjectionObject(),
+ this.ovmap.getProjectionObject() );
+ }
+
var resRatio = this.ovmap.getResolution() / this.map.getResolution();
return ((resRatio > this.minRatio) &&
(resRatio <= this.maxRatio) &&
@@ -11113,8 +22033,16 @@
// zoom out overview map
targetRes = this.maxRatio * mapRes;
}
- this.ovmap.setCenter(this.map.center,
- this.ovmap.getZoomForResolution(targetRes));
+ var center;
+ if (this.ovmap.getProjection() != this.map.getProjection()) {
+ center = this.map.center.clone();
+ center.transform(this.map.getProjectionObject(),
+ this.ovmap.getProjectionObject() );
+ } else {
+ center = this.map.center;
+ }
+ this.ovmap.setCenter(center, this.ovmap.getZoomForResolution(
+ targetRes * this.resolutionFactor));
this.updateRectToMap();
},
@@ -11176,6 +22104,15 @@
}
});
+ if (this.ovmap.getProjection() != this.map.getProjection()) {
+ var sourceUnits = this.map.getProjectionObject().getUnits() ||
+ this.map.units || this.map.baseLayer.units;
+ var targetUnits = this.ovmap.getProjectionObject().getUnits() ||
+ this.ovmap.units || this.ovmap.baseLayer.units;
+ this.resolutionFactor = sourceUnits && targetUnits ?
+ OpenLayers.INCHES_PER_UNIT[sourceUnits] /
+ OpenLayers.INCHES_PER_UNIT[targetUnits] : 1;
+ }
},
/**
@@ -11183,14 +22120,16 @@
* Updates the extent rectangle position and size to match the map extent
*/
updateRectToMap: function() {
- // The base layer for overview map needs to be in the same projection
- // as the base layer for the main map. This should be made more robust.
- if(this.map.units != 'degrees') {
- if(this.ovmap.getProjection() && (this.map.getProjection() != this.ovmap.getProjection())) {
- alert(OpenLayers.i18n("sameProjection"));
- }
+ // If the projections differ we need to reproject
+ var bounds;
+ if (this.ovmap.getProjection() != this.map.getProjection()) {
+ bounds = this.map.getExtent().transform(
+ this.map.getProjectionObject(),
+ this.ovmap.getProjectionObject() );
+ } else {
+ bounds = this.map.getExtent();
}
- var pxBounds = this.getRectBoundsFromMapBounds(this.map.getExtent());
+ var pxBounds = this.getRectBoundsFromMapBounds(bounds);
if (pxBounds) {
this.setRectPxBounds(pxBounds);
}
@@ -11202,6 +22141,11 @@
*/
updateMapToRect: function() {
var lonLatBounds = this.getMapBoundsFromRectBounds(this.rectPxBounds);
+ if (this.ovmap.getProjection() != this.map.getProjection()) {
+ lonLatBounds = lonLatBounds.transform(
+ this.ovmap.getProjectionObject(),
+ this.map.getProjectionObject() );
+ }
this.map.panTo(lonLatBounds.getCenterLonLat());
},
@@ -11341,6 +22285,1306 @@
CLASS_NAME: 'OpenLayers.Control.OverviewMap'
});
/* ======================================================================
+ OpenLayers/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/Util.js
+ * @requires OpenLayers/Marker.js
+ * @requires OpenLayers/Popup/AnchoredBubble.js
+ */
+
+/**
+ * Class: OpenLayers.Feature
+ * Features are combinations of geography and attributes. The OpenLayers.Feature
+ * class specifically combines a marker and a lonlat.
+ */
+OpenLayers.Feature = OpenLayers.Class({
+
+ /**
+ * Property: layer
+ * {<OpenLayers.Layer>}
+ */
+ layer: null,
+
+ /**
+ * Property: id
+ * {String}
+ */
+ id: null,
+
+ /**
+ * Property: lonlat
+ * {<OpenLayers.LonLat>}
+ */
+ lonlat: null,
+
+ /**
+ * Property: data
+ * {Object}
+ */
+ data: null,
+
+ /**
+ * Property: marker
+ * {<OpenLayers.Marker>}
+ */
+ marker: null,
+
+ /**
+ * APIProperty: popupClass
+ * {<OpenLayers.Class>} The class which will be used to instantiate
+ * a new Popup. Default is <OpenLayers.Popup.AnchoredBubble>.
+ */
+ popupClass: OpenLayers.Popup.AnchoredBubble,
+
+ /**
+ * Property: popup
+ * {<OpenLayers.Popup>}
+ */
+ popup: null,
+
+ /**
+ * Constructor: OpenLayers.Feature
+ * Constructor for features.
+ *
+ * Parameters:
+ * layer - {<OpenLayers.Layer>}
+ * lonlat - {<OpenLayers.LonLat>}
+ * data - {Object}
+ *
+ * Returns:
+ * {<OpenLayers.Feature>}
+ */
+ initialize: function(layer, lonlat, data) {
+ this.layer = layer;
+ this.lonlat = lonlat;
+ this.data = (data != null) ? data : {};
+ this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
+ },
+
+ /**
+ * Method: destroy
+ * nullify references to prevent circular references and memory leaks
+ */
+ destroy: function() {
+
+ //remove the popup from the map
+ if ((this.layer != null) && (this.layer.map != null)) {
+ if (this.popup != null) {
+ this.layer.map.removePopup(this.popup);
+ }
+ }
+
+ this.layer = null;
+ this.id = null;
+ this.lonlat = null;
+ this.data = null;
+ if (this.marker != null) {
+ this.destroyMarker(this.marker);
+ this.marker = null;
+ }
+ if (this.popup != null) {
+ this.destroyPopup(this.popup);
+ this.popup = null;
+ }
+ },
+
+ /**
+ * Method: onScreen
+ *
+ * Returns:
+ * {Boolean} Whether or not the feature is currently visible on screen
+ * (based on its 'lonlat' property)
+ */
+ onScreen:function() {
+
+ var onScreen = false;
+ if ((this.layer != null) && (this.layer.map != null)) {
+ var screenBounds = this.layer.map.getExtent();
+ onScreen = screenBounds.containsLonLat(this.lonlat);
+ }
+ return onScreen;
+ },
+
+
+ /**
+ * Method: createMarker
+ * Based on the data associated with the Feature, create and return a marker object.
+ *
+ * Returns:
+ * {<OpenLayers.Marker>} A Marker Object created from the 'lonlat' and 'icon' properties
+ * set in this.data. If no 'lonlat' is set, returns null. If no
+ * 'icon' is set, OpenLayers.Marker() will load the default image.
+ *
+ * Note - this.marker is set to return value
+ *
+ */
+ createMarker: function() {
+
+ if (this.lonlat != null) {
+ this.marker = new OpenLayers.Marker(this.lonlat, this.data.icon);
+ }
+ return this.marker;
+ },
+
+ /**
+ * Method: destroyMarker
+ * Destroys marker.
+ * If user overrides the createMarker() function, s/he should be able
+ * to also specify an alternative function for destroying it
+ */
+ destroyMarker: function() {
+ this.marker.destroy();
+ },
+
+ /**
+ * Method: createPopup
+ * Creates a popup object created from the 'lonlat', 'popupSize',
+ * and 'popupContentHTML' properties set in this.data. It uses
+ * this.marker.icon as default anchor.
+ *
+ * If no 'lonlat' is set, returns null.
+ * If no this.marker has been created, no anchor is sent.
+ *
+ * Note - the returned popup object is 'owned' by the feature, so you
+ * cannot use the popup's destroy method to discard the popup.
+ * Instead, you must use the feature's destroyPopup
+ *
+ * Note - this.popup is set to return value
+ *
+ * Parameters:
+ * closeBox - {Boolean} create popup with closebox or not
+ *
+ * Returns:
+ * {<OpenLayers.Popup>} Returns the created popup, which is also set
+ * as 'popup' property of this feature. Will be of whatever type
+ * specified by this feature's 'popupClass' property, but must be
+ * of type <OpenLayers.Popup>.
+ *
+ */
+ createPopup: function(closeBox) {
+
+ if (this.lonlat != null) {
+
+ var id = this.id + "_popup";
+ var anchor = (this.marker) ? this.marker.icon : null;
+
+ if (!this.popup) {
+ this.popup = new this.popupClass(id,
+ this.lonlat,
+ this.data.popupSize,
+ this.data.popupContentHTML,
+ anchor,
+ closeBox);
+ }
+ if (this.data.overflow != null) {
+ this.popup.contentDiv.style.overflow = this.data.overflow;
+ }
+
+ this.popup.feature = this;
+ }
+ return this.popup;
+ },
+
+
+ /**
+ * Method: destroyPopup
+ * Destroys the popup created via createPopup.
+ *
+ * As with the marker, if user overrides the createPopup() function, s/he
+ * should also be able to override the destruction
+ */
+ destroyPopup: function() {
+ if (this.popup) {
+ this.popup.feature = null;
+ this.popup.destroy();
+ this.popup = null;
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Feature"
+});
+/* ======================================================================
+ OpenLayers/Format/WMC.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/Format/XML.js
+ */
+
+/**
+ * Class: OpenLayers.Format.WMC
+ * Read and write Web Map Context documents.
+ *
+ * Inherits from:
+ * - <OpenLayers.Format.XML>
+ */
+OpenLayers.Format.WMC = OpenLayers.Class({
+
+ /**
+ * APIProperty: defaultVersion
+ * {String} Version number to assume if none found. Default is "1.1.0".
+ */
+ defaultVersion: "1.1.0",
+
+ /**
+ * APIProperty: version
+ * {String} Specify a version string if one is known.
+ */
+ version: null,
+
+ /**
+ * Property: layerOptions
+ * {Object} Default options for layers created by the parser. These
+ * options are overridden by the options which are read from the
+ * capabilities document.
+ */
+ layerOptions: null,
+
+ /**
+ * Property: parser
+ * {Object} Instance of the versioned parser. Cached for multiple read and
+ * write calls of the same version.
+ */
+ parser: null,
+
+ /**
+ * Constructor: OpenLayers.Format.WMC
+ * Create a new parser for WMC docs.
+ *
+ * Parameters:
+ * options - {Object} An optional object whose properties will be set on
+ * this instance.
+ */
+ initialize: function(options) {
+ OpenLayers.Util.extend(this, options);
+ this.options = options;
+ },
+
+ /**
+ * APIMethod: read
+ * Read WMC data from a string, and return an object with map properties
+ * and a list of layers.
+ *
+ * Parameters:
+ * data - {String} or {DOMElement} data to read/parse.
+ * options - {Object} The options object must contain a map property. If
+ * the map property is a string, it must be the id of a dom element
+ * where the new map will be placed. If the map property is an
+ * <OpenLayers.Map>, the layers from the context document will be added
+ * to the map.
+ *
+ * Returns:
+ * {<OpenLayers.Map>} A map based on the context.
+ */
+ read: function(data, options) {
+ if(typeof data == "string") {
+ data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
+ }
+ var root = data.documentElement;
+ var version = this.version;
+ if(!version) {
+ version = root.getAttribute("version");
+ if(!version) {
+ version = this.defaultVersion;
+ }
+ }
+ if(!this.parser || this.parser.VERSION != version) {
+ var format = OpenLayers.Format.WMC[
+ "v" + version.replace(/\./g, "_")
+ ];
+ if(!format) {
+ throw "Can't find a WMC parser for version " +
+ version;
+ }
+ this.parser = new format(this.options);
+ }
+ var context = this.parser.read(data, options);
+ var map;
+ if(options.map) {
+ this.context = context;
+ if(options.map instanceof OpenLayers.Map) {
+ map = this.mergeContextToMap(context, options.map);
+ } else {
+ map = this.contextToMap(context, options.map);
+ }
+ } else {
+ // not documented as part of the API, provided as a non-API option
+ map = context;
+ }
+ return map;
+ },
+
+ /**
+ * Method: contextToMap
+ * Create a map given a context object.
+ *
+ * Parameters:
+ * context - {Object} The context object.
+ * id - {String | Element} The dom element or element id that will contain
+ * the map.
+ *
+ * Returns:
+ * {<OpenLayers.Map>} A map based on the context object.
+ */
+ contextToMap: function(context, id) {
+ var map = new OpenLayers.Map(id, {
+ maxExtent: context.maxExtent,
+ projection: context.projection
+ });
+ map.addLayers(context.layers);
+ map.setCenter(
+ context.bounds.getCenterLonLat(),
+ map.getZoomForExtent(context.bounds, true)
+ );
+ return map;
+ },
+
+ /**
+ * Method: mergeContextToMap
+ * Add layers from a context object to a map.
+ *
+ * Parameters:
+ * context - {Object} The context object.
+ * map - {<OpenLayers.Map>} The map.
+ *
+ * Returns:
+ * {<OpenLayers.Map>} The same map with layers added.
+ */
+ mergeContextToMap: function(context, map) {
+ map.addLayers(context.layers);
+ return map;
+ },
+
+ /**
+ * APIMethod: write
+ * Write a WMC document given a map.
+ *
+ * Parameters:
+ * obj - {<OpenLayers.Map> | Object} A map or context object.
+ * options - {Object} Optional configuration object.
+ *
+ * Returns:
+ * {String} A WMC document string.
+ */
+ write: function(obj, options) {
+ if(obj.CLASS_NAME == "OpenLayers.Map") {
+ obj = this.mapToContext(obj);
+ }
+ var version = (options && options.version) ||
+ this.version || this.defaultVersion;
+ if(!this.parser || this.parser.VERSION != version) {
+ var format = OpenLayers.Format.WMC[
+ "v" + version.replace(/\./g, "_")
+ ];
+ if(!format) {
+ throw "Can't find a WMS capabilities parser for version " +
+ version;
+ }
+ this.parser = new format(this.options);
+ }
+ var wmc = this.parser.write(obj, options);
+ return wmc;
+ },
+
+ /**
+ * Method: mapToContext
+ * Create a context object given a map.
+ *
+ * Parameters:
+ * map - {<OpenLayers.Map>} The map.
+ *
+ * Returns:
+ * {Object} A context object.
+ */
+ mapToContext: function(map) {
+ var context = {
+ bounds: map.getExtent(),
+ maxExtent: map.maxExtent,
+ projection: map.projection,
+ layers: map.layers,
+ size: map.getSize()
+ };
+ return context;
+ },
+
+ CLASS_NAME: "OpenLayers.Format.WMC"
+
+});
+/* ======================================================================
+ OpenLayers/Format/WMC/v1.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/Format/XML.js
+ */
+
+/**
+ * Class: OpenLayers.Format.WMC.v1
+ * Superclass for WMC version 1 parsers.
+ *
+ * Inherits from:
+ * - <OpenLayers.Format.XML>
+ */
+OpenLayers.Format.WMC.v1 = OpenLayers.Class(OpenLayers.Format.XML, {
+
+ /**
+ * Property: namespaces
+ * {Object} Mapping of namespace aliases to namespace URIs.
+ */
+ namespaces: {
+ ol: "http://openlayers.org/context",
+ wmc: "http://www.opengis.net/context",
+ sld: "http://www.opengis.net/sld",
+ xlink: "http://www.w3.org/1999/xlink",
+ xsi: "http://www.w3.org/2001/XMLSchema-instance"
+ },
+
+ /**
+ * Property: schemaLocation
+ * {String} Schema location for a particular minor version.
+ */
+ schemaLocation: "",
+
+ /**
+ * Method: getNamespacePrefix
+ * Get the namespace prefix for a given uri from the <namespaces> object.
+ *
+ * Returns:
+ * {String} A namespace prefix or null if none found.
+ */
+ getNamespacePrefix: function(uri) {
+ var prefix = null;
+ if(uri == null) {
+ prefix = this.namespaces[this.defaultPrefix];
+ } else {
+ for(prefix in this.namespaces) {
+ if(this.namespaces[prefix] == uri) {
+ break;
+ }
+ }
+ }
+ return prefix;
+ },
+
+ /**
+ * Property: defaultPrefix
+ */
+ defaultPrefix: "wmc",
+
+ /**
+ * Property: rootPrefix
+ * {String} Prefix on the root node that maps to the context namespace URI.
+ */
+ rootPrefix: null,
+
+ /**
+ * Property: defaultStyleName
+ * {String} Style name used if layer has no style param. Default is "".
+ */
+ defaultStyleName: "",
+
+ /**
+ * Property: defaultStyleTitle
+ * {String} Default style title. Default is "Default".
+ */
+ defaultStyleTitle: "Default",
+
+ /**
+ * Constructor: OpenLayers.Format.WMC.v1
+ * Instances of this class are not created directly. Use the
+ * <OpenLayers.Format.WMC> constructor instead.
+ *
+ * Parameters:
+ * options - {Object} An optional object whose properties will be set on
+ * this instance.
+ */
+ initialize: function(options) {
+ OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
+ },
+
+ /**
+ * Method: read
+ * Read capabilities data from a string, and return a list of layers.
+ *
+ * Parameters:
+ * data - {String} or {DOMElement} data to read/parse.
+ *
+ * Returns:
+ * {Array} List of named layers.
+ */
+ read: function(data) {
+ if(typeof data == "string") {
+ data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
+ }
+ var root = data.documentElement;
+ this.rootPrefix = root.prefix;
+ var context = {
+ version: root.getAttribute("version")
+ };
+ this.runChildNodes(context, root);
+ return context;
+ },
+
+ /**
+ * Method: runChildNodes
+ */
+ runChildNodes: function(obj, node) {
+ var children = node.childNodes;
+ var childNode, processor, prefix, local;
+ for(var i=0, len=children.length; i<len; ++i) {
+ childNode = children[i];
+ if(childNode.nodeType == 1) {
+ prefix = this.getNamespacePrefix(childNode.namespaceURI);
+ local = childNode.nodeName.split(":").pop();
+ processor = this["read_" + prefix + "_" + local];
+ if(processor) {
+ processor.apply(this, [obj, childNode]);
+ }
+ }
+ }
+ },
+
+ /**
+ * Method: read_wmc_General
+ */
+ read_wmc_General: function(context, node) {
+ this.runChildNodes(context, node);
+ },
+
+ /**
+ * Method: read_wmc_BoundingBox
+ */
+ read_wmc_BoundingBox: function(context, node) {
+ context.projection = node.getAttribute("SRS");
+ context.bounds = new OpenLayers.Bounds(
+ parseFloat(node.getAttribute("minx")),
+ parseFloat(node.getAttribute("miny")),
+ parseFloat(node.getAttribute("maxx")),
+ parseFloat(node.getAttribute("maxy"))
+ );
+ },
+
+ /**
+ * Method: read_wmc_LayerList
+ */
+ read_wmc_LayerList: function(context, node) {
+ context.layers = [];
+ this.runChildNodes(context, node);
+ },
+
+ /**
+ * Method: read_wmc_Layer
+ */
+ read_wmc_Layer: function(context, node) {
+ var layerInfo = {
+ params: {},
+ options: {
+ visibility: (node.getAttribute("hidden") != "1"),
+ queryable: (node.getAttribute("queryable") == "1")
+
+ },
+ formats: [],
+ styles: []
+ };
+ this.runChildNodes(layerInfo, node);
+ // set properties common to multiple objects on layer options/params
+ layerInfo.params.layers = layerInfo.name;
+ layerInfo.options.maxExtent = layerInfo.maxExtent;
+ // create the layer
+ var layer = this.getLayerFromInfo(layerInfo);
+ context.layers.push(layer);
+ },
+
+ /**
+ * Method: getLayerFromInfo
+ * Create a WMS layer from a layerInfo object.
+ *
+ * Parameters:
+ * layerInfo - {Object} An object representing a WMS layer.
+ *
+ * Returns:
+ * {<OpenLayers.Layer.WMS>} A WMS layer.
+ */
+ getLayerFromInfo: function(layerInfo) {
+ var options = layerInfo.options;
+ if (this.layerOptions) {
+ OpenLayers.Util.applyDefaults(options, this.layerOptions);
+ }
+ var layer = new OpenLayers.Layer.WMS(
+ layerInfo.title,
+ layerInfo.href,
+ layerInfo.params,
+ options
+ );
+ return layer;
+ },
+
+ /**
+ * Method: read_wmc_Extension
+ */
+ read_wmc_Extension: function(obj, node) {
+ this.runChildNodes(obj, node);
+ },
+
+ /**
+ * Method: read_ol_units
+ */
+ read_ol_units: function(layerInfo, node) {
+ layerInfo.options.units = this.getChildValue(node);
+ },
+
+ /**
+ * Method: read_ol_maxExtent
+ */
+ read_ol_maxExtent: function(obj, node) {
+ var bounds = new OpenLayers.Bounds(
+ node.getAttribute("minx"), node.getAttribute("miny"),
+ node.getAttribute("maxx"), node.getAttribute("maxy")
+ );
+ obj.maxExtent = bounds;
+ },
+
+ /**
+ * Method: read_ol_transparent
+ */
+ read_ol_transparent: function(layerInfo, node) {
+ layerInfo.params.transparent = this.getChildValue(node);
+ },
+
+ /**
+ * Method: read_ol_numZoomLevels
+ */
+ read_ol_numZoomLevels: function(layerInfo, node) {
+ layerInfo.options.numZoomLevels = parseInt(this.getChildValue(node));
+ },
+
+ /**
+ * Method: read_ol_opacity
+ */
+ read_ol_opacity: function(layerInfo, node) {
+ layerInfo.options.opacity = parseFloat(this.getChildValue(node));
+ },
+
+ /**
+ * Method: read_ol_singleTile
+ */
+ read_ol_singleTile: function(layerInfo, node) {
+ layerInfo.options.singleTile = (this.getChildValue(node) == "true");
+ },
+
+ /**
+ * Method: read_ol_isBaseLayer
+ */
+ read_ol_isBaseLayer: function(layerInfo, node) {
+ layerInfo.options.isBaseLayer = (this.getChildValue(node) == "true");
+ },
+
+ /**
+ * Method: read_ol_displayInLayerSwitcher
+ */
+ read_ol_displayInLayerSwitcher: function(layerInfo, node) {
+ layerInfo.options.displayInLayerSwitcher =
+ (this.getChildValue(node) == "true");
+ },
+
+ /**
+ * Method: read_wmc_Server
+ */
+ read_wmc_Server: function(layerInfo, node) {
+ layerInfo.params.version = node.getAttribute("version");
+ this.runChildNodes(layerInfo, node);
+ },
+
+ /**
+ * Method: read_wmc_FormatList
+ */
+ read_wmc_FormatList: function(layerInfo, node) {
+ this.runChildNodes(layerInfo, node);
+ },
+
+ /**
+ * Method: read_wmc_Format
+ */
+ read_wmc_Format: function(layerInfo, node) {
+ var format = this.getChildValue(node);
+ layerInfo.formats.push(format);
+ if(node.getAttribute("current") == "1") {
+ layerInfo.params.format = format;
+ }
+ },
+
+ /**
+ * Method: read_wmc_StyleList
+ */
+ read_wmc_StyleList: function(layerInfo, node) {
+ this.runChildNodes(layerInfo, node);
+ },
+
+ /**
+ * Method: read_wmc_Style
+ */
+ read_wmc_Style: function(layerInfo, node) {
+ var style = {};
+ this.runChildNodes(style, node);
+ if(node.getAttribute("current") == "1") {
+ // three style types to consider
+ // 1) linked SLD
+ // 2) inline SLD
+ // 3) named style
+ // running child nodes always gets name, optionally gets href or body
+ if(style.href) {
+ layerInfo.params.sld = style.href;
+ } else if(style.body) {
+ layerInfo.params.sld_body = style.body;
+ } else {
+ layerInfo.params.styles = style.name;
+ }
+ }
+ layerInfo.styles.push(style);
+ },
+
+ /**
+ * Method: read_wmc_SLD
+ */
+ read_wmc_SLD: function(style, node) {
+ this.runChildNodes(style, node);
+ // style either comes back with an href or a body property
+ },
+
+ /**
+ * Method: read_sld_StyledLayerDescriptor
+ */
+ read_sld_StyledLayerDescriptor: function(sld, node) {
+ var xml = OpenLayers.Format.XML.prototype.write.apply(this, [node]);
+ sld.body = xml;
+ },
+
+ /**
+ * Method: read_wmc_OnlineResource
+ */
+ read_wmc_OnlineResource: function(obj, node) {
+ obj.href = this.getAttributeNS(
+ node, this.namespaces.xlink, "href"
+ );
+ },
+
+ /**
+ * Method: read_wmc_Name
+ */
+ read_wmc_Name: function(obj, node) {
+ var name = this.getChildValue(node);
+ if(name) {
+ obj.name = name;
+ }
+ },
+
+ /**
+ * Method: read_wmc_Title
+ */
+ read_wmc_Title: function(obj, node) {
+ var title = this.getChildValue(node);
+ if(title) {
+ obj.title = title;
+ }
+ },
+
+ /**
+ * Method: read_wmc_MetadataURL
+ */
+ read_wmc_MetadataURL: function(layerInfo, node) {
+ var metadataURL = {};
+ var links = node.getElementsByTagName("OnlineResource");
+ if(links.length > 0) {
+ this.read_wmc_OnlineResource(metadataURL, links[0]);
+ }
+ layerInfo.options.metadataURL = metadataURL.href;
+
+ },
+
+ /**
+ * Method: read_wmc_Abstract
+ */
+ read_wmc_Abstract: function(obj, node) {
+ var abst = this.getChildValue(node);
+ if(abst) {
+ obj["abstract"] = abst;
+ }
+ },
+
+ /**
+ * Method: read_wmc_LatLonBoundingBox
+ */
+ read_wmc_LatLonBoundingBox: function(layer, node) {
+ layer.llbbox = [
+ parseFloat(node.getAttribute("minx")),
+ parseFloat(node.getAttribute("miny")),
+ parseFloat(node.getAttribute("maxx")),
+ parseFloat(node.getAttribute("maxy"))
+ ];
+ },
+
+ /**
+ * Method: read_wmc_LegendURL
+ */
+ read_wmc_LegendURL: function(style, node) {
+ var legend = {
+ width: node.getAttribute('width'),
+ height: node.getAttribute('height')
+ };
+ var links = node.getElementsByTagName("OnlineResource");
+ if(links.length > 0) {
+ this.read_wmc_OnlineResource(legend, links[0]);
+ }
+ style.legend = legend;
+ },
+
+ /**
+ * Method: write
+ *
+ * Parameters:
+ * context - {Object} An object representing the map context.
+ * options - {Object} Optional object.
+ *
+ * Returns:
+ * {String} A WMC document string.
+ */
+ write: function(context, options) {
+ var root = this.createElementDefaultNS("ViewContext");
+ this.setAttributes(root, {
+ version: this.VERSION,
+ id: (options && typeof options.id == "string") ?
+ options.id :
+ OpenLayers.Util.createUniqueID("OpenLayers_Context_")
+ });
+
+ // add schemaLocation attribute
+ this.setAttributeNS(
+ root, this.namespaces.xsi,
+ "xsi:schemaLocation", this.schemaLocation
+ );
+
+ // required General element
+ root.appendChild(this.write_wmc_General(context));
+
+ // required LayerList element
+ root.appendChild(this.write_wmc_LayerList(context));
+
+ return OpenLayers.Format.XML.prototype.write.apply(this, [root]);
+ },
+
+ /**
+ * Method: createElementDefaultNS
+ * Shorthand for createElementNS with namespace from <defaultPrefix>.
+ * Can optionally be used to set attributes and a text child value.
+ *
+ * Parameters:
+ * name - {String} The qualified node name.
+ * childValue - {String} Optional value for text child node.
+ * attributes - {Object} Optional object representing attributes.
+ *
+ * Returns:
+ * {Element} An element node.
+ */
+ createElementDefaultNS: function(name, childValue, attributes) {
+ var node = this.createElementNS(
+ this.namespaces[this.defaultPrefix],
+ name
+ );
+ if(childValue) {
+ node.appendChild(this.createTextNode(childValue));
+ }
+ if(attributes) {
+ this.setAttributes(node, attributes);
+ }
+ return node;
+ },
+
+ /**
+ * Method: setAttributes
+ * Set multiple attributes given key value pairs from an object.
+ *
+ * Parameters:
+ * node - {Element} An element node.
+ * obj - {Object} An object whose properties represent attribute names and
+ * values represent attribute values.
+ */
+ setAttributes: function(node, obj) {
+ var value;
+ for(var name in obj) {
+ value = obj[name].toString();
+ if(value.match(/[A-Z]/)) {
+ // safari lowercases attributes with setAttribute
+ this.setAttributeNS(node, null, name, value);
+ } else {
+ node.setAttribute(name, value);
+ }
+ }
+ },
+
+ /**
+ * Method: write_wmc_General
+ * Create a General node given an context object.
+ *
+ * Parameters:
+ * context - {Object} Context object.
+ *
+ * Returns:
+ * {Element} A WMC General element node.
+ */
+ write_wmc_General: function(context) {
+ var node = this.createElementDefaultNS("General");
+
+ // optional Window element
+ if(context.size) {
+ node.appendChild(this.createElementDefaultNS(
+ "Window", null,
+ {
+ width: context.size.w,
+ height: context.size.h
+ }
+ ));
+ }
+
+ // required BoundingBox element
+ var bounds = context.bounds;
+ node.appendChild(this.createElementDefaultNS(
+ "BoundingBox", null,
+ {
+ minx: bounds.left.toPrecision(10),
+ miny: bounds.bottom.toPrecision(10),
+ maxx: bounds.right.toPrecision(10),
+ maxy: bounds.top.toPrecision(10),
+ SRS: context.projection
+ }
+ ));
+
+ // required Title element
+ node.appendChild(this.createElementDefaultNS(
+ "Title", context.title
+ ));
+
+ // OpenLayers specific map properties
+ node.appendChild(this.write_ol_MapExtension(context));
+
+ return node;
+ },
+
+ /**
+ * Method: write_ol_MapExtension
+ */
+ write_ol_MapExtension: function(context) {
+ var node = this.createElementDefaultNS("Extension");
+
+ var bounds = context.maxExtent;
+ if(bounds) {
+ var maxExtent = this.createElementNS(
+ this.namespaces.ol, "ol:maxExtent"
+ );
+ this.setAttributes(maxExtent, {
+ minx: bounds.left.toPrecision(10),
+ miny: bounds.bottom.toPrecision(10),
+ maxx: bounds.right.toPrecision(10),
+ maxy: bounds.top.toPrecision(10)
+ });
+ node.appendChild(maxExtent);
+ }
+
+ return node;
+ },
+
+ /**
+ * Method: write_wmc_LayerList
+ * Create a LayerList node given an context object.
+ *
+ * Parameters:
+ * context - {Object} Context object.
+ *
+ * Returns:
+ * {Element} A WMC LayerList element node.
+ */
+ write_wmc_LayerList: function(context) {
+ var list = this.createElementDefaultNS("LayerList");
+
+ var layer;
+ for(var i=0, len=context.layers.length; i<len; ++i) {
+ layer = context.layers[i];
+ if(layer instanceof OpenLayers.Layer.WMS) {
+ list.appendChild(this.write_wmc_Layer(layer));
+ }
+ }
+
+ return list;
+ },
+
+ /**
+ * Method: write_wmc_Layer
+ * Create a Layer node given a layer object.
+ *
+ * Parameters:
+ * layer - {<OpenLayers.Layer.WMS>} Layer object.
+ *
+ * Returns:
+ * {Element} A WMC Layer element node.
+ */
+ write_wmc_Layer: function(layer) {
+ var node = this.createElementDefaultNS(
+ "Layer", null, {
+ queryable: layer.queryable ? "1" : "0",
+ hidden: layer.visibility ? "0" : "1"
+ }
+ );
+
+ // required Server element
+ node.appendChild(this.write_wmc_Server(layer));
+
+ // required Name element
+ node.appendChild(this.createElementDefaultNS(
+ "Name", layer.params["LAYERS"]
+ ));
+
+ // required Title element
+ node.appendChild(this.createElementDefaultNS(
+ "Title", layer.name
+ ));
+
+ // optional MetadataURL element
+ if (layer.metadataURL) {
+ node.appendChild(this.write_wmc_MetadataURL(layer));
+ }
+
+ // optional FormatList element
+ node.appendChild(this.write_wmc_FormatList(layer));
+
+ // optional StyleList element
+ node.appendChild(this.write_wmc_StyleList(layer));
+
+ // OpenLayers specific properties go in an Extension element
+ node.appendChild(this.write_wmc_LayerExtension(layer));
+
+ return node;
+ },
+
+ /**
+ * Method: write_wmc_LayerExtension
+ * Add OpenLayers specific layer parameters to an Extension element.
+ *
+ * Parameters:
+ * layer - {<OpenLayers.Layer.WMS>} A WMS layer.
+ *
+ * Returns:
+ * {Element} A WMC Extension element (for a layer).
+ */
+ write_wmc_LayerExtension: function(layer) {
+ var node = this.createElementDefaultNS("Extension");
+
+ var bounds = layer.maxExtent;
+ var maxExtent = this.createElementNS(
+ this.namespaces.ol, "ol:maxExtent"
+ );
+ this.setAttributes(maxExtent, {
+ minx: bounds.left.toPrecision(10),
+ miny: bounds.bottom.toPrecision(10),
+ maxx: bounds.right.toPrecision(10),
+ maxy: bounds.top.toPrecision(10)
+ });
+ node.appendChild(maxExtent);
+
+ var param = layer.params["TRANSPARENT"];
+ if(param) {
+ var trans = this.createElementNS(
+ this.namespaces.ol, "ol:transparent"
+ );
+ trans.appendChild(this.createTextNode(param));
+ node.appendChild(trans);
+ }
+
+ var properties = [
+ "numZoomLevels", "units", "isBaseLayer",
+ "opacity", "displayInLayerSwitcher", "singleTile"
+ ];
+ var child;
+ for(var i=0, len=properties.length; i<len; ++i) {
+ child = this.createOLPropertyNode(layer, properties[i]);
+ if(child) {
+ node.appendChild(child);
+ }
+ }
+
+ return node;
+ },
+
+ /**
+ * Method: createOLPropertyNode
+ * Create a node representing an OpenLayers property. If the property is
+ * null or undefined, null will be returned.
+ *
+ * Parameters:
+ * object - {Object} An object.
+ * prop - {String} A property.
+ *
+ * Returns:
+ * {Element} A property node.
+ */
+ createOLPropertyNode: function(obj, prop) {
+ var node = null;
+ if(obj[prop] != null) {
+ node = this.createElementNS(this.namespaces.ol, "ol:" + prop);
+ node.appendChild(this.createTextNode(obj[prop].toString()));
+ }
+ return node;
+ },
+
+ /**
+ * Method: write_wmc_Server
+ * Create a Server node given a layer object.
+ *
+ * Parameters:
+ * layer - {<OpenLayers.Layer.WMS>} Layer object.
+ *
+ * Returns:
+ * {Element} A WMC Server element node.
+ */
+ write_wmc_Server: function(layer) {
+ var node = this.createElementDefaultNS("Server");
+ this.setAttributes(node, {
+ service: "OGC:WMS",
+ version: layer.params["VERSION"]
+ });
+
+ // required OnlineResource element
+ node.appendChild(this.write_wmc_OnlineResource(layer.url));
+
+ return node;
+ },
+
+ /**
+ * Method: write_wmc_MetadataURL
+ * Create a MetadataURL node given a layer object.
+ *
+ * Parameters:
+ * layer - {<OpenLayers.Layer.WMS>} Layer object.
+ *
+ * Returns:
+ * {Element} A WMC metadataURL element node.
+ */
+ write_wmc_MetadataURL: function(layer) {
+ var node = this.createElementDefaultNS("MetadataURL");
+
+ // required OnlineResource element
+ node.appendChild(this.write_wmc_OnlineResource(layer.metadataURL));
+
+ return node;
+ },
+
+ /**
+ * Method: write_wmc_FormatList
+ * Create a FormatList node given a layer.
+ *
+ * Parameters:
+ * layer - {<OpenLayers.Layer.WMS>} Layer object.
+ *
+ * Returns:
+ * {Element} A WMC FormatList element node.
+ */
+ write_wmc_FormatList: function(layer) {
+ var node = this.createElementDefaultNS("FormatList");
+ node.appendChild(this.createElementDefaultNS(
+ "Format", layer.params["FORMAT"], {current: "1"}
+ ));
+
+ return node;
+ },
+
+ /**
+ * Method: write_wmc_StyleList
+ * Create a StyleList node given a layer.
+ *
+ * Parameters:
+ * layer - {<OpenLayers.Layer.WMS>} Layer object.
+ *
+ * Returns:
+ * {Element} A WMC StyleList element node.
+ */
+ write_wmc_StyleList: function(layer) {
+ var node = this.createElementDefaultNS("StyleList");
+ var style = this.createElementDefaultNS(
+ "Style", null, {current: "1"}
+ );
+
+ // Style can come from one of three places (prioritized as below):
+ // 1) an SLD parameter
+ // 2) and SLD_BODY parameter
+ // 3) the STYLES parameter
+
+ if(layer.params["SLD"]) {
+ // create link from SLD parameter
+ var sld = this.createElementDefaultNS("SLD");
+ var link = this.write_wmc_OnlineResource(layer.params["SLD"]);
+ sld.appendChild(link);
+ style.appendChild(sld);
+ } else if(layer.params["SLD_BODY"]) {
+ // include sld fragment from SLD_BODY parameter
+ var sld = this.createElementDefaultNS("SLD");
+ var body = layer.params["SLD_BODY"];
+ // read in body as xml doc - assume proper namespace declarations
+ var doc = OpenLayers.Format.XML.prototype.read.apply(this, [body]);
+ // append to StyledLayerDescriptor node
+ var imported = doc.documentElement;
+ if(sld.ownerDocument && sld.ownerDocument.importNode) {
+ imported = sld.ownerDocument.importNode(imported, true);
+ }
+ sld.appendChild(imported);
+ style.appendChild(sld);
+ } else {
+ // use name(s) from STYLES parameter
+ var name = layer.params["STYLES"] ?
+ layer.params["STYLES"] : this.defaultStyleName;
+
+ style.appendChild(this.createElementDefaultNS("Name", name));
+ style.appendChild(this.createElementDefaultNS(
+ "Title", this.defaultStyleTitle
+ ));
+ }
+ node.appendChild(style);
+ return node;
+ },
+
+ /**
+ * Method: write_wmc_OnlineResource
+ * Create an OnlineResource node given a URL.
+ *
+ * Parameters:
+ * href - {String} URL for the resource.
+ *
+ * Returns:
+ * {Element} A WMC OnlineResource element node.
+ */
+ write_wmc_OnlineResource: function(href) {
+ var node = this.createElementDefaultNS("OnlineResource");
+ this.setAttributeNS(node, this.namespaces.xlink, "xlink:type", "simple");
+ this.setAttributeNS(node, this.namespaces.xlink, "xlink:href", href);
+ return node;
+ },
+
+ CLASS_NAME: "OpenLayers.Format.WMC.v1"
+
+});
+/* ======================================================================
OpenLayers/Handler/Click.js
====================================================================== */
@@ -11436,6 +23680,13 @@
down: null,
/**
+ * Property: rightclickTimerId
+ * {Number} The id of the right mouse timeout waiting to clear the
+ * <delayedEvent>.
+ */
+ rightclickTimerId: null,
+
+ /**
* Constructor: OpenLayers.Handler.Click
* Create a new click handler.
*
@@ -11471,8 +23722,80 @@
* {Boolean} Continue propagating this event.
*/
mousedown: null,
+
+ /**
+ * Method: mouseup
+ * Handle mouseup. Installed to support collection of right mouse events.
+ *
+ * Returns:
+ * {Boolean} Continue propagating this event.
+ */
+ mouseup: function (evt) {
+ var propagate = true;
+
+ // Collect right mouse clicks from the mouseup
+ // IE - ignores the second right click in mousedown so using
+ // mouseup instead
+ if (this.checkModifiers(evt) &&
+ this.control.handleRightClicks &&
+ OpenLayers.Event.isRightClick(evt)) {
+ propogate = this.rightclick(evt);
+ }
+
+ return propagate;
+ },
/**
+ * Method: rightclick
+ * Handle rightclick. For a dblrightclick, we get two clicks so we need
+ * to always register for dblrightclick to properly handle single
+ * clicks.
+ *
+ * Returns:
+ * {Boolean} Continue propagating this event.
+ */
+ rightclick: function(evt) {
+ if(this.passesTolerance(evt)) {
+ if(this.rightclickTimerId != null) {
+ //Second click received before timeout this must be
+ // a double click
+ this.clearTimer();
+ this.callback('dblrightclick', [evt]);
+ return !this.stopDouble;
+ } else {
+ //Set the rightclickTimerId, send evt only if double is
+ // true else trigger single
+ var clickEvent = this['double'] ?
+ OpenLayers.Util.extend({}, evt) :
+ this.callback('rightclick', [evt]);
+
+ var delayedRightCall = OpenLayers.Function.bind(
+ this.delayedRightCall,
+ this,
+ clickEvent
+ );
+ this.rightclickTimerId = window.setTimeout(
+ delayedRightCall, this.delay
+ );
+ }
+ }
+ return !this.stopSingle;
+ },
+
+ /**
+ * Method: delayedRightCall
+ * Sets <rightclickTimerId> to null. And optionally triggers the
+ * rightclick callback if evt is set.
+ */
+ delayedRightCall: function(evt) {
+ this.rightclickTimerId = null;
+ if (evt) {
+ this.callback('rightclick', [evt]);
+ }
+ return !this.stopSingle;
+ },
+
+ /**
* Method: dblclick
* Handle dblclick. For a dblclick, we get two clicks in some browsers
* (FF) and one in others (IE). So we need to always register for
@@ -11657,6 +23980,23 @@
* {Function}
*/
oldOnselectstart: null,
+
+ /**
+ * Property: interval
+ * {Integer} In order to increase performance, an interval (in
+ * milliseconds) can be set to reduce the number of drag events
+ * called. If set, a new drag event will not be set until the
+ * interval has passed.
+ * Defaults to 0, meaning no interval.
+ */
+ interval: 0,
+
+ /**
+ * Property: timeoutId
+ * {String} The id of the timeout used for the mousedown interval.
+ * This is "private", and should be left alone.
+ */
+ timeoutId: null,
/**
* Constructor: OpenLayers.Handler.Drag
@@ -11782,20 +24122,29 @@
* {Boolean} Let the event propagate.
*/
mousemove: function (evt) {
- if (this.started) {
- if(evt.xy.x != this.last.x || evt.xy.y != this.last.y) {
- this.dragging = true;
- this.move(evt);
- this.callback("move", [evt.xy]);
- if(!this.oldOnselectstart) {
- this.oldOnselectstart = document.onselectstart;
- document.onselectstart = function() {return false;};
- }
- this.last = evt.xy;
+ if (this.started && !this.timeoutId && (evt.xy.x != this.last.x || evt.xy.y != this.last.y)) {
+ if (this.interval > 0) {
+ this.timeoutId = setTimeout(OpenLayers.Function.bind(this.removeTimeout, this), this.interval);
}
+ this.dragging = true;
+ this.move(evt);
+ this.callback("move", [evt.xy]);
+ if(!this.oldOnselectstart) {
+ this.oldOnselectstart = document.onselectstart;
+ document.onselectstart = function() {return false;};
+ }
+ this.last = this.evt.xy;
}
return true;
},
+
+ /**
+ * Method: removeTimeout
+ * Private. Called by mousemove() to remove the drag timeout.
+ */
+ removeTimeout: function() {
+ this.timeoutId = null;
+ },
/**
* Method: mouseup
@@ -11908,6 +24257,680 @@
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) {
+ 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(previouslyIn || (click && this.lastFeature)) {
+ 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/Keyboard.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
+ * @requires OpenLayers/Events.js
+ */
+
+/**
+ * Class: OpenLayers.handler.Keyboard
+ * A handler for keyboard events. Create a new instance with the
+ * <OpenLayers.Handler.Keyboard> constructor.
+ *
+ * Inherits from:
+ * - <OpenLayers.Handler>
+ */
+OpenLayers.Handler.Keyboard = OpenLayers.Class(OpenLayers.Handler, {
+
+ /* http://www.quirksmode.org/js/keys.html explains key x-browser
+ key handling quirks in pretty nice detail */
+
+ /**
+ * Constant: KEY_EVENTS
+ * keydown, keypress, keyup
+ */
+ KEY_EVENTS: ["keydown", "keyup"],
+
+ /**
+ * Property: eventListener
+ * {Function}
+ */
+ eventListener: null,
+
+ /**
+ * Constructor: OpenLayers.Handler.Keyboard
+ * Returns a new keyboard handler.
+ *
+ * Parameters:
+ * control - {<OpenLayers.Control>} The control that is making use of
+ * this handler. If a handler is being used without a control, the
+ * handlers setMap method must be overridden to deal properly with
+ * the map.
+ * callbacks - {Object} An object containing a single function to be
+ * called when the drag operation is finished. The callback should
+ * expect to recieve a single argument, the pixel location of the event.
+ * Callbacks for 'keydown', 'keypress', and 'keyup' are supported.
+ * options - {Object} Optional object whose properties will be set on the
+ * handler.
+ */
+ initialize: function(control, callbacks, options) {
+ OpenLayers.Handler.prototype.initialize.apply(this, arguments);
+ // cache the bound event listener method so it can be unobserved later
+ this.eventListener = OpenLayers.Function.bindAsEventListener(
+ this.handleKeyEvent, this
+ );
+ },
+
+ /**
+ * Method: destroy
+ */
+ destroy: function() {
+ this.deactivate();
+ this.eventListener = null;
+ OpenLayers.Handler.prototype.destroy.apply(this, arguments);
+ },
+
+ /**
+ * Method: activate
+ */
+ activate: function() {
+ if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
+ for (var i=0, len=this.KEY_EVENTS.length; i<len; i++) {
+ OpenLayers.Event.observe(
+ document, this.KEY_EVENTS[i], this.eventListener);
+ }
+ return true;
+ } else {
+ return false;
+ }
+ },
+
+ /**
+ * Method: deactivate
+ */
+ deactivate: function() {
+ var deactivated = false;
+ if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
+ for (var i=0, len=this.KEY_EVENTS.length; i<len; i++) {
+ OpenLayers.Event.stopObserving(
+ document, this.KEY_EVENTS[i], this.eventListener);
+ }
+ deactivated = true;
+ }
+ return deactivated;
+ },
+
+ /**
+ * Method: handleKeyEvent
+ */
+ handleKeyEvent: function (evt) {
+ if (this.checkModifiers(evt)) {
+ this.callback(evt.type, [evt]);
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Handler.Keyboard"
+});
+/* ======================================================================
OpenLayers/Handler/MouseWheel.js
====================================================================== */
@@ -12016,7 +25039,7 @@
}
if (!overLayerDiv) {
- for(var i=0; i < this.map.layers.length; i++) {
+ for(var i=0, len=this.map.layers.length; i<len; i++) {
// Are we in the layer div? Note that we have two cases
// here: one is to catch EventPane layers, which have a
// pane above the layer (layer.pane)
@@ -12197,6 +25220,23 @@
opacity: null,
/**
+ * APIProperty: alwaysInRange
+ * {Boolean} If a layer's display should not be scale-based, this should
+ * be set to true. This will cause the layer, as an overlay, to always
+ * be 'active', by always returning true from the calculateInRange()
+ * function.
+ *
+ * If not explicitly specified for a layer, its value will be
+ * determined on startup in initResolutions() based on whether or not
+ * any scale-specific properties have been set as options on the
+ * layer. If no scale-specific options have been set on the layer, we
+ * assume that it should always be in range.
+ *
+ * See #987 for more info.
+ */
+ alwaysInRange: null,
+
+ /**
* Constant: EVENT_TYPES
* {Array(String)} Supported application event types. Register a listener
* for a particular event with the following syntax:
@@ -12216,8 +25256,12 @@
* - *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
+ * argument has a zoomChanged boolean property which tells that the
+ * zoom has changed.
*/
- EVENT_TYPES: ["loadstart", "loadend", "loadcancel", "visibilitychanged"],
+ EVENT_TYPES: ["loadstart", "loadend", "loadcancel", "visibilitychanged",
+ "moveend"],
/**
* APIProperty: events
@@ -12774,14 +25818,20 @@
*
* Returns:
* {Boolean} The layer is displayable at the current map's current
- * resolution.
+ * resolution. Note that if 'alwaysInRange' is true for the layer,
+ * this function will always return true.
*/
calculateInRange: function() {
var inRange = false;
- if (this.map) {
- var resolution = this.map.getResolution();
- inRange = ( (resolution >= this.minResolution) &&
- (resolution <= this.maxResolution) );
+
+ if (this.alwaysInRange) {
+ inRange = true;
+ } else {
+ if (this.map) {
+ var resolution = this.map.getResolution();
+ inRange = ( (resolution >= this.minResolution) &&
+ (resolution <= this.maxResolution) );
+ }
}
return inRange;
},
@@ -12836,27 +25886,60 @@
'numZoomLevels', 'maxZoomLevel'
);
+ //these are the properties which do *not* imply that user wishes
+ // this layer to be scale-dependant
+ var notScaleProps = ['projection', 'units'];
+
+ //should the layer be scale-dependant? default is false -- this will
+ // only be set true if we find that the user has specified a property
+ // from the 'props' array that is not in 'notScaleProps'
+ var useInRange = false;
+
// First we create a new object where we will store all of the
// resolution-related properties that we find in either the layer's
// 'options' array or from the map.
//
var confProps = {};
- for(var i=0; i < props.length; i++) {
+ for(var i=0, len=props.length; i<len; i++) {
var property = props[i];
+
+ // If the layer had one of these properties set *and* it is
+ // a scale property (is not a non-scale property), then we assume
+ // the user did intend to use scale-dependant display (useInRange).
+ if (this.options[property] &&
+ OpenLayers.Util.indexOf(notScaleProps, property) == -1) {
+ useInRange = true;
+ }
+
confProps[property] = this.options[property] || this.map[property];
}
- // Do not use the scales/resolutions at the map level if
- // minScale/minResolution and maxScale/maxResolution are
- // specified at the layer level
- if (this.options.minScale != null &&
- this.options.maxScale != null &&
+ //only automatically set 'alwaysInRange' if the user hasn't already
+ // set it (to true or false, since the default is null). If user did
+ // not intend to use scale-dependant display then we set they layer
+ // as alwaysInRange. This means calculateInRange() will always return
+ // true and the layer will never be turned off due to scale changes.
+ //
+ if (this.alwaysInRange == null) {
+ this.alwaysInRange = !useInRange;
+ }
+
+ // Do not use the scales array set at the map level if
+ // either minScale or maxScale or both are set at the
+ // layer level
+ if ((this.options.minScale != null ||
+ this.options.maxScale != null) &&
this.options.scales == null) {
+
confProps.scales = null;
}
- if (this.options.minResolution != null &&
- this.options.maxResolution != null &&
+ // Do not use the resolutions array set at the map level if
+ // either minResolution or maxResolution or both are set at the
+ // layer level
+ if ((this.options.minResolution != null ||
+ this.options.maxResolution != null) &&
this.options.resolutions == null) {
+
confProps.resolutions = null;
}
@@ -12874,7 +25957,7 @@
//preset levels
if (confProps.scales != null) {
confProps.resolutions = [];
- for(var i = 0; i < confProps.scales.length; i++) {
+ for(var i=0, len=confProps.scales.length; i<len; i++) {
var scale = confProps.scales[i];
confProps.resolutions[i] =
OpenLayers.Util.getResolutionFromScale(scale,
@@ -12960,7 +26043,7 @@
this.minResolution = confProps.resolutions[lastIndex];
this.scales = [];
- for(var i = 0; i < confProps.resolutions.length; i++) {
+ for(var i=0, len=confProps.resolutions.length; i<len; i++) {
this.scales[i] =
OpenLayers.Util.getScaleFromResolution(confProps.resolutions[i],
confProps.units);
@@ -13083,7 +26166,7 @@
var highRes = this.resolutions[lowZoom];
var lowRes = this.resolutions[highZoom];
var res;
- for(var i=0; i<this.resolutions.length; ++i) {
+ for(var i=0, len=this.resolutions.length; i<len; ++i) {
res = this.resolutions[i];
if(res >= resolution) {
highRes = res;
@@ -13104,7 +26187,7 @@
} else {
var diff;
var minDiff = Number.POSITIVE_INFINITY;
- for(var i=0; i < this.resolutions.length; i++) {
+ for(var i=0, len=this.resolutions.length; i<len; i++) {
if (closest) {
diff = Math.abs(this.resolutions[i] - resolution);
if (diff > minDiff) {
@@ -13189,7 +26272,7 @@
setOpacity: function(opacity) {
if (opacity != this.opacity) {
this.opacity = opacity;
- for(var i=0; i<this.div.childNodes.length; ++i) {
+ for(var i=0, len=this.div.childNodes.length; i<len; ++i) {
var element = this.div.childNodes[i].firstChild;
OpenLayers.Util.modifyDOMElement(element, null, null, null,
null, null, null, opacity);
@@ -13198,6 +26281,16 @@
},
/**
+ * Method: getZIndex
+ *
+ * Returns:
+ * {Integer} the z-index of this layer
+ */
+ getZIndex: function () {
+ return this.div.style.zIndex;
+ },
+
+ /**
* Method: setZIndex
*
* Parameters:
@@ -13367,6 +26460,542 @@
});
/* ======================================================================
+ OpenLayers/Popup/FramedCloud.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/Popup/Framed.js
+ * @requires OpenLayers/Util.js
+ */
+
+/**
+ * Class: OpenLayers.Popup.FramedCloud
+ *
+ * Inherits from:
+ * - <OpenLayers.Popup.Framed>
+ */
+OpenLayers.Popup.FramedCloud =
+ OpenLayers.Class(OpenLayers.Popup.Framed, {
+
+ /**
+ * Property: contentDisplayClass
+ * {String} The CSS class of the popup content div.
+ */
+ contentDisplayClass: "olFramedCloudPopupContent",
+
+ /**
+ * APIProperty: autoSize
+ * {Boolean} Framed Cloud is autosizing by default.
+ */
+ autoSize: true,
+
+ /**
+ * APIProperty: panMapIfOutOfView
+ * {Boolean} Framed Cloud does pan into view by default.
+ */
+ panMapIfOutOfView: true,
+
+ /**
+ * APIProperty: imageSize
+ * {<OpenLayers.Size>}
+ */
+ imageSize: new OpenLayers.Size(676, 736),
+
+ /**
+ * APIProperty: isAlphaImage
+ * {Boolean} The FramedCloud does not use an alpha image (in honor of the
+ * good ie6 folk out there)
+ */
+ isAlphaImage: false,
+
+ /**
+ * APIProperty: fixedRelativePosition
+ * {Boolean} The Framed Cloud popup works in just one fixed position.
+ */
+ fixedRelativePosition: false,
+
+ /**
+ * Property: positionBlocks
+ * {Object} Hash of differen position blocks, keyed by relativePosition
+ * two-character code string (ie "tl", "tr", "bl", "br")
+ */
+ positionBlocks: {
+ "tl": {
+ 'offset': new OpenLayers.Pixel(44, 0),
+ 'padding': new OpenLayers.Bounds(8, 40, 8, 9),
+ 'blocks': [
+ { // top-left
+ size: new OpenLayers.Size('auto', 'auto'),
+ anchor: new OpenLayers.Bounds(0, 51, 22, 0),
+ position: new OpenLayers.Pixel(0, 0)
+ },
+ { //top-right
+ size: new OpenLayers.Size(22, 'auto'),
+ anchor: new OpenLayers.Bounds(null, 50, 0, 0),
+ position: new OpenLayers.Pixel(-638, 0)
+ },
+ { //bottom-left
+ size: new OpenLayers.Size('auto', 21),
+ anchor: new OpenLayers.Bounds(0, 32, 80, null),
+ position: new OpenLayers.Pixel(0, -629)
+ },
+ { //bottom-right
+ size: new OpenLayers.Size(22, 21),
+ anchor: new OpenLayers.Bounds(null, 32, 0, null),
+ position: new OpenLayers.Pixel(-638, -629)
+ },
+ { // stem
+ size: new OpenLayers.Size(81, 54),
+ anchor: new OpenLayers.Bounds(null, 0, 0, null),
+ position: new OpenLayers.Pixel(0, -668)
+ }
+ ]
+ },
+ "tr": {
+ 'offset': new OpenLayers.Pixel(-45, 0),
+ 'padding': new OpenLayers.Bounds(8, 40, 8, 9),
+ 'blocks': [
+ { // top-left
+ size: new OpenLayers.Size('auto', 'auto'),
+ anchor: new OpenLayers.Bounds(0, 51, 22, 0),
+ position: new OpenLayers.Pixel(0, 0)
+ },
+ { //top-right
+ size: new OpenLayers.Size(22, 'auto'),
+ anchor: new OpenLayers.Bounds(null, 50, 0, 0),
+ position: new OpenLayers.Pixel(-638, 0)
+ },
+ { //bottom-left
+ size: new OpenLayers.Size('auto', 21),
+ anchor: new OpenLayers.Bounds(0, 32, 22, null),
+ position: new OpenLayers.Pixel(0, -629)
+ },
+ { //bottom-right
+ size: new OpenLayers.Size(22, 21),
+ anchor: new OpenLayers.Bounds(null, 32, 0, null),
+ position: new OpenLayers.Pixel(-638, -629)
+ },
+ { // stem
+ size: new OpenLayers.Size(81, 54),
+ anchor: new OpenLayers.Bounds(0, 0, null, null),
+ position: new OpenLayers.Pixel(-215, -668)
+ }
+ ]
+ },
+ "bl": {
+ 'offset': new OpenLayers.Pixel(45, 0),
+ 'padding': new OpenLayers.Bounds(8, 9, 8, 40),
+ 'blocks': [
+ { // top-left
+ size: new OpenLayers.Size('auto', 'auto'),
+ anchor: new OpenLayers.Bounds(0, 21, 22, 32),
+ position: new OpenLayers.Pixel(0, 0)
+ },
+ { //top-right
+ size: new OpenLayers.Size(22, 'auto'),
+ anchor: new OpenLayers.Bounds(null, 21, 0, 32),
+ position: new OpenLayers.Pixel(-638, 0)
+ },
+ { //bottom-left
+ size: new OpenLayers.Size('auto', 21),
+ anchor: new OpenLayers.Bounds(0, 0, 22, null),
+ position: new OpenLayers.Pixel(0, -629)
+ },
+ { //bottom-right
+ size: new OpenLayers.Size(22, 21),
+ anchor: new OpenLayers.Bounds(null, 0, 0, null),
+ position: new OpenLayers.Pixel(-638, -629)
+ },
+ { // stem
+ size: new OpenLayers.Size(81, 54),
+ anchor: new OpenLayers.Bounds(null, null, 0, 0),
+ position: new OpenLayers.Pixel(-101, -674)
+ }
+ ]
+ },
+ "br": {
+ 'offset': new OpenLayers.Pixel(-44, 0),
+ 'padding': new OpenLayers.Bounds(8, 9, 8, 40),
+ 'blocks': [
+ { // top-left
+ size: new OpenLayers.Size('auto', 'auto'),
+ anchor: new OpenLayers.Bounds(0, 21, 22, 32),
+ position: new OpenLayers.Pixel(0, 0)
+ },
+ { //top-right
+ size: new OpenLayers.Size(22, 'auto'),
+ anchor: new OpenLayers.Bounds(null, 21, 0, 32),
+ position: new OpenLayers.Pixel(-638, 0)
+ },
+ { //bottom-left
+ size: new OpenLayers.Size('auto', 21),
+ anchor: new OpenLayers.Bounds(0, 0, 22, null),
+ position: new OpenLayers.Pixel(0, -629)
+ },
+ { //bottom-right
+ size: new OpenLayers.Size(22, 21),
+ anchor: new OpenLayers.Bounds(null, 0, 0, null),
+ position: new OpenLayers.Pixel(-638, -629)
+ },
+ { // stem
+ size: new OpenLayers.Size(81, 54),
+ anchor: new OpenLayers.Bounds(0, null, null, 0),
+ position: new OpenLayers.Pixel(-311, -674)
+ }
+ ]
+ }
+ },
+
+ /**
+ * APIProperty: minSize
+ * {<OpenLayers.Size>}
+ */
+ minSize: new OpenLayers.Size(105, 10),
+
+ /**
+ * APIProperty: maxSize
+ * {<OpenLayers.Size>}
+ */
+ maxSize: new OpenLayers.Size(600, 660),
+
+ /**
+ * Constructor: OpenLayers.Popup.FramedCloud
+ *
+ * Parameters:
+ * id - {String}
+ * lonlat - {<OpenLayers.LonLat>}
+ * contentSize - {<OpenLayers.Size>}
+ * contentHTML - {String}
+ * anchor - {Object} Object to which we'll anchor the popup. Must expose
+ * a 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>)
+ * (Note that this is generally an <OpenLayers.Icon>).
+ * closeBox - {Boolean}
+ * closeBoxCallback - {Function} Function to be called on closeBox click.
+ */
+ initialize:function(id, lonlat, contentSize, contentHTML, anchor, closeBox,
+ closeBoxCallback) {
+
+ this.imageSrc = OpenLayers.Util.getImagesLocation() + 'cloud-popup-relative.png';
+ OpenLayers.Popup.Framed.prototype.initialize.apply(this, arguments);
+ this.contentDiv.className = this.contentDisplayClass;
+ },
+
+ /**
+ * APIMethod: destroy
+ */
+ destroy: function() {
+ OpenLayers.Popup.Framed.prototype.destroy.apply(this, arguments);
+ },
+
+ CLASS_NAME: "OpenLayers.Popup.FramedCloud"
+});
+/* ======================================================================
+ OpenLayers/Control/DragFeature.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/Handler/Drag.js
+ * @requires OpenLayers/Handler/Feature.js
+ */
+
+/**
+ * Class: OpenLayers.Control.DragFeature
+ * Move a feature with a drag. Create a new control with the
+ * <OpenLayers.Control.DragFeature> constructor.
+ *
+ * Inherits From:
+ * - <OpenLayers.Control>
+ */
+OpenLayers.Control.DragFeature = OpenLayers.Class(OpenLayers.Control, {
+
+ /**
+ * APIProperty: geometryTypes
+ * {Array(String)} To restrict dragging to a limited set of geometry types,
+ * send a list of strings corresponding to the geometry class names.
+ */
+ geometryTypes: null,
+
+ /**
+ * APIProperty: onStart
+ * {Function} Define this function if you want to know when a drag starts.
+ * The function should expect to receive two arguments: the feature
+ * that is about to be dragged and the pixel location of the mouse.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>} The feature that is about to be
+ * dragged.
+ * pixel - {<OpenLayers.Pixel>} The pixel location of the mouse.
+ */
+ onStart: function(feature, pixel) {},
+
+ /**
+ * APIProperty: onDrag
+ * {Function} Define this function if you want to know about each move of a
+ * feature. The function should expect to receive two arguments: the
+ * feature that is being dragged and the pixel location of the mouse.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>} The feature that was dragged.
+ * pixel - {<OpenLayers.Pixel>} The pixel location of the mouse.
+ */
+ onDrag: function(feature, pixel) {},
+
+ /**
+ * APIProperty: onComplete
+ * {Function} Define this function if you want to know when a feature is
+ * done dragging. The function should expect to receive two arguments:
+ * the feature that is being dragged and the pixel location of the
+ * mouse.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>} The feature that was dragged.
+ * pixel - {<OpenLayers.Pixel>} The pixel location of the mouse.
+ */
+ onComplete: function(feature, pixel) {},
+
+ /**
+ * Property: layer
+ * {<OpenLayers.Layer.Vector>}
+ */
+ layer: null,
+
+ /**
+ * Property: feature
+ * {<OpenLayers.Feature.Vector>}
+ */
+ feature: null,
+
+ /**
+ * Property: dragCallbacks
+ * {Object} The functions that are sent to the drag handler for callback.
+ */
+ dragCallbacks: {},
+
+ /**
+ * Property: featureCallbacks
+ * {Object} The functions that are sent to the feature handler for callback.
+ */
+ featureCallbacks: {},
+
+ /**
+ * Property: lastPixel
+ * {<OpenLayers.Pixel>}
+ */
+ lastPixel: null,
+
+ /**
+ * Constructor: OpenLayers.Control.DragFeature
+ * Create a new control to drag features.
+ *
+ * Parameters:
+ * layer - {<OpenLayers.Layer.Vector>} The layer containing features to be
+ * dragged.
+ * options - {Object} Optional object whose properties will be set on the
+ * control.
+ */
+ initialize: function(layer, options) {
+ OpenLayers.Control.prototype.initialize.apply(this, [options]);
+ this.layer = layer;
+ this.handlers = {
+ drag: new OpenLayers.Handler.Drag(
+ this, OpenLayers.Util.extend({
+ down: this.downFeature,
+ move: this.moveFeature,
+ up: this.upFeature,
+ out: this.cancel,
+ done: this.doneDragging
+ }, this.dragCallbacks)
+ ),
+ feature: new OpenLayers.Handler.Feature(
+ this, this.layer, OpenLayers.Util.extend({
+ over: this.overFeature,
+ out: this.outFeature
+ }, this.featureCallbacks),
+ {geometryTypes: this.geometryTypes}
+ )
+ };
+ },
+
+ /**
+ * APIMethod: destroy
+ * Take care of things that are not handled in superclass
+ */
+ destroy: function() {
+ this.layer = null;
+ OpenLayers.Control.prototype.destroy.apply(this, []);
+ },
+
+ /**
+ * APIMethod: activate
+ * Activate the control and the feature handler.
+ *
+ * Returns:
+ * {Boolean} Successfully activated the control and feature handler.
+ */
+ activate: function() {
+ return (this.handlers.feature.activate() &&
+ OpenLayers.Control.prototype.activate.apply(this, arguments));
+ },
+
+ /**
+ * APIMethod: deactivate
+ * Deactivate the control and all handlers.
+ *
+ * Returns:
+ * {Boolean} Successfully deactivated the control.
+ */
+ deactivate: function() {
+ // the return from the handlers is unimportant in this case
+ this.handlers.drag.deactivate();
+ this.handlers.feature.deactivate();
+ this.feature = null;
+ this.dragging = false;
+ this.lastPixel = null;
+ return OpenLayers.Control.prototype.deactivate.apply(this, arguments);
+ },
+
+ /**
+ * Method: overFeature
+ * Called when the feature handler detects a mouse-over on a feature.
+ * This activates the drag handler.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>} The selected feature.
+ */
+ overFeature: function(feature) {
+ if(!this.handlers.drag.dragging) {
+ this.feature = feature;
+ this.handlers.drag.activate();
+ this.over = true;
+ // TBD replace with CSS classes
+ this.map.div.style.cursor = "move";
+ } else {
+ if(this.feature.id == feature.id) {
+ this.over = true;
+ } else {
+ this.over = false;
+ }
+ }
+ },
+
+ /**
+ * Method: downFeature
+ * Called when the drag handler detects a mouse-down.
+ *
+ * Parameters:
+ * pixel - {<OpenLayers.Pixel>} Location of the mouse event.
+ */
+ downFeature: function(pixel) {
+ this.lastPixel = pixel;
+ this.onStart(this.feature, pixel);
+ },
+
+ /**
+ * Method: moveFeature
+ * Called when the drag handler detects a mouse-move. Also calls the
+ * optional onDrag method.
+ *
+ * Parameters:
+ * pixel - {<OpenLayers.Pixel>} Location of the mouse event.
+ */
+ moveFeature: function(pixel) {
+ var res = this.map.getResolution();
+ this.feature.geometry.move(res * (pixel.x - this.lastPixel.x),
+ res * (this.lastPixel.y - pixel.y));
+ this.layer.drawFeature(this.feature);
+ this.lastPixel = pixel;
+ this.onDrag(this.feature, pixel);
+ },
+
+ /**
+ * Method: upFeature
+ * Called when the drag handler detects a mouse-up. Also calls the
+ * optional onComplete method.
+ *
+ * Parameters:
+ * pixel - {<OpenLayers.Pixel>} Location of the mouse event.
+ */
+ upFeature: function(pixel) {
+ if(!this.over) {
+ this.handlers.drag.deactivate();
+ this.feature = null;
+ // TBD replace with CSS classes
+ this.map.div.style.cursor = "default";
+ } else {
+ // the drag handler itself resetted the cursor, so
+ // set it back to "move" here
+ this.map.div.style.cursor = "move";
+ }
+ },
+
+ /**
+ * Method: doneDragging
+ * Called when the drag handler is done dragging.
+ *
+ * Parameters:
+ * pixel - {<OpenLayers.Pixel>} The last event pixel location. If this event
+ * came from a mouseout, this may not be in the map viewport.
+ */
+ doneDragging: function(pixel) {
+ this.onComplete(this.feature, pixel);
+ },
+
+ /**
+ * Method: outFeature
+ * Called when the feature handler detects a mouse-out on a feature.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>} The feature that the mouse left.
+ */
+ outFeature: function(feature) {
+ if(!this.handlers.drag.dragging) {
+ this.over = false;
+ this.handlers.drag.deactivate();
+ // TBD replace with CSS classes
+ this.map.div.style.cursor = "default";
+ this.feature = null;
+ } else {
+ if(this.feature.id == feature.id) {
+ this.over = false;
+ }
+ }
+ },
+
+ /**
+ * Method: cancel
+ * Called when the drag handler detects a mouse-out (from the map viewport).
+ */
+ cancel: function() {
+ this.handlers.drag.deactivate();
+ this.over = false;
+ },
+
+ /**
+ * Method: setMap
+ * Set the map property for the control and all handlers.
+ *
+ * Parameters:
+ * map - {<OpenLayers.Map>} The control's map.
+ */
+ setMap: function(map) {
+ this.handlers.drag.setMap(map);
+ this.handlers.feature.setMap(map);
+ OpenLayers.Control.prototype.setMap.apply(this, arguments);
+ },
+
+ CLASS_NAME: "OpenLayers.Control.DragFeature"
+});
+/* ======================================================================
OpenLayers/Control/DragPan.js
====================================================================== */
@@ -13401,13 +27030,26 @@
panned: false,
/**
+ * Property: interval
+ * {Integer} The number of milliseconds that should ellapse before
+ * panning the map again. Set this to increase dragging performance.
+ * Defaults to 25 milliseconds.
+ */
+ interval: 25,
+
+ /**
* Method: draw
* Creates a Drag handler, using <panMap> and
* <panMapDone> as callbacks.
*/
draw: function() {
- this.handler = new OpenLayers.Handler.Drag(this,
- {"move": this.panMap, "done": this.panMapDone});
+ this.handler = new OpenLayers.Handler.Drag(this, {
+ "move": this.panMap,
+ "done": this.panMapDone
+ }, {
+ interval: this.interval
+ }
+ );
},
/**
@@ -13443,6 +27085,777 @@
CLASS_NAME: "OpenLayers.Control.DragPan"
});
/* ======================================================================
+ OpenLayers/Control/KeyboardDefaults.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/Handler/Keyboard.js
+ */
+
+/**
+ * Class: OpenLayers.Control.KeyboardDefaults
+ *
+ * Inherits from:
+ * - <OpenLayers.Control>
+ */
+OpenLayers.Control.KeyboardDefaults = OpenLayers.Class(OpenLayers.Control, {
+
+ /**
+ * APIProperty: slideFactor
+ * Pixels to slide by.
+ */
+ slideFactor: 75,
+
+ /**
+ * Constructor: OpenLayers.Control.KeyboardDefaults
+ */
+ initialize: function() {
+ OpenLayers.Control.prototype.initialize.apply(this, arguments);
+ },
+
+ /**
+ * APIMethod: destroy
+ */
+ destroy: function() {
+ if (this.handler) {
+ this.handler.destroy();
+ }
+ this.handler = null;
+
+ OpenLayers.Control.prototype.destroy.apply(this, arguments);
+ },
+
+ /**
+ * Method: draw
+ * Create handler.
+ */
+ draw: function() {
+ this.handler = new OpenLayers.Handler.Keyboard( this, {
+ "keydown": this.defaultKeyPress });
+ this.activate();
+ },
+
+ /**
+ * Method: defaultKeyPress
+ * When handling the key event, we only use evt.keyCode. This holds
+ * some drawbacks, though we get around them below. When interpretting
+ * the keycodes below (including the comments associated with them),
+ * consult the URL below. For instance, the Safari browser returns
+ * "IE keycodes", and so is supported by any keycode labeled "IE".
+ *
+ * Very informative URL:
+ * http://unixpapa.com/js/key.html
+ *
+ * Parameters:
+ * code - {Integer}
+ */
+ defaultKeyPress: function (evt) {
+ switch(evt.keyCode) {
+ case OpenLayers.Event.KEY_LEFT:
+ this.map.pan(-this.slideFactor, 0);
+ break;
+ case OpenLayers.Event.KEY_RIGHT:
+ this.map.pan(this.slideFactor, 0);
+ break;
+ case OpenLayers.Event.KEY_UP:
+ this.map.pan(0, -this.slideFactor);
+ break;
+ case OpenLayers.Event.KEY_DOWN:
+ this.map.pan(0, this.slideFactor);
+ break;
+
+ case 33: // Page Up. Same in all browsers.
+ var size = this.map.getSize();
+ this.map.pan(0, -0.75*size.h);
+ break;
+ case 34: // Page Down. Same in all browsers.
+ var size = this.map.getSize();
+ this.map.pan(0, 0.75*size.h);
+ break;
+ case 35: // End. Same in all browsers.
+ var size = this.map.getSize();
+ this.map.pan(0.75*size.w, 0);
+ break;
+ case 36: // Home. Same in all browsers.
+ var size = this.map.getSize();
+ this.map.pan(-0.75*size.w, 0);
+ break;
+
+ case 43: // +/= (ASCII), keypad + (ASCII, Opera)
+ case 61: // +/= (Mozilla, Opera, some ASCII)
+ case 187: // +/= (IE)
+ case 107: // keypad + (IE, Mozilla)
+ this.map.zoomIn();
+ break;
+ case 45: // -/_ (ASCII, Opera), keypad - (ASCII, Opera)
+ case 109: // -/_ (Mozilla), keypad - (Mozilla, IE)
+ case 189: // -/_ (IE)
+ case 95: // -/_ (some ASCII)
+ this.map.zoomOut();
+ break;
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Control.KeyboardDefaults"
+});
+/* ======================================================================
+ OpenLayers/Feature/Vector.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. */
+
+// TRASH THIS
+OpenLayers.State = {
+ /** states */
+ UNKNOWN: 'Unknown',
+ INSERT: 'Insert',
+ UPDATE: 'Update',
+ DELETE: 'Delete'
+};
+
+/**
+ * @requires OpenLayers/Feature.js
+ * @requires OpenLayers/Util.js
+ */
+
+/**
+ * Class: OpenLayers.Feature.Vector
+ * Vector features use the OpenLayers.Geometry classes as geometry description.
+ * They have an 'attributes' property, which is the data object, and a 'style'
+ * property, the default values of which are defined in the
+ * <OpenLayers.Feature.Vector.style> objects.
+ *
+ * Inherits from:
+ * - <OpenLayers.Feature>
+ */
+OpenLayers.Feature.Vector = OpenLayers.Class(OpenLayers.Feature, {
+
+ /**
+ * Property: fid
+ * {String}
+ */
+ fid: null,
+
+ /**
+ * APIProperty: geometry
+ * {<OpenLayers.Geometry>}
+ */
+ geometry: null,
+
+ /**
+ * APIProperty: attributes
+ * {Object} This object holds arbitrary properties that describe the
+ * feature.
+ */
+ attributes: null,
+
+ /**
+ * Property: state
+ * {String}
+ */
+ state: null,
+
+ /**
+ * APIProperty: style
+ * {Object}
+ */
+ style: null,
+
+ /**
+ * Property: renderIntent
+ * {String} rendering intent currently being used
+ */
+ renderIntent: "default",
+
+ /**
+ * Constructor: OpenLayers.Feature.Vector
+ * Create a vector feature.
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry>} The geometry that this feature
+ * represents.
+ * attributes - {Object} An optional object that will be mapped to the
+ * <attributes> property.
+ * style - {Object} An optional style object.
+ */
+ initialize: function(geometry, attributes, style) {
+ OpenLayers.Feature.prototype.initialize.apply(this,
+ [null, null, attributes]);
+ this.lonlat = null;
+ this.geometry = geometry ? geometry : null;
+ this.state = null;
+ this.attributes = {};
+ if (attributes) {
+ this.attributes = OpenLayers.Util.extend(this.attributes,
+ attributes);
+ }
+ this.style = style ? style : null;
+ },
+
+ /**
+ * Method: destroy
+ * nullify references to prevent circular references and memory leaks
+ */
+ destroy: function() {
+ if (this.layer) {
+ this.layer.removeFeatures(this);
+ this.layer = null;
+ }
+
+ this.geometry = null;
+ OpenLayers.Feature.prototype.destroy.apply(this, arguments);
+ },
+
+ /**
+ * Method: clone
+ * Create a clone of this vector feature. Does not set any non-standard
+ * properties.
+ *
+ * Returns:
+ * {<OpenLayers.Feature.Vector>} An exact clone of this vector feature.
+ */
+ clone: function () {
+ return new OpenLayers.Feature.Vector(
+ this.geometry ? this.geometry.clone() : null,
+ this.attributes,
+ this.style);
+ },
+
+ /**
+ * Method: onScreen
+ * Determine whether the feature is within the map viewport. This method
+ * tests for an intersection between the geometry and the viewport
+ * bounds. If a more effecient but less precise geometry bounds
+ * intersection is desired, call the method with the boundsOnly
+ * parameter true.
+ *
+ * Parameters:
+ * boundsOnly - {Boolean} Only test whether a feature's bounds intersects
+ * the viewport bounds. Default is false. If false, the feature's
+ * geometry must intersect the viewport for onScreen to return true.
+ *
+ * Returns:
+ * {Boolean} The feature is currently visible on screen (optionally
+ * based on its bounds if boundsOnly is true).
+ */
+ onScreen:function(boundsOnly) {
+ var onScreen = false;
+ if(this.layer && this.layer.map) {
+ var screenBounds = this.layer.map.getExtent();
+ if(boundsOnly) {
+ var featureBounds = this.geometry.getBounds();
+ onScreen = screenBounds.intersectsBounds(featureBounds);
+ } else {
+ var screenPoly = screenBounds.toGeometry();
+ onScreen = screenPoly.intersects(this.geometry);
+ }
+ }
+ return onScreen;
+ },
+
+ /**
+ * Method: createMarker
+ * HACK - we need to decide if all vector features should be able to
+ * create markers
+ *
+ * Returns:
+ * {<OpenLayers.Marker>} For now just returns null
+ */
+ createMarker: function() {
+ return null;
+ },
+
+ /**
+ * Method: destroyMarker
+ * HACK - we need to decide if all vector features should be able to
+ * delete markers
+ *
+ * If user overrides the createMarker() function, s/he should be able
+ * to also specify an alternative function for destroying it
+ */
+ destroyMarker: function() {
+ // pass
+ },
+
+ /**
+ * Method: createPopup
+ * HACK - we need to decide if all vector features should be able to
+ * create popups
+ *
+ * Returns:
+ * {<OpenLayers.Popup>} For now just returns null
+ */
+ createPopup: function() {
+ return null;
+ },
+
+ /**
+ * Method: atPoint
+ * Determins whether the feature intersects with the specified location.
+ *
+ * Parameters:
+ * lonlat - {<OpenLayers.LonLat>}
+ * toleranceLon - {float} Optional tolerance in Geometric Coords
+ * toleranceLat - {float} Optional tolerance in Geographic Coords
+ *
+ * Returns:
+ * {Boolean} Whether or not the feature is at the specified location
+ */
+ atPoint: function(lonlat, toleranceLon, toleranceLat) {
+ var atPoint = false;
+ if(this.geometry) {
+ atPoint = this.geometry.atPoint(lonlat, toleranceLon,
+ toleranceLat);
+ }
+ return atPoint;
+ },
+
+ /**
+ * Method: destroyPopup
+ * HACK - we need to decide if all vector features should be able to
+ * delete popups
+ */
+ destroyPopup: function() {
+ // pass
+ },
+
+ /**
+ * Method: move
+ * Moves the feature and redraws it at its new location
+ *
+ * Parameters:
+ * state - {OpenLayers.LonLat or OpenLayers.Pixel} the
+ * location to which to move the feature.
+ */
+ move: function(location) {
+
+ if(!this.layer || !this.geometry.move){
+ //do nothing if no layer or immoveable geometry
+ return;
+ }
+
+ var pixel;
+ if (location.CLASS_NAME == "OpenLayers.LonLat") {
+ pixel = this.layer.getViewPortPxFromLonLat(location);
+ } else {
+ pixel = location;
+ }
+
+ var lastPixel = this.layer.getViewPortPxFromLonLat(this.geometry.getBounds().getCenterLonLat());
+ var res = this.layer.map.getResolution();
+ this.geometry.move(res * (pixel.x - lastPixel.x),
+ res * (lastPixel.y - pixel.y));
+ this.layer.drawFeature(this);
+ return lastPixel;
+ },
+
+ /**
+ * Method: toState
+ * Sets the new state
+ *
+ * Parameters:
+ * state - {String}
+ */
+ toState: function(state) {
+ if (state == OpenLayers.State.UPDATE) {
+ switch (this.state) {
+ case OpenLayers.State.UNKNOWN:
+ case OpenLayers.State.DELETE:
+ this.state = state;
+ break;
+ case OpenLayers.State.UPDATE:
+ case OpenLayers.State.INSERT:
+ break;
+ }
+ } else if (state == OpenLayers.State.INSERT) {
+ switch (this.state) {
+ case OpenLayers.State.UNKNOWN:
+ break;
+ default:
+ this.state = state;
+ break;
+ }
+ } else if (state == OpenLayers.State.DELETE) {
+ switch (this.state) {
+ case OpenLayers.State.INSERT:
+ // the feature should be destroyed
+ break;
+ case OpenLayers.State.DELETE:
+ break;
+ case OpenLayers.State.UNKNOWN:
+ case OpenLayers.State.UPDATE:
+ this.state = state;
+ break;
+ }
+ } else if (state == OpenLayers.State.UNKNOWN) {
+ this.state = state;
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Feature.Vector"
+});
+
+
+/**
+ * Constant: OpenLayers.Feature.Vector.style
+ * OpenLayers features can have a number of style attributes. The 'default'
+ * style will typically be used if no other style is specified.
+ *
+ * Default style properties:
+ *
+ * - fillColor: "#ee9900",
+ * - fillOpacity: 0.4,
+ * - hoverFillColor: "white",
+ * - hoverFillOpacity: 0.8,
+ * - strokeColor: "#ee9900",
+ * - strokeOpacity: 1,
+ * - strokeWidth: 1,
+ * - strokeLinecap: "round", [butt | round | square]
+ * - strokeDashstyle: "solid", [dot | dash | dashdot | longdash | longdashdot | solid]
+ * - hoverStrokeColor: "red",
+ * - hoverStrokeOpacity: 1,
+ * - hoverStrokeWidth: 0.2,
+ * - pointRadius: 6,
+ * - hoverPointRadius: 1,
+ * - hoverPointUnit: "%",
+ * - pointerEvents: "visiblePainted"
+ * - cursor: ""
+ *
+ * Other style properties that have no default values:
+ *
+ * - externalGraphic,
+ * - graphicWidth,
+ * - graphicHeight,
+ * - graphicOpacity,
+ * - graphicXOffset,
+ * - graphicYOffset,
+ * - graphicName,
+ * - display
+ */
+OpenLayers.Feature.Vector.style = {
+ 'default': {
+ fillColor: "#ee9900",
+ fillOpacity: 0.4,
+ hoverFillColor: "white",
+ hoverFillOpacity: 0.8,
+ strokeColor: "#ee9900",
+ strokeOpacity: 1,
+ strokeWidth: 1,
+ strokeLinecap: "round",
+ strokeDashstyle: "solid",
+ hoverStrokeColor: "red",
+ hoverStrokeOpacity: 1,
+ hoverStrokeWidth: 0.2,
+ pointRadius: 6,
+ hoverPointRadius: 1,
+ hoverPointUnit: "%",
+ pointerEvents: "visiblePainted",
+ cursor: "inherit"
+ },
+ 'select': {
+ fillColor: "blue",
+ fillOpacity: 0.4,
+ hoverFillColor: "white",
+ hoverFillOpacity: 0.8,
+ strokeColor: "blue",
+ strokeOpacity: 1,
+ strokeWidth: 2,
+ strokeLinecap: "round",
+ strokeDashstyle: "solid",
+ hoverStrokeColor: "red",
+ hoverStrokeOpacity: 1,
+ hoverStrokeWidth: 0.2,
+ pointRadius: 6,
+ hoverPointRadius: 1,
+ hoverPointUnit: "%",
+ pointerEvents: "visiblePainted",
+ cursor: "pointer"
+ },
+ 'temporary': {
+ fillColor: "yellow",
+ fillOpacity: 0.2,
+ hoverFillColor: "white",
+ hoverFillOpacity: 0.8,
+ strokeColor: "yellow",
+ strokeOpacity: 1,
+ strokeLinecap: "round",
+ strokeWidth: 4,
+ strokeDashstyle: "solid",
+ hoverStrokeColor: "red",
+ hoverStrokeOpacity: 1,
+ hoverStrokeWidth: 0.2,
+ pointRadius: 6,
+ hoverPointRadius: 1,
+ hoverPointUnit: "%",
+ pointerEvents: "visiblePainted",
+ cursor: "inherit"
+ }
+};
+/* ======================================================================
+ OpenLayers/Feature/WFS.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/Feature.js
+ */
+
+/**
+ * Class: OpenLayers.Feature.WFS
+ * WFS handling class, for use as a featureClass on the WFS layer for handling
+ * 'point' WFS types. Good for subclassing when creating a custom WFS like
+ * XML application.
+ *
+ * Inherits from:
+ * - <OpenLayers.Feature>
+ */
+OpenLayers.Feature.WFS = OpenLayers.Class(OpenLayers.Feature, {
+
+ /**
+ * Constructor: OpenLayers.Feature.WFS
+ * Create a WFS feature.
+ *
+ * Parameters:
+ * layer - {<OpenLayers.Layer>}
+ * xmlNode - {XMLNode}
+ */
+ initialize: function(layer, xmlNode) {
+ var newArguments = arguments;
+ var data = this.processXMLNode(xmlNode);
+ newArguments = new Array(layer, data.lonlat, data);
+ OpenLayers.Feature.prototype.initialize.apply(this, newArguments);
+ this.createMarker();
+ this.layer.addMarker(this.marker);
+ },
+
+ /**
+ * Method: destroy
+ * nullify references to prevent circular references and memory leaks
+ */
+ destroy: function() {
+ if (this.marker != null) {
+ this.layer.removeMarker(this.marker);
+ }
+ OpenLayers.Feature.prototype.destroy.apply(this, arguments);
+ },
+
+ /**
+ * Method: processXMLNode
+ * When passed an xmlNode, parses it for a GML point, and passes
+ * back an object describing that point.
+ *
+ * For subclasses of Feature.WFS, this is the feature to change.
+ *
+ * Parameters:
+ * xmlNode - {XMLNode}
+ *
+ * Returns:
+ * {Object} Data Object with 'id', 'lonlat', and private properties set
+ */
+ processXMLNode: function(xmlNode) {
+ //this should be overridden by subclasses
+ // must return an Object with 'id' and 'lonlat' values set
+ var point = OpenLayers.Ajax.getElementsByTagNameNS(xmlNode, "http://www.opengis.net/gml", "gml", "Point");
+ var text = OpenLayers.Util.getXmlNodeValue(OpenLayers.Ajax.getElementsByTagNameNS(point[0], "http://www.opengis.net/gml","gml", "coordinates")[0]);
+ var floats = text.split(",");
+ return {lonlat: new OpenLayers.LonLat(parseFloat(floats[0]),
+ parseFloat(floats[1])),
+ id: null};
+
+ },
+
+ CLASS_NAME: "OpenLayers.Feature.WFS"
+});
+
+
+
+
+
+/* ======================================================================
+ OpenLayers/Format/WMC/v1_0_0.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/Format/WMC/v1.js
+ */
+
+/**
+ * Class: OpenLayers.Format.WMC.v1_0_0
+ * Read and write WMC version 1.0.0.
+ *
+ * Inherits from:
+ * - <OpenLayers.Format.WMC.v1>
+ */
+OpenLayers.Format.WMC.v1_0_0 = OpenLayers.Class(
+ OpenLayers.Format.WMC.v1, {
+
+ /**
+ * Constant: VERSION
+ * {String} 1.0.0
+ */
+ VERSION: "1.0.0",
+
+ /**
+ * Property: schemaLocation
+ * {String} http://www.opengis.net/context
+ * http://schemas.opengis.net/context/1.0.0/context.xsd
+ */
+ schemaLocation: "http://www.opengis.net/context http://schemas.opengis.net/context/1.0.0/context.xsd",
+
+ /**
+ * Constructor: OpenLayers.Format.WMC.v1_0_0
+ * Instances of this class are not created directly. Use the
+ * <OpenLayers.Format.WMC> constructor instead.
+ *
+ * Parameters:
+ * options - {Object} An optional object whose properties will be set on
+ * this instance.
+ */
+ initialize: function(options) {
+ OpenLayers.Format.WMC.v1.prototype.initialize.apply(
+ this, [options]
+ );
+ },
+
+ CLASS_NAME: "OpenLayers.Format.WMC.v1_0_0"
+
+});
+/* ======================================================================
+ OpenLayers/Format/WMC/v1_1_0.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/Format/WMC/v1.js
+ */
+
+/**
+ * Class: OpenLayers.Format.WMC.v1_1_0
+ * Read and write WMC version 1.1.0.
+ *
+ * Differences between 1.1.0 and 1.0.0:
+ * - 1.1.0 Layers have optional sld:MinScaleDenominator and
+ * sld:MaxScaleDenominator
+ *
+ * Inherits from:
+ * - <OpenLayers.Format.WMC.v1>
+ */
+OpenLayers.Format.WMC.v1_1_0 = OpenLayers.Class(
+ OpenLayers.Format.WMC.v1, {
+
+ /**
+ * Constant: VERSION
+ * {String} 1.1.0
+ */
+ VERSION: "1.1.0",
+
+ /**
+ * Property: schemaLocation
+ * {String} http://www.opengis.net/context
+ * http://schemas.opengis.net/context/1.1.0/context.xsd
+ */
+ schemaLocation: "http://www.opengis.net/context http://schemas.opengis.net/context/1.1.0/context.xsd",
+
+ /**
+ * Constructor: OpenLayers.Format.WMC.v1_1_0
+ * Instances of this class are not created directly. Use the
+ * <OpenLayers.Format.WMC> constructor instead.
+ *
+ * Parameters:
+ * options - {Object} An optional object whose properties will be set on
+ * this instance.
+ */
+ initialize: function(options) {
+ OpenLayers.Format.WMC.v1.prototype.initialize.apply(
+ this, [options]
+ );
+ },
+
+ /**
+ * Method: read_sld_MinScaleDenominator
+ * Read a sld:MinScaleDenominator node.
+ *
+ * Parameters:
+ * layerInfo - {Object} An object representing a layer.
+ * node - {Element} An element node.
+ */
+ read_sld_MinScaleDenominator: function(layerInfo, node) {
+ layerInfo.options.maxScale = this.getChildValue(node);
+ },
+
+ /**
+ * Method: read_sld_MaxScaleDenominator
+ * Read a sld:MaxScaleDenominator node.
+ *
+ * Parameters:
+ * layerInfo - {Object} An object representing a layer.
+ * node - {Element} An element node.
+ */
+ read_sld_MaxScaleDenominator: function(layerInfo, node) {
+ layerInfo.options.minScale = this.getChildValue(node);
+ },
+
+ /**
+ * Method: write_wmc_Layer
+ * Create a Layer node given a layer object. This method adds elements
+ * specific to version 1.1.0.
+ *
+ * Parameters:
+ * layer - {<OpenLayers.Layer.WMS>} Layer object.
+ *
+ * Returns:
+ * {Element} A WMC Layer element node.
+ */
+ write_wmc_Layer: function(layer) {
+ var node = OpenLayers.Format.WMC.v1.prototype.write_wmc_Layer.apply(
+ this, [layer]
+ );
+
+ // min/max scale denominator elements go before the 4th element in v1
+ if(layer.options.resolutions || layer.options.scales ||
+ layer.options.minResolution || layer.options.maxScale) {
+ var minSD = this.createElementNS(
+ this.namespaces.sld, "sld:MinScaleDenominator"
+ );
+ minSD.appendChild(this.createTextNode(layer.maxScale.toPrecision(10)));
+ node.insertBefore(minSD, node.childNodes[3]);
+ }
+
+ if(layer.options.resolutions || layer.options.scales ||
+ layer.options.maxResolution || layer.options.minScale) {
+ var maxSD = this.createElementNS(
+ this.namespaces.sld, "sld:MaxScaleDenominator"
+ );
+ maxSD.appendChild(this.createTextNode(layer.minScale.toPrecision(10)));
+ node.insertBefore(maxSD, node.childNodes[4]);
+ }
+
+ return node;
+
+ },
+
+ CLASS_NAME: "OpenLayers.Format.WMC.v1_1_0"
+
+});
+/* ======================================================================
OpenLayers/Handler/Box.js
====================================================================== */
@@ -13477,6 +27890,13 @@
* olHandlerBoxZoomBox
*/
boxDivClassName: 'olHandlerBoxZoomBox',
+
+ /**
+ * Property: boxCharacteristics
+ * {Object} Caches some box characteristics from css. This is used
+ * by the getBoxCharacteristics method.
+ */
+ boxCharacteristics: null,
/**
* Constructor: OpenLayers.Handler.Box
@@ -13532,16 +27952,28 @@
* Method: moveBox
*/
moveBox: function (xy) {
- var deltaX = Math.abs(this.dragHandler.start.x - xy.x);
- var deltaY = Math.abs(this.dragHandler.start.y - xy.y);
+ var startX = this.dragHandler.start.x;
+ var startY = this.dragHandler.start.y;
+ var deltaX = Math.abs(startX - xy.x);
+ var deltaY = Math.abs(startY - xy.y);
this.zoomBox.style.width = Math.max(1, deltaX) + "px";
this.zoomBox.style.height = Math.max(1, deltaY) + "px";
- if (xy.x < this.dragHandler.start.x) {
- this.zoomBox.style.left = xy.x+"px";
+ this.zoomBox.style.left = xy.x < startX ? xy.x+"px" : startX+"px";
+ this.zoomBox.style.top = xy.y < startY ? xy.y+"px" : startY+"px";
+
+ // depending on the box model, modify width and height to take borders
+ // of the box into account
+ var box = this.getBoxCharacteristics(deltaX, deltaY);
+ if (box.newBoxModel) {
+ if (xy.x > startX) {
+ this.zoomBox.style.width =
+ Math.max(1, deltaX - box.xOffset) + "px";
+ }
+ if (xy.y > startY) {
+ this.zoomBox.style.height =
+ Math.max(1, deltaY - box.yOffset) + "px";
+ }
}
- if (xy.y < this.dragHandler.start.y) {
- this.zoomBox.style.top = xy.y+"px";
- }
},
/**
@@ -13575,6 +28007,7 @@
removeBox: function() {
this.map.viewPortDiv.removeChild(this.zoomBox);
this.zoomBox = null;
+ this.boxCharacteristics = null;
},
/**
@@ -13600,10 +28033,1111 @@
return false;
}
},
+
+ getBoxCharacteristics: function(dx, dy) {
+ if (!this.boxCharacteristics) {
+ var xOffset = parseInt(OpenLayers.Element.getStyle(this.zoomBox,
+ "border-left-width")) + parseInt(OpenLayers.Element.getStyle(
+ this.zoomBox, "border-right-width")) + 1;
+ var yOffset = parseInt(OpenLayers.Element.getStyle(this.zoomBox,
+ "border-top-width")) + parseInt(OpenLayers.Element.getStyle(
+ this.zoomBox, "border-bottom-width")) + 1;
+ // all browsers use the new box model, except IE in quirks mode
+ var newBoxModel = OpenLayers.Util.getBrowserName() == "msie" ?
+ document.compatMode != "BackCompat" : true;
+ this.boxCharacteristics = {
+ xOffset: xOffset,
+ yOffset: yOffset,
+ newBoxModel: newBoxModel
+ }
+ }
+ return this.boxCharacteristics;
+ },
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: downFeature
+ * 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
+ ====================================================================== */
+
+/* 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/Layer.js
+ * @requires OpenLayers/Util.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.EventPane
+ * Base class for 3rd party layers. Create a new event pane layer with the
+ * <OpenLayers.Layer.EventPane> constructor.
+ *
+ * Inherits from:
+ * - <OpenLayers.Layer>
+ */
+OpenLayers.Layer.EventPane = OpenLayers.Class(OpenLayers.Layer, {
+
+ /**
+ * APIProperty: smoothDragPan
+ * {Boolean} smoothDragPan determines whether non-public/internal API
+ * methods are used for better performance while dragging EventPane
+ * layers. When not in sphericalMercator mode, the smoother dragging
+ * doesn't actually move north/south directly with the number of
+ * pixels moved, resulting in a slight offset when you drag your mouse
+ * north south with this option on. If this visual disparity bothers
+ * you, you should turn this option off, or use spherical mercator.
+ * Default is on.
+ */
+ smoothDragPan: true,
+
+ /**
+ * Property: isBaseLayer
+ * {Boolean} EventPaned layers are always base layers, by necessity.
+ */
+ isBaseLayer: true,
+
+ /**
+ * APIProperty: isFixed
+ * {Boolean} EventPaned layers are fixed by default.
+ */
+ isFixed: true,
+
+ /**
+ * Property: pane
+ * {DOMElement} A reference to the element that controls the events.
+ */
+ pane: null,
+
+
+ /**
+ * Property: mapObject
+ * {Object} This is the object which will be used to load the 3rd party library
+ * in the case of the google layer, this will be of type GMap,
+ * in the case of the ve layer, this will be of type VEMap
+ */
+ mapObject: null,
+
+
+ /**
+ * Constructor: OpenLayers.Layer.EventPane
+ * Create a new event pane layer
+ *
+ * Parameters:
+ * name - {String}
+ * options - {Object} Hashtable of extra options to tag onto the layer
+ */
+ initialize: function(name, options) {
+ OpenLayers.Layer.prototype.initialize.apply(this, arguments);
+ if (this.pane == null) {
+ this.pane = OpenLayers.Util.createDiv(this.div.id + "_EventPane");
+ }
+ },
+
+ /**
+ * APIMethod: destroy
+ * Deconstruct this layer.
+ */
+ destroy: function() {
+ this.mapObject = null;
+ OpenLayers.Layer.prototype.destroy.apply(this, arguments);
+ },
+
+
+ /**
+ * Method: setMap
+ * Set the map property for the layer. This is done through an accessor
+ * so that subclasses can override this and take special action once
+ * they have their map variable set.
+ *
+ * Parameters:
+ * map - {<OpenLayers.Map>}
+ */
+ setMap: function(map) {
+ OpenLayers.Layer.prototype.setMap.apply(this, arguments);
+
+ this.pane.style.zIndex = parseInt(this.div.style.zIndex) + 1;
+ this.pane.style.display = this.div.style.display;
+ this.pane.style.width="100%";
+ this.pane.style.height="100%";
+ if (OpenLayers.Util.getBrowserName() == "msie") {
+ this.pane.style.background =
+ "url(" + OpenLayers.Util.getImagesLocation() + "blank.gif)";
+ }
+
+ if (this.isFixed) {
+ this.map.viewPortDiv.appendChild(this.pane);
+ } else {
+ this.map.layerContainerDiv.appendChild(this.pane);
+ }
+
+ // once our layer has been added to the map, we can load it
+ this.loadMapObject();
+
+ // if map didn't load, display warning
+ if (this.mapObject == null) {
+ this.loadWarningMessage();
+ }
+ },
+
+ /**
+ * APIMethod: removeMap
+ * On being removed from the map, we'll like to remove the invisible 'pane'
+ * div that we added to it on creation.
+ *
+ * Parameters:
+ * map - {<OpenLayers.Map>}
+ */
+ removeMap: function(map) {
+ if (this.pane && this.pane.parentNode) {
+ this.pane.parentNode.removeChild(this.pane);
+ this.pane = null;
+ }
+ OpenLayers.Layer.prototype.removeMap.apply(this, arguments);
+ },
+
+ /**
+ * Method: loadWarningMessage
+ * If we can't load the map lib, then display an error message to the
+ * user and tell them where to go for help.
+ *
+ * This function sets up the layout for the warning message. Each 3rd
+ * party layer must implement its own getWarningHTML() function to
+ * provide the actual warning message.
+ */
+ loadWarningMessage:function() {
+
+ this.div.style.backgroundColor = "darkblue";
+
+ var viewSize = this.map.getSize();
+
+ var msgW = Math.min(viewSize.w, 300);
+ var msgH = Math.min(viewSize.h, 200);
+ var size = new OpenLayers.Size(msgW, msgH);
+
+ var centerPx = new OpenLayers.Pixel(viewSize.w/2, viewSize.h/2);
+
+ var topLeft = centerPx.add(-size.w/2, -size.h/2);
+
+ var div = OpenLayers.Util.createDiv(this.name + "_warning",
+ topLeft,
+ size,
+ null,
+ null,
+ null,
+ "auto");
+
+ div.style.padding = "7px";
+ div.style.backgroundColor = "yellow";
+
+ div.innerHTML = this.getWarningHTML();
+ this.div.appendChild(div);
+ },
+
+ /**
+ * Method: getWarningHTML
+ * To be implemented by subclasses.
+ *
+ * Returns:
+ * {String} String with information on why layer is broken, how to get
+ * it working.
+ */
+ getWarningHTML:function() {
+ //should be implemented by subclasses
+ return "";
+ },
+
+ /**
+ * Method: display
+ * Set the display on the pane
+ *
+ * Parameters:
+ * display - {Boolean}
+ */
+ display: function(display) {
+ OpenLayers.Layer.prototype.display.apply(this, arguments);
+ this.pane.style.display = this.div.style.display;
+ },
+
+ /**
+ * Method: setZIndex
+ * Set the z-index order for the pane.
+ *
+ * Parameters:
+ * zIndex - {int}
+ */
+ setZIndex: function (zIndex) {
+ OpenLayers.Layer.prototype.setZIndex.apply(this, arguments);
+ this.pane.style.zIndex = parseInt(this.div.style.zIndex) + 1;
+ },
+
+ /**
+ * Method: moveTo
+ * Handle calls to move the layer.
+ *
+ * Parameters:
+ * bounds - {<OpenLayers.Bounds>}
+ * zoomChanged - {Boolean}
+ * dragging - {Boolean}
+ */
+ moveTo:function(bounds, zoomChanged, dragging) {
+ OpenLayers.Layer.prototype.moveTo.apply(this, arguments);
+
+ if (this.mapObject != null) {
+
+ var newCenter = this.map.getCenter();
+ var newZoom = this.map.getZoom();
+
+ if (newCenter != null) {
+
+ var moOldCenter = this.getMapObjectCenter();
+ var oldCenter = this.getOLLonLatFromMapObjectLonLat(moOldCenter);
+
+ var moOldZoom = this.getMapObjectZoom();
+ var oldZoom= this.getOLZoomFromMapObjectZoom(moOldZoom);
+
+ if ( !(newCenter.equals(oldCenter)) ||
+ !(newZoom == oldZoom) ) {
+
+ if (dragging && this.dragPanMapObject &&
+ this.smoothDragPan) {
+ var oldPx = this.map.getViewPortPxFromLonLat(oldCenter);
+ var newPx = this.map.getViewPortPxFromLonLat(newCenter);
+ this.dragPanMapObject(newPx.x-oldPx.x, oldPx.y-newPx.y);
+ } else {
+ var center = this.getMapObjectLonLatFromOLLonLat(newCenter);
+ var zoom = this.getMapObjectZoomFromOLZoom(newZoom);
+ this.setMapObjectCenter(center, zoom, dragging);
+ }
+ }
+ }
+ }
+ },
+
+
+ /********************************************************/
+ /* */
+ /* Baselayer Functions */
+ /* */
+ /********************************************************/
+
+ /**
+ * Method: getLonLatFromViewPortPx
+ * Get a map location from a pixel location
+ *
+ * Parameters:
+ * viewPortPx - {<OpenLayers.Pixel>}
+ *
+ * Returns:
+ * {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in view
+ * port OpenLayers.Pixel, translated into lon/lat by map lib
+ * If the map lib is not loaded or not centered, returns null
+ */
+ getLonLatFromViewPortPx: function (viewPortPx) {
+ var lonlat = null;
+ if ( (this.mapObject != null) &&
+ (this.getMapObjectCenter() != null) ) {
+ var moPixel = this.getMapObjectPixelFromOLPixel(viewPortPx);
+ var moLonLat = this.getMapObjectLonLatFromMapObjectPixel(moPixel);
+ lonlat = this.getOLLonLatFromMapObjectLonLat(moLonLat);
+ }
+ return lonlat;
+ },
+
+
+ /**
+ * Method: getViewPortPxFromLonLat
+ * Get a pixel location from a map location
+ *
+ * Parameters:
+ * lonlat - {<OpenLayers.LonLat>}
+ *
+ * Returns:
+ * {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in
+ * OpenLayers.LonLat, translated into view port pixels by map lib
+ * If map lib is not loaded or not centered, returns null
+ */
+ getViewPortPxFromLonLat: function (lonlat) {
+ var viewPortPx = null;
+ if ( (this.mapObject != null) &&
+ (this.getMapObjectCenter() != null) ) {
+
+ var moLonLat = this.getMapObjectLonLatFromOLLonLat(lonlat);
+ var moPixel = this.getMapObjectPixelFromMapObjectLonLat(moLonLat);
+
+ viewPortPx = this.getOLPixelFromMapObjectPixel(moPixel);
+ }
+ return viewPortPx;
+ },
+
+ /********************************************************/
+ /* */
+ /* Translation Functions */
+ /* */
+ /* The following functions translate Map Object and */
+ /* OL formats for Pixel, LonLat */
+ /* */
+ /********************************************************/
+
+ //
+ // TRANSLATION: MapObject LatLng <-> OpenLayers.LonLat
+ //
+
+ /**
+ * Method: getOLLonLatFromMapObjectLonLat
+ * Get an OL style map location from a 3rd party style map location
+ *
+ * Parameters
+ * moLonLat - {Object}
+ *
+ * Returns:
+ * {<OpenLayers.LonLat>} An OpenLayers.LonLat, translated from the passed in
+ * MapObject LonLat
+ * Returns null if null value is passed in
+ */
+ getOLLonLatFromMapObjectLonLat: function(moLonLat) {
+ var olLonLat = null;
+ if (moLonLat != null) {
+ var lon = this.getLongitudeFromMapObjectLonLat(moLonLat);
+ var lat = this.getLatitudeFromMapObjectLonLat(moLonLat);
+ olLonLat = new OpenLayers.LonLat(lon, lat);
+ }
+ return olLonLat;
+ },
+
+ /**
+ * Method: getMapObjectLonLatFromOLLonLat
+ * Get a 3rd party map location from an OL map location.
+ *
+ * Parameters:
+ * olLonLat - {<OpenLayers.LonLat>}
+ *
+ * Returns:
+ * {Object} A MapObject LonLat, translated from the passed in
+ * OpenLayers.LonLat
+ * Returns null if null value is passed in
+ */
+ getMapObjectLonLatFromOLLonLat: function(olLonLat) {
+ var moLatLng = null;
+ if (olLonLat != null) {
+ moLatLng = this.getMapObjectLonLatFromLonLat(olLonLat.lon,
+ olLonLat.lat);
+ }
+ return moLatLng;
+ },
+
+
+ //
+ // TRANSLATION: MapObject Pixel <-> OpenLayers.Pixel
+ //
+
+ /**
+ * Method: getOLPixelFromMapObjectPixel
+ * Get an OL pixel location from a 3rd party pixel location.
+ *
+ * Parameters:
+ * moPixel - {Object}
+ *
+ * Returns:
+ * {<OpenLayers.Pixel>} An OpenLayers.Pixel, translated from the passed in
+ * MapObject Pixel
+ * Returns null if null value is passed in
+ */
+ getOLPixelFromMapObjectPixel: function(moPixel) {
+ var olPixel = null;
+ if (moPixel != null) {
+ var x = this.getXFromMapObjectPixel(moPixel);
+ var y = this.getYFromMapObjectPixel(moPixel);
+ olPixel = new OpenLayers.Pixel(x, y);
+ }
+ return olPixel;
+ },
+
+ /**
+ * Method: getMapObjectPixelFromOLPixel
+ * Get a 3rd party pixel location from an OL pixel location
+ *
+ * Parameters:
+ * olPixel - {<OpenLayers.Pixel>}
+ *
+ * Returns:
+ * {Object} A MapObject Pixel, translated from the passed in
+ * OpenLayers.Pixel
+ * Returns null if null value is passed in
+ */
+ getMapObjectPixelFromOLPixel: function(olPixel) {
+ var moPixel = null;
+ if (olPixel != null) {
+ moPixel = this.getMapObjectPixelFromXY(olPixel.x, olPixel.y);
+ }
+ return moPixel;
+ },
+
+ CLASS_NAME: "OpenLayers.Layer.EventPane"
+});
+/* ======================================================================
+ OpenLayers/Layer/FixedZoomLevels.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/Layer.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.FixedZoomLevels
+ * Some Layers will already have established zoom levels (like google
+ * or ve). Instead of trying to determine them and populate a resolutions[]
+ * Array with those values, we will hijack the resolution functionality
+ * here.
+ *
+ * When you subclass FixedZoomLevels:
+ *
+ * The initResolutions() call gets nullified, meaning no resolutions[] array
+ * is set up. Which would be a big problem getResolution() in Layer, since
+ * it merely takes map.zoom and indexes into resolutions[]... but....
+ *
+ * The getResolution() call is also overridden. Instead of using the
+ * resolutions[] array, we simply calculate the current resolution based
+ * on the current extent and the current map size. But how will we be able
+ * to calculate the current extent without knowing the resolution...?
+ *
+ * The getExtent() function is also overridden. Instead of calculating extent
+ * based on the center point and the current resolution, we instead
+ * calculate the extent by getting the lonlats at the top-left and
+ * bottom-right by using the getLonLatFromViewPortPx() translation function,
+ * taken from the pixel locations (0,0) and the size of the map. But how
+ * will we be able to do lonlat-px translation without resolution....?
+ *
+ * The getZoomForResolution() method is overridden. Instead of indexing into
+ * the resolutions[] array, we call OpenLayers.Layer.getExent(), passing in
+ * the desired resolution. With this extent, we then call getZoomForExtent()
+ *
+ *
+ * Whenever you implement a layer using OpenLayers.Layer.FixedZoomLevels,
+ * it is your responsibility to provide the following three functions:
+ *
+ * - getLonLatFromViewPortPx
+ * - getViewPortPxFromLonLat
+ * - getZoomForExtent
+ *
+ * ...those three functions should generally be provided by any reasonable
+ * API that you might be working from.
+ *
+ */
+OpenLayers.Layer.FixedZoomLevels = OpenLayers.Class({
+
+ /********************************************************/
+ /* */
+ /* Baselayer Functions */
+ /* */
+ /* The following functions must all be implemented */
+ /* by all base layers */
+ /* */
+ /********************************************************/
+
+ /**
+ * Constructor: OpenLayers.Layer.FixedZoomLevels
+ * Create a new fixed zoom levels layer.
+ */
+ initialize: function() {
+ //this class is only just to add the following functions...
+ // nothing to actually do here... but it is probably a good
+ // idea to have layers that use these functions call this
+ // inititalize() anyways, in case at some point we decide we
+ // do want to put some functionality or state in here.
+ },
+
+ /**
+ * Method: initResolutions
+ * Populate the resolutions array
+ */
+ initResolutions: function() {
+
+ var props = new Array('minZoomLevel', 'maxZoomLevel', 'numZoomLevels');
+
+ for(var i=0, len=props.length; i<len; i++) {
+ var property = props[i];
+ this[property] = (this.options[property] != null)
+ ? this.options[property]
+ : this.map[property];
+ }
+
+ if ( (this.minZoomLevel == null) ||
+ (this.minZoomLevel < this.MIN_ZOOM_LEVEL) ){
+ this.minZoomLevel = this.MIN_ZOOM_LEVEL;
+ }
+
+ var limitZoomLevels = this.MAX_ZOOM_LEVEL - this.minZoomLevel + 1;
+ if (this.numZoomLevels != null) {
+ this.numZoomLevels = Math.min(this.numZoomLevels, limitZoomLevels);
+ } else {
+ if (this.maxZoomLevel != null) {
+ var zoomDiff = this.maxZoomLevel - this.minZoomLevel + 1;
+ this.numZoomLevels = Math.min(zoomDiff, limitZoomLevels);
+ } else {
+ this.numZoomLevels = limitZoomLevels;
+ }
+ }
+
+ this.maxZoomLevel = this.minZoomLevel + this.numZoomLevels - 1;
+
+ if (this.RESOLUTIONS != null) {
+ var resolutionsIndex = 0;
+ this.resolutions = [];
+ for(var i= this.minZoomLevel; i <= this.maxZoomLevel; i++) {
+ this.resolutions[resolutionsIndex++] = this.RESOLUTIONS[i];
+ }
+ this.maxResolution = this.resolutions[0];
+ this.minResolution = this.resolutions[this.resolutions.length - 1];
+ }
+ },
+
+ /**
+ * APIMethod: getResolution
+ * Get the current map resolution
+ *
+ * Returns:
+ * {Float} Map units per Pixel
+ */
+ getResolution: function() {
+
+ if (this.resolutions != null) {
+ return OpenLayers.Layer.prototype.getResolution.apply(this, arguments);
+ } else {
+ var resolution = null;
+
+ var viewSize = this.map.getSize();
+ var extent = this.getExtent();
+
+ if ((viewSize != null) && (extent != null)) {
+ resolution = Math.max( extent.getWidth() / viewSize.w,
+ extent.getHeight() / viewSize.h );
+ }
+ return resolution;
+ }
+ },
+
+ /**
+ * APIMethod: getExtent
+ * Calculates using px-> lonlat translation functions on tl and br
+ * corners of viewport
+ *
+ * Returns:
+ * {<OpenLayers.Bounds>} A Bounds object which represents the lon/lat
+ * bounds of the current viewPort.
+ */
+ getExtent: function () {
+ var extent = null;
+
+
+ var size = this.map.getSize();
+
+ var tlPx = new OpenLayers.Pixel(0,0);
+ var tlLL = this.getLonLatFromViewPortPx(tlPx);
+
+ var brPx = new OpenLayers.Pixel(size.w, size.h);
+ var brLL = this.getLonLatFromViewPortPx(brPx);
+
+ if ((tlLL != null) && (brLL != null)) {
+ extent = new OpenLayers.Bounds(tlLL.lon,
+ brLL.lat,
+ brLL.lon,
+ tlLL.lat);
+ }
+
+ return extent;
+ },
+
+ /**
+ * Method: getZoomForResolution
+ * Get the zoom level for a given resolution
+ *
+ * Parameters:
+ * resolution - {Float}
+ *
+ * Returns:
+ * {Integer} A suitable zoom level for the specified resolution.
+ * If no baselayer is set, returns null.
+ */
+ getZoomForResolution: function(resolution) {
+
+ if (this.resolutions != null) {
+ return OpenLayers.Layer.prototype.getZoomForResolution.apply(this, arguments);
+ } else {
+ var extent = OpenLayers.Layer.prototype.getExtent.apply(this, []);
+ return this.getZoomForExtent(extent);
+ }
+ },
+
+
+
+
+ /********************************************************/
+ /* */
+ /* Translation Functions */
+ /* */
+ /* The following functions translate GMaps and OL */
+ /* formats for Pixel, LonLat, Bounds, and Zoom */
+ /* */
+ /********************************************************/
+
+
+ //
+ // TRANSLATION: MapObject Zoom <-> OpenLayers Zoom
+ //
+
+ /**
+ * Method: getOLZoomFromMapObjectZoom
+ * Get the OL zoom index from the map object zoom level
+ *
+ * Parameters:
+ * moZoom - {Integer}
+ *
+ * Returns:
+ * {Integer} An OpenLayers Zoom level, translated from the passed in zoom
+ * Returns null if null value is passed in
+ */
+ getOLZoomFromMapObjectZoom: function(moZoom) {
+ var zoom = null;
+ if (moZoom != null) {
+ zoom = moZoom - this.minZoomLevel;
+ }
+ return zoom;
+ },
+
+ /**
+ * Method: getMapObjectZoomFromOLZoom
+ * Get the map object zoom level from the OL zoom level
+ *
+ * Parameters:
+ * olZoom - {Integer}
+ *
+ * Returns:
+ * {Integer} A MapObject level, translated from the passed in olZoom
+ * Returns null if null value is passed in
+ */
+ getMapObjectZoomFromOLZoom: function(olZoom) {
+ var zoom = null;
+ if (olZoom != null) {
+ zoom = olZoom + this.minZoomLevel;
+ }
+ return zoom;
+ },
+
+ CLASS_NAME: "FixedZoomLevels.js"
+});
+
+/* ======================================================================
OpenLayers/Layer/HTTPRequest.js
====================================================================== */
@@ -13769,7 +29303,7 @@
*/
selectUrl: function(paramString, urls) {
var product = 1;
- for (var i = 0; i < paramString.length; i++) {
+ for (var i=0, len=paramString.length; i<len; i++) {
product *= paramString.charCodeAt(i) * this.URL_HASH_FACTOR;
product -= Math.floor(product);
}
@@ -13848,6 +29382,1072 @@
CLASS_NAME: "OpenLayers.Layer.HTTPRequest"
});
/* ======================================================================
+ OpenLayers/Layer/Image.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/Layer.js
+ * @requires OpenLayers/Tile/Image.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.Image
+ * Instances of OpenLayers.Layer.Image are used to display data from a web
+ * accessible image as a map layer. Create a new image layer with the
+ * <OpenLayers.Layer.Image> constructor. Inherits from <OpenLayers.Layer>.
+ */
+OpenLayers.Layer.Image = OpenLayers.Class(OpenLayers.Layer, {
+
+ /**
+ * Property: isBaseLayer
+ * {Boolean} The layer is a base layer. Default is true. Set this property
+ * in the layer options
+ */
+ isBaseLayer: true,
+
+ /**
+ * Property: url
+ * {String} URL of the image to use
+ */
+ url: null,
+
+ /**
+ * Property: extent
+ * {<OpenLayers.Bounds>} The image bounds in map units
+ */
+ extent: null,
+
+ /**
+ * Property: size
+ * {<OpenLayers.Size>} The image size in pixels
+ */
+ size: null,
+
+ /**
+ * Property: tile
+ * {<OpenLayers.Tile.Image>}
+ */
+ tile: null,
+
+ /**
+ * Property: aspectRatio
+ * {Float} The ratio of height/width represented by a single pixel in the
+ * graphic
+ */
+ aspectRatio: null,
+
+ /**
+ * Constructor: OpenLayers.Layer.Image
+ * Create a new image layer
+ *
+ * Parameters:
+ * name - {String} A name for the layer.
+ * url - {String} Relative or absolute path to the image
+ * extent - {<OpenLayers.Bounds>} The extent represented by the image
+ * size - {<OpenLayers.Size>} The size (in pixels) of the image
+ * options - {Object} Hashtable of extra options to tag onto the layer
+ */
+ initialize: function(name, url, extent, size, options) {
+ this.url = url;
+ this.extent = extent;
+ this.size = size;
+ OpenLayers.Layer.prototype.initialize.apply(this, [name, options]);
+
+ this.aspectRatio = (this.extent.getHeight() / this.size.h) /
+ (this.extent.getWidth() / this.size.w);
+ },
+
+ /**
+ * Method: destroy
+ * Destroy this layer
+ */
+ destroy: function() {
+ if (this.tile) {
+ this.tile.destroy();
+ this.tile = null;
+ }
+ OpenLayers.Layer.prototype.destroy.apply(this, arguments);
+ },
+
+ /**
+ * Method: clone
+ * Create a clone of this layer
+ *
+ * Paramters:
+ * obj - {Object} An optional layer (is this ever used?)
+ *
+ * Returns:
+ * {<OpenLayers.Layer.Image>} An exact copy of this layer
+ */
+ clone: function(obj) {
+
+ if(obj == null) {
+ obj = new OpenLayers.Layer.Image(this.name,
+ this.url,
+ this.extent,
+ this.size,
+ this.options);
+ }
+
+ //get all additions from superclasses
+ obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]);
+
+ // copy/set any non-init, non-simple values here
+
+ return obj;
+ },
+
+ /**
+ * APIMethod: setMap
+ *
+ * Parameters:
+ * map - {<OpenLayers.Map>}
+ */
+ setMap: function(map) {
+ /**
+ * If nothing to do with resolutions has been set, assume a single
+ * resolution determined by ratio*extent/size - if an image has a
+ * pixel aspect ratio different than one (as calculated above), the
+ * image will be stretched in one dimension only.
+ */
+ if( this.options.maxResolution == null ) {
+ this.options.maxResolution = this.aspectRatio *
+ this.extent.getWidth() /
+ this.size.w;
+ }
+ OpenLayers.Layer.prototype.setMap.apply(this, arguments);
+ },
+
+ /**
+ * Method: moveTo
+ * Create the tile for the image or resize it for the new resolution
+ *
+ * Parameters:
+ * bounds - {<OpenLayers.Bounds>}
+ * zoomChanged - {Boolean}
+ * dragging - {Boolean}
+ */
+ moveTo:function(bounds, zoomChanged, dragging) {
+ OpenLayers.Layer.prototype.moveTo.apply(this, arguments);
+
+ var firstRendering = (this.tile == null);
+
+ if(zoomChanged || firstRendering) {
+
+ //determine new tile size
+ this.setTileSize();
+
+ //determine new position (upper left corner of new bounds)
+ var ul = new OpenLayers.LonLat(this.extent.left, this.extent.top);
+ var ulPx = this.map.getLayerPxFromLonLat(ul);
+
+ if(firstRendering) {
+ //create the new tile
+ this.tile = new OpenLayers.Tile.Image(this, ulPx, this.extent,
+ null, this.tileSize);
+ } else {
+ //just resize the tile and set it's new position
+ this.tile.size = this.tileSize.clone();
+ this.tile.position = ulPx.clone();
+ }
+ this.tile.draw();
+ }
+ },
+
+ /**
+ * Set the tile size based on the map size.
+ */
+ setTileSize: function() {
+ var tileWidth = this.extent.getWidth() / this.map.getResolution();
+ var tileHeight = this.extent.getHeight() / this.map.getResolution();
+ this.tileSize = new OpenLayers.Size(tileWidth, tileHeight);
+ },
+
+ /**
+ * APIMethod: setUrl
+ *
+ * Parameters:
+ * newUrl - {String}
+ */
+ setUrl: function(newUrl) {
+ this.url = newUrl;
+ this.tile.draw();
+ },
+
+ /**
+ * APIMethod: getURL
+ * The url we return is always the same (the image itself never changes)
+ * so we can ignore the bounds parameter (it will always be the same,
+ * anyways)
+ *
+ * Parameters:
+ * bounds - {<OpenLayers.Bounds>}
+ */
+ getURL: function(bounds) {
+ return this.url;
+ },
+
+ CLASS_NAME: "OpenLayers.Layer.Image"
+});
+/* ======================================================================
+ OpenLayers/Layer/Markers.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/Layer.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.Markers
+ *
+ * Inherits from:
+ * - <OpenLayers.Layer>
+ */
+OpenLayers.Layer.Markers = OpenLayers.Class(OpenLayers.Layer, {
+
+ /**
+ * APIProperty: isBaseLayer
+ * {Boolean} Markers layer is never a base layer.
+ */
+ isBaseLayer: false,
+
+ /**
+ * Property: markers
+ * {Array(<OpenLayers.Marker>)} internal marker list
+ */
+ markers: null,
+
+
+ /**
+ * Property: drawn
+ * {Boolean} internal state of drawing. This is a workaround for the fact
+ * that the map does not call moveTo with a zoomChanged when the map is
+ * first starting up. This lets us catch the case where we have *never*
+ * drawn the layer, and draw it even if the zoom hasn't changed.
+ */
+ drawn: false,
+
+ /**
+ * Constructor: OpenLayers.Layer.Markers
+ * Create a Markers layer.
+ *
+ * Parameters:
+ * name - {String}
+ * options - {Object} Hashtable of extra options to tag onto the layer
+ */
+ initialize: function(name, options) {
+ OpenLayers.Layer.prototype.initialize.apply(this, arguments);
+ this.markers = [];
+ },
+
+ /**
+ * APIMethod: destroy
+ */
+ destroy: function() {
+ this.clearMarkers();
+ this.markers = null;
+ OpenLayers.Layer.prototype.destroy.apply(this, arguments);
+ },
+
+ /**
+ * APIMethod: setOpacity
+ * Sets the opacity for all the markers.
+ *
+ * Parameter:
+ * opacity - {Float}
+ */
+ setOpacity: function(opacity) {
+ if (opacity != this.opacity) {
+ this.opacity = opacity;
+ for (var i=0, len=this.markers.length; i<len; i++) {
+ this.markers[i].setOpacity(this.opacity);
+ }
+ }
+ },
+
+ /**
+ * Method: moveTo
+ *
+ * Parameters:
+ * bounds - {<OpenLayers.Bounds>}
+ * zoomChanged - {Boolean}
+ * dragging - {Boolean}
+ */
+ moveTo:function(bounds, zoomChanged, dragging) {
+ OpenLayers.Layer.prototype.moveTo.apply(this, arguments);
+
+ if (zoomChanged || !this.drawn) {
+ for(var i=0, len=this.markers.length; i<len; i++) {
+ this.drawMarker(this.markers[i]);
+ }
+ this.drawn = true;
+ }
+ },
+
+ /**
+ * APIMethod: addMarker
+ *
+ * Parameters:
+ * marker - {<OpenLayers.Marker>}
+ */
+ addMarker: function(marker) {
+ this.markers.push(marker);
+
+ if (this.opacity != null) {
+ marker.setOpacity(this.opacity);
+ }
+
+ if (this.map && this.map.getExtent()) {
+ marker.map = this.map;
+ this.drawMarker(marker);
+ }
+ },
+
+ /**
+ * APIMethod: removeMarker
+ *
+ * Parameters:
+ * marker - {<OpenLayers.Marker>}
+ */
+ removeMarker: function(marker) {
+ if (this.markers && this.markers.length) {
+ OpenLayers.Util.removeItem(this.markers, marker);
+ if ((marker.icon != null) && (marker.icon.imageDiv != null) &&
+ (marker.icon.imageDiv.parentNode == this.div) ) {
+ this.div.removeChild(marker.icon.imageDiv);
+ marker.drawn = false;
+ }
+ }
+ },
+
+ /**
+ * Method: clearMarkers
+ * This method removes all markers from a layer. The markers are not
+ * destroyed by this function, but are removed from the list of markers.
+ */
+ clearMarkers: function() {
+ if (this.markers != null) {
+ while(this.markers.length > 0) {
+ this.removeMarker(this.markers[0]);
+ }
+ }
+ },
+
+ /**
+ * Method: drawMarker
+ * Calculate the pixel location for the marker, create it, and
+ * add it to the layer's div
+ *
+ * Parameters:
+ * marker - {<OpenLayers.Marker>}
+ */
+ drawMarker: function(marker) {
+ var px = this.map.getLayerPxFromLonLat(marker.lonlat);
+ if (px == null) {
+ marker.display(false);
+ } else {
+ var markerImg = marker.draw(px);
+ if (!marker.drawn) {
+ this.div.appendChild(markerImg);
+ marker.drawn = true;
+ }
+ }
+ },
+
+ /**
+ * APIMethod: getDataExtent
+ * Calculates the max extent which includes all of the markers.
+ *
+ * Returns:
+ * {<OpenLayers.Bounds>}
+ */
+ getDataExtent: function () {
+ var maxExtent = null;
+
+ if ( this.markers && (this.markers.length > 0)) {
+ var maxExtent = new OpenLayers.Bounds();
+ for(var i=0, len=this.markers.length; i<len; i++) {
+ var marker = this.markers[i];
+ maxExtent.extend(marker.lonlat);
+ }
+ }
+
+ return maxExtent;
+ },
+
+ CLASS_NAME: "OpenLayers.Layer.Markers"
+});
+/* ======================================================================
+ OpenLayers/Layer/SphericalMercator.js
+ ====================================================================== */
+
+/**
+ * @requires OpenLayers/Layer.js
+ * @requires OpenLayers/Projection.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.SphericalMercator
+ * A mixin for layers that wraps up the pieces neccesary to have a coordinate
+ * conversion for working with commercial APIs which use a spherical
+ * mercator projection. Using this layer as a base layer, additional
+ * layers can be used as overlays if they are in the same projection.
+ *
+ * A layer is given properties of this object by setting the sphericalMercator
+ * property to true.
+ *
+ * More projection information:
+ * - http://spatialreference.org/ref/user/google-projection/
+ *
+ * Proj4 Text:
+ * +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0
+ * +k=1.0 +units=m +nadgrids=@null +no_defs
+ *
+ * WKT:
+ * 900913=PROJCS["WGS84 / Simple Mercator", GEOGCS["WGS 84",
+ * DATUM["WGS_1984", SPHEROID["WGS_1984", 6378137.0, 298.257223563]],
+ * PRIMEM["Greenwich", 0.0], UNIT["degree", 0.017453292519943295],
+ * AXIS["Longitude", EAST], AXIS["Latitude", NORTH]],
+ * PROJECTION["Mercator_1SP_Google"],
+ * PARAMETER["latitude_of_origin", 0.0], PARAMETER["central_meridian", 0.0],
+ * PARAMETER["scale_factor", 1.0], PARAMETER["false_easting", 0.0],
+ * PARAMETER["false_northing", 0.0], UNIT["m", 1.0], AXIS["x", EAST],
+ * AXIS["y", NORTH], AUTHORITY["EPSG","900913"]]
+ */
+OpenLayers.Layer.SphericalMercator = {
+
+ /**
+ * Method: getExtent
+ * Get the map's extent.
+ *
+ * Returns:
+ * {<OpenLayers.Bounds>} The map extent.
+ */
+ getExtent: function() {
+ var extent = null;
+ if (this.sphericalMercator) {
+ extent = this.map.calculateBounds();
+ } else {
+ extent = OpenLayers.Layer.FixedZoomLevels.prototype.getExtent.apply(this);
+ }
+ return extent;
+ },
+
+ /**
+ * Method: initMercatorParameters
+ * Set up the mercator parameters on the layer: resolutions,
+ * projection, units.
+ */
+ initMercatorParameters: function() {
+ // set up properties for Mercator - assume EPSG:900913
+ this.RESOLUTIONS = [];
+ var maxResolution = 156543.0339;
+ for(var zoom=0; zoom<=this.MAX_ZOOM_LEVEL; ++zoom) {
+ this.RESOLUTIONS[zoom] = maxResolution / Math.pow(2, zoom);
+ }
+ this.units = "m";
+ this.projection = "EPSG:900913";
+ },
+
+ /**
+ * APIMethod: forwardMercator
+ * Given a lon,lat in EPSG:4326, return a point in Spherical Mercator.
+ *
+ * Parameters:
+ * lon - {float}
+ * lat - {float}
+ *
+ * Returns:
+ * {<OpenLayers.LonLat>} The coordinates transformed to Mercator.
+ */
+ forwardMercator: function(lon, lat) {
+ var x = lon * 20037508.34 / 180;
+ var y = Math.log(Math.tan((90 + lat) * Math.PI / 360)) / (Math.PI / 180);
+
+ y = y * 20037508.34 / 180;
+
+ return new OpenLayers.LonLat(x, y);
+ },
+
+ /**
+ * APIMethod: inverseMercator
+ * Given a x,y in Spherical Mercator, return a point in EPSG:4326.
+ *
+ * Parameters:
+ * x - {float} A map x in Spherical Mercator.
+ * y - {float} A map y in Spherical Mercator.
+ *
+ * Returns:
+ * {<OpenLayers.LonLat>} The coordinates transformed to EPSG:4326.
+ */
+ inverseMercator: function(x, y) {
+
+ var lon = (x / 20037508.34) * 180;
+ var lat = (y / 20037508.34) * 180;
+
+ lat = 180/Math.PI * (2 * Math.atan(Math.exp(lat * Math.PI / 180)) - Math.PI / 2);
+
+ return new OpenLayers.LonLat(lon, lat);
+ },
+
+ /**
+ * Method: projectForward
+ * Given an object with x and y properties in EPSG:4326, modify the x,y
+ * properties on the object to be the Spherical Mercator projected
+ * coordinates.
+ *
+ * Parameters:
+ * point - {Object} An object with x and y properties.
+ *
+ * Returns:
+ * {Object} The point, with the x and y properties transformed to spherical
+ * mercator.
+ */
+ projectForward: function(point) {
+ var lonlat = OpenLayers.Layer.SphericalMercator.forwardMercator(point.x, point.y);
+ point.x = lonlat.lon;
+ point.y = lonlat.lat;
+ return point;
+ },
+
+ /**
+ * Method: projectInverse
+ * Given an object with x and y properties in Spherical Mercator, modify
+ * the x,y properties on the object to be the unprojected coordinates.
+ *
+ * Parameters:
+ * point - {Object} An object with x and y properties.
+ *
+ * Returns:
+ * {Object} The point, with the x and y properties transformed from
+ * spherical mercator to unprojected coordinates..
+ */
+ projectInverse: function(point) {
+ var lonlat = OpenLayers.Layer.SphericalMercator.inverseMercator(point.x, point.y);
+ point.x = lonlat.lon;
+ point.y = lonlat.lat;
+ return point;
+ }
+
+};
+
+/**
+ * Note: Two transforms declared
+ * Transforms from EPSG:4326 to EPSG:900913 and from EPSG:900913 to EPSG:4326
+ * are set by this class.
+ */
+OpenLayers.Projection.addTransform("EPSG:4326", "EPSG:900913",
+ OpenLayers.Layer.SphericalMercator.projectForward);
+OpenLayers.Projection.addTransform("EPSG:900913", "EPSG:4326",
+ OpenLayers.Layer.SphericalMercator.projectInverse);
+/* ======================================================================
+ OpenLayers/Control/DrawFeature.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.DrawFeature
+ * Draws features on a vector layer when active.
+ *
+ * Inherits from:
+ * - <OpenLayers.Control>
+ */
+OpenLayers.Control.DrawFeature = OpenLayers.Class(OpenLayers.Control, {
+
+ /**
+ * Property: layer
+ * {<OpenLayers.Layer.Vector>}
+ */
+ layer: null,
+
+ /**
+ * Property: callbacks
+ * {Object} The functions that are sent to the handler for callback
+ */
+ callbacks: null,
+
+ /**
+ * Constant: EVENT_TYPES
+ *
+ * Supported event types:
+ * - *featureadded* Triggered when a feature is added
+ */
+ EVENT_TYPES: ["featureadded"],
+
+ /**
+ * APIProperty: featureAdded
+ * {Function} Called after each feature is added
+ */
+ featureAdded: function() {},
+
+ /**
+ * APIProperty: handlerOptions
+ * {Object} Used to set non-default properties on the control's handler
+ */
+ handlerOptions: null,
+
+ /**
+ * Constructor: OpenLayers.Control.DrawFeature
+ *
+ * Parameters:
+ * layer - {<OpenLayers.Layer.Vector>}
+ * handler - {<OpenLayers.Handler>}
+ * options - {Object}
+ */
+ initialize: function(layer, handler, options) {
+
+ // concatenate events specific to vector with those from the base
+ this.EVENT_TYPES =
+ OpenLayers.Control.DrawFeature.prototype.EVENT_TYPES.concat(
+ OpenLayers.Control.prototype.EVENT_TYPES
+ );
+
+ OpenLayers.Control.prototype.initialize.apply(this, [options]);
+ this.callbacks = OpenLayers.Util.extend({done: this.drawFeature},
+ this.callbacks);
+ this.layer = layer;
+ this.handler = new handler(this, this.callbacks, this.handlerOptions);
+ },
+
+ /**
+ * Method: drawFeature
+ */
+ drawFeature: function(geometry) {
+ var feature = new OpenLayers.Feature.Vector(geometry);
+ this.layer.addFeatures([feature]);
+ this.featureAdded(feature);
+ this.events.triggerEvent("featureadded",{feature : feature});
+ },
+
+ CLASS_NAME: "OpenLayers.Control.DrawFeature"
+});
+/* ======================================================================
+ OpenLayers/Control/SelectFeature.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
+ * @requires OpenLayers/Handler/Feature.js
+ */
+
+/**
+ * Class: OpenLayers.Control.SelectFeature
+ * Selects vector features from a given layer on click or hover.
+ *
+ * Inherits from:
+ * - <OpenLayers.Control>
+ */
+OpenLayers.Control.SelectFeature = OpenLayers.Class(OpenLayers.Control, {
+
+ /**
+ * Property: multipleKey
+ * {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets
+ * the <multiple> property to true. Default is null.
+ */
+ multipleKey: null,
+
+ /**
+ * Property: toggleKey
+ * {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets
+ * the <toggle> property to true. Default is null.
+ */
+ toggleKey: null,
+
+ /**
+ * APIProperty: multiple
+ * {Boolean} Allow selection of multiple geometries. Default is false.
+ */
+ multiple: false,
+
+ /**
+ * APIProperty: clickout
+ * {Boolean} Unselect features when clicking outside any feature.
+ * Default is true.
+ */
+ clickout: true,
+
+ /**
+ * APIProperty: toggle
+ * {Boolean} Unselect a selected feature on click. Default is false. Only
+ * has meaning if hover is false.
+ */
+ toggle: false,
+
+ /**
+ * APIProperty: hover
+ * {Boolean} Select on mouse over and deselect on mouse out. If true, this
+ * ignores clicks and only listens to mouse moves.
+ */
+ hover: false,
+
+ /**
+ * APIProperty: box
+ * {Boolean} Allow feature selection by drawing a box.
+ */
+ box: false,
+
+ /**
+ * APIProperty: onSelect
+ * {Function} Optional function to be called when a feature is selected.
+ * The function should expect to be called with a feature.
+ */
+ onSelect: function() {},
+
+ /**
+ * APIProperty: onUnselect
+ * {Function} Optional function to be called when a feature is unselected.
+ * The function should expect to be called with a feature.
+ */
+ onUnselect: function() {},
+
+ /**
+ * APIProperty: geometryTypes
+ * {Array(String)} To restrict selecting to a limited set of geometry types,
+ * send a list of strings corresponding to the geometry class names.
+ */
+ geometryTypes: null,
+
+ /**
+ * Property: layer
+ * {<OpenLayers.Layer.Vector>}
+ */
+ layer: null,
+
+ /**
+ * APIProperty: callbacks
+ * {Object} The functions that are sent to the handlers.feature for callback
+ */
+ callbacks: null,
+
+ /**
+ * APIProperty: selectStyle
+ * {Object} Hash of styles
+ */
+ selectStyle: null,
+
+ /**
+ * Property: renderIntent
+ * {String} key used to retrieve the select style from the layer's
+ * style map.
+ */
+ renderIntent: "select",
+
+ /**
+ * Property: handlers
+ * {Object} Object with references to multiple <OpenLayers.Handler>
+ * instances.
+ */
+ handlers: null,
+
+ /**
+ * Constructor: <OpenLayers.Control.SelectFeature>
+ *
+ * Parameters:
+ * layer - {<OpenLayers.Layer.Vector>}
+ * options - {Object}
+ */
+ initialize: function(layer, options) {
+ OpenLayers.Control.prototype.initialize.apply(this, [options]);
+ this.layer = layer;
+ var callbacks = {
+ click: this.clickFeature,
+ clickout: this.clickoutFeature
+ };
+ if (this.hover) {
+ callbacks.over = this.overFeature;
+ callbacks.out = this.outFeature;
+ }
+
+ this.callbacks = OpenLayers.Util.extend(callbacks, this.callbacks);
+ this.handlers = {
+ feature: new OpenLayers.Handler.Feature(
+ this, layer, this.callbacks, {geometryTypes: this.geometryTypes}
+ )
+ };
+
+ if (this.box) {
+ this.handlers.box = new OpenLayers.Handler.Box(
+ this, {done: this.selectBox},
+ {boxDivClassName: "olHandlerBoxSelectFeature"}
+ );
+ }
+ },
+
+ /**
+ * Method: activate
+ * Activates the control.
+ *
+ * Returns:
+ * {Boolean} The control was effectively activated.
+ */
+ activate: function () {
+ if (!this.active) {
+ this.handlers.feature.activate();
+ if(this.box && this.handlers.box) {
+ this.handlers.box.activate();
+ }
+ }
+ return OpenLayers.Control.prototype.activate.apply(
+ this, arguments
+ );
+ },
+
+ /**
+ * Method: deactivate
+ * Deactivates the control.
+ *
+ * Returns:
+ * {Boolean} The control was effectively deactivated.
+ */
+ deactivate: function () {
+ if (this.active) {
+ this.handlers.feature.deactivate();
+ if(this.handlers.box) {
+ this.handlers.box.deactivate();
+ }
+ }
+ return OpenLayers.Control.prototype.deactivate.apply(
+ this, arguments
+ );
+ },
+
+ /**
+ * Method: unselectAll
+ * Unselect all selected features. To unselect all except for a single
+ * feature, set the options.except property to the feature.
+ *
+ * Parameters:
+ * options - {Object} Optional configuration object.
+ */
+ unselectAll: function(options) {
+ // we'll want an option to supress notification here
+ var feature;
+ for(var i=this.layer.selectedFeatures.length-1; i>=0; --i) {
+ feature = this.layer.selectedFeatures[i];
+ if(!options || options.except != feature) {
+ this.unselect(feature);
+ }
+ }
+ },
+
+ /**
+ * Method: clickFeature
+ * Called on click in a feature
+ * Only responds if this.hover is false.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>}
+ */
+ clickFeature: function(feature) {
+ if(!this.hover) {
+ var selected = (OpenLayers.Util.indexOf(this.layer.selectedFeatures,
+ feature) > -1);
+ if(selected) {
+ if(this.toggleSelect()) {
+ this.unselect(feature);
+ } else if(!this.multipleSelect()) {
+ this.unselectAll({except: feature});
+ }
+ } else {
+ if(!this.multipleSelect()) {
+ this.unselectAll({except: feature});
+ }
+ this.select(feature);
+ }
+ }
+ },
+
+ /**
+ * Method: multipleSelect
+ * Allow for multiple selected features based on <multiple> property and
+ * <multipleKey> event modifier.
+ *
+ * Returns:
+ * {Boolean} Allow for multiple selected features.
+ */
+ multipleSelect: function() {
+ return this.multiple || this.handlers.feature.evt[this.multipleKey];
+ },
+
+ /**
+ * Method: toggleSelect
+ * Event should toggle the selected state of a feature based on <toggle>
+ * property and <toggleKey> event modifier.
+ *
+ * Returns:
+ * {Boolean} Toggle the selected state of a feature.
+ */
+ toggleSelect: function() {
+ return this.toggle || this.handlers.feature.evt[this.toggleKey];
+ },
+
+ /**
+ * Method: clickoutFeature
+ * Called on click outside a previously clicked (selected) feature.
+ * Only responds if this.hover is false.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Vector.Feature>}
+ */
+ clickoutFeature: function(feature) {
+ if(!this.hover && this.clickout) {
+ this.unselectAll();
+ }
+ },
+
+ /**
+ * Method: overFeature
+ * Called on over a feature.
+ * Only responds if this.hover is true.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>}
+ */
+ overFeature: function(feature) {
+ if(this.hover &&
+ (OpenLayers.Util.indexOf(this.layer.selectedFeatures, feature) == -1)) {
+ this.select(feature);
+ }
+ },
+
+ /**
+ * Method: outFeature
+ * Called on out of a selected feature.
+ * Only responds if this.hover is true.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>}
+ */
+ outFeature: function(feature) {
+ if(this.hover) {
+ this.unselect(feature);
+ }
+ },
+
+ /**
+ * Method: select
+ * Add feature to the layer's selectedFeature array, render the feature as
+ * selected, and call the onSelect function.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>}
+ */
+ select: function(feature) {
+ var cont = this.layer.events.triggerEvent("beforefeatureselected", {
+ feature: feature
+ });
+ if(cont !== false) {
+ this.layer.selectedFeatures.push(feature);
+
+ var selectStyle = this.selectStyle || this.renderIntent;
+
+ this.layer.drawFeature(feature, selectStyle);
+ this.layer.events.triggerEvent("featureselected", {feature: feature});
+ this.onSelect(feature);
+ }
+ },
+
+ /**
+ * Method: unselect
+ * Remove feature from the layer's selectedFeature array, render the feature as
+ * normal, and call the onUnselect function.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>}
+ */
+ unselect: function(feature) {
+ // Store feature style for restoration later
+ this.layer.drawFeature(feature, "default");
+ OpenLayers.Util.removeItem(this.layer.selectedFeatures, feature);
+ this.layer.events.triggerEvent("featureunselected", {feature: feature});
+ this.onUnselect(feature);
+ },
+
+ /**
+ * Method: selectBox
+ * Callback from the handlers.box set up when <box> selection is true
+ * on.
+ *
+ * Parameters:
+ * position - {<OpenLayers.Bounds> || <OpenLayers.Pixel> }
+ */
+ selectBox: function(position) {
+ if (position instanceof OpenLayers.Bounds) {
+ var minXY = this.map.getLonLatFromPixel(
+ new OpenLayers.Pixel(position.left, position.bottom)
+ );
+ var maxXY = this.map.getLonLatFromPixel(
+ new OpenLayers.Pixel(position.right, position.top)
+ );
+ var bounds = new OpenLayers.Bounds(
+ minXY.lon, minXY.lat, maxXY.lon, maxXY.lat
+ );
+
+ // if multiple is false, first deselect currently selected features
+ if (!this.multipleSelect()) {
+ this.unselectAll();
+ }
+
+ // because we're using a box, we consider we want multiple selection
+ var prevMultiple = this.multiple;
+ this.multiple = true;
+ for(var i=0, len = this.layer.features.length; i<len; ++i) {
+ var feature = this.layer.features[i];
+ if (this.geometryTypes == null || OpenLayers.Util.indexOf(
+ this.geometryTypes, feature.geometry.CLASS_NAME) > -1) {
+ if (bounds.toGeometry().intersects(feature.geometry)) {
+ if (OpenLayers.Util.indexOf(this.layer.selectedFeatures, feature) == -1) {
+ this.select(feature);
+ }
+ }
+ }
+ }
+ this.multiple = prevMultiple;
+ }
+ },
+
+ /**
+ * Method: setMap
+ * Set the map property for the control.
+ *
+ * Parameters:
+ * map - {<OpenLayers.Map>}
+ */
+ setMap: function(map) {
+ this.handlers.feature.setMap(map);
+ if (this.box) {
+ this.handlers.box.setMap(map);
+ }
+ OpenLayers.Control.prototype.setMap.apply(this, arguments);
+ },
+
+ CLASS_NAME: "OpenLayers.Control.SelectFeature"
+});
+/* ======================================================================
OpenLayers/Control/ZoomBox.js
====================================================================== */
@@ -13931,6 +30531,1260 @@
CLASS_NAME: "OpenLayers.Control.ZoomBox"
});
/* ======================================================================
+ OpenLayers/Format/WKT.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/Format.js
+ * @requires OpenLayers/Feature/Vector.js
+ */
+
+/**
+ * Class: OpenLayers.Format.WKT
+ * Class for reading and writing Well-Known Text. Create a new instance
+ * with the <OpenLayers.Format.WKT> constructor.
+ *
+ * Inherits from:
+ * - <OpenLayers.Format>
+ */
+OpenLayers.Format.WKT = OpenLayers.Class(OpenLayers.Format, {
+
+ /**
+ * Constructor: OpenLayers.Format.WKT
+ * Create a new parser for WKT
+ *
+ * Parameters:
+ * options - {Object} An optional object whose properties will be set on
+ * this instance
+ *
+ * Returns:
+ * {<OpenLayers.Format.WKT>} A new WKT parser.
+ */
+ initialize: function(options) {
+ this.regExes = {
+ 'typeStr': /^\s*(\w+)\s*\(\s*(.*)\s*\)\s*$/,
+ 'spaces': /\s+/,
+ 'parenComma': /\)\s*,\s*\(/,
+ 'doubleParenComma': /\)\s*\)\s*,\s*\(\s*\(/, // can't use {2} here
+ 'trimParens': /^\s*\(?(.*?)\)?\s*$/
+ };
+ OpenLayers.Format.prototype.initialize.apply(this, [options]);
+ },
+
+ /**
+ * Method: read
+ * Deserialize a WKT string and return a vector feature or an
+ * array of vector features. Supports WKT for POINT, MULTIPOINT,
+ * LINESTRING, MULTILINESTRING, POLYGON, MULTIPOLYGON, and
+ * GEOMETRYCOLLECTION.
+ *
+ * Parameters:
+ * wkt - {String} A WKT string
+ *
+ * Returns:
+ * {<OpenLayers.Feature.Vector>|Array} A feature or array of features for
+ * GEOMETRYCOLLECTION WKT.
+ */
+ read: function(wkt) {
+ var features, type, str;
+ var matches = this.regExes.typeStr.exec(wkt);
+ if(matches) {
+ type = matches[1].toLowerCase();
+ str = matches[2];
+ if(this.parse[type]) {
+ features = this.parse[type].apply(this, [str]);
+ }
+ if (this.internalProjection && this.externalProjection) {
+ if (features &&
+ features.CLASS_NAME == "OpenLayers.Feature.Vector") {
+ features.geometry.transform(this.externalProjection,
+ this.internalProjection);
+ } else if (features &&
+ type != "geometrycollection" &&
+ typeof features == "object") {
+ for (var i=0, len=features.length; i<len; i++) {
+ var component = features[i];
+ component.geometry.transform(this.externalProjection,
+ this.internalProjection);
+ }
+ }
+ }
+ }
+ return features;
+ },
+
+ /**
+ * Method: write
+ * Serialize a feature or array of features into a WKT string.
+ *
+ * Parameters:
+ * features - {<OpenLayers.Feature.Vector>|Array} A feature or array of
+ * features
+ *
+ * Returns:
+ * {String} The WKT string representation of the input geometries
+ */
+ write: function(features) {
+ var collection, geometry, type, data, isCollection;
+ if(features.constructor == Array) {
+ collection = features;
+ isCollection = true;
+ } else {
+ collection = [features];
+ isCollection = false;
+ }
+ var pieces = [];
+ if(isCollection) {
+ pieces.push('GEOMETRYCOLLECTION(');
+ }
+ for(var i=0, len=collection.length; i<len; ++i) {
+ if(isCollection && i>0) {
+ pieces.push(',');
+ }
+ geometry = collection[i].geometry;
+ type = geometry.CLASS_NAME.split('.')[2].toLowerCase();
+ if(!this.extract[type]) {
+ return null;
+ }
+ if (this.internalProjection && this.externalProjection) {
+ geometry = geometry.clone();
+ geometry.transform(this.internalProjection,
+ this.externalProjection);
+ }
+ data = this.extract[type].apply(this, [geometry]);
+ pieces.push(type.toUpperCase() + '(' + data + ')');
+ }
+ if(isCollection) {
+ pieces.push(')');
+ }
+ return pieces.join('');
+ },
+
+ /**
+ * Object with properties corresponding to the geometry types.
+ * Property values are functions that do the actual data extraction.
+ */
+ extract: {
+ /**
+ * Return a space delimited string of point coordinates.
+ * @param {<OpenLayers.Geometry.Point>} point
+ * @returns {String} A string of coordinates representing the point
+ */
+ 'point': function(point) {
+ return point.x + ' ' + point.y;
+ },
+
+ /**
+ * Return a comma delimited string of point coordinates from a multipoint.
+ * @param {<OpenLayers.Geometry.MultiPoint>} multipoint
+ * @returns {String} A string of point coordinate strings representing
+ * the multipoint
+ */
+ 'multipoint': function(multipoint) {
+ var array = [];
+ for(var i=0, len=multipoint.components.length; i<len; ++i) {
+ array.push(this.extract.point.apply(this, [multipoint.components[i]]));
+ }
+ return array.join(',');
+ },
+
+ /**
+ * Return a comma delimited string of point coordinates from a line.
+ * @param {<OpenLayers.Geometry.LineString>} linestring
+ * @returns {String} A string of point coordinate strings representing
+ * the linestring
+ */
+ 'linestring': function(linestring) {
+ var array = [];
+ for(var i=0, len=linestring.components.length; i<len; ++i) {
+ array.push(this.extract.point.apply(this, [linestring.components[i]]));
+ }
+ return array.join(',');
+ },
+
+ /**
+ * Return a comma delimited string of linestring strings from a multilinestring.
+ * @param {<OpenLayers.Geometry.MultiLineString>} multilinestring
+ * @returns {String} A string of of linestring strings representing
+ * the multilinestring
+ */
+ 'multilinestring': function(multilinestring) {
+ var array = [];
+ for(var i=0, len=multilinestring.components.length; i<len; ++i) {
+ array.push('(' +
+ this.extract.linestring.apply(this, [multilinestring.components[i]]) +
+ ')');
+ }
+ return array.join(',');
+ },
+
+ /**
+ * Return a comma delimited string of linear ring arrays from a polygon.
+ * @param {<OpenLayers.Geometry.Polygon>} polygon
+ * @returns {String} An array of linear ring arrays representing the polygon
+ */
+ 'polygon': function(polygon) {
+ var array = [];
+ for(var i=0, len=polygon.components.length; i<len; ++i) {
+ array.push('(' +
+ this.extract.linestring.apply(this, [polygon.components[i]]) +
+ ')');
+ }
+ return array.join(',');
+ },
+
+ /**
+ * Return an array of polygon arrays from a multipolygon.
+ * @param {<OpenLayers.Geometry.MultiPolygon>} multipolygon
+ * @returns {Array} An array of polygon arrays representing
+ * the multipolygon
+ */
+ 'multipolygon': function(multipolygon) {
+ var array = [];
+ for(var i=0, len=multipolygon.components.length; i<len; ++i) {
+ array.push('(' +
+ this.extract.polygon.apply(this, [multipolygon.components[i]]) +
+ ')');
+ }
+ return array.join(',');
+ }
+
+ },
+
+ /**
+ * Object with properties corresponding to the geometry types.
+ * Property values are functions that do the actual parsing.
+ */
+ parse: {
+ /**
+ * Return point feature given a point WKT fragment.
+ * @param {String} str A WKT fragment representing the point
+ * @returns {<OpenLayers.Feature.Vector>} A point feature
+ * @private
+ */
+ 'point': function(str) {
+ var coords = OpenLayers.String.trim(str).split(this.regExes.spaces);
+ return new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(coords[0], coords[1])
+ );
+ },
+
+ /**
+ * Return a multipoint feature given a multipoint WKT fragment.
+ * @param {String} A WKT fragment representing the multipoint
+ * @returns {<OpenLayers.Feature.Vector>} A multipoint feature
+ * @private
+ */
+ 'multipoint': function(str) {
+ var points = OpenLayers.String.trim(str).split(',');
+ var components = [];
+ for(var i=0, len=points.length; i<len; ++i) {
+ components.push(this.parse.point.apply(this, [points[i]]).geometry);
+ }
+ return new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.MultiPoint(components)
+ );
+ },
+
+ /**
+ * Return a linestring feature given a linestring WKT fragment.
+ * @param {String} A WKT fragment representing the linestring
+ * @returns {<OpenLayers.Feature.Vector>} A linestring feature
+ * @private
+ */
+ 'linestring': function(str) {
+ var points = OpenLayers.String.trim(str).split(',');
+ var components = [];
+ for(var i=0, len=points.length; i<len; ++i) {
+ components.push(this.parse.point.apply(this, [points[i]]).geometry);
+ }
+ return new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.LineString(components)
+ );
+ },
+
+ /**
+ * Return a multilinestring feature given a multilinestring WKT fragment.
+ * @param {String} A WKT fragment representing the multilinestring
+ * @returns {<OpenLayers.Feature.Vector>} A multilinestring feature
+ * @private
+ */
+ 'multilinestring': function(str) {
+ var line;
+ var lines = OpenLayers.String.trim(str).split(this.regExes.parenComma);
+ var components = [];
+ for(var i=0, len=lines.length; i<len; ++i) {
+ line = lines[i].replace(this.regExes.trimParens, '$1');
+ components.push(this.parse.linestring.apply(this, [line]).geometry);
+ }
+ return new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.MultiLineString(components)
+ );
+ },
+
+ /**
+ * Return a polygon feature given a polygon WKT fragment.
+ * @param {String} A WKT fragment representing the polygon
+ * @returns {<OpenLayers.Feature.Vector>} A polygon feature
+ * @private
+ */
+ 'polygon': function(str) {
+ var ring, linestring, linearring;
+ var rings = OpenLayers.String.trim(str).split(this.regExes.parenComma);
+ var components = [];
+ for(var i=0, len=rings.length; i<len; ++i) {
+ ring = rings[i].replace(this.regExes.trimParens, '$1');
+ linestring = this.parse.linestring.apply(this, [ring]).geometry;
+ linearring = new OpenLayers.Geometry.LinearRing(linestring.components);
+ components.push(linearring);
+ }
+ return new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Polygon(components)
+ );
+ },
+
+ /**
+ * Return a multipolygon feature given a multipolygon WKT fragment.
+ * @param {String} A WKT fragment representing the multipolygon
+ * @returns {<OpenLayers.Feature.Vector>} A multipolygon feature
+ * @private
+ */
+ 'multipolygon': function(str) {
+ var polygon;
+ var polygons = OpenLayers.String.trim(str).split(this.regExes.doubleParenComma);
+ var components = [];
+ for(var i=0, len=polygons.length; i<len; ++i) {
+ polygon = polygons[i].replace(this.regExes.trimParens, '$1');
+ components.push(this.parse.polygon.apply(this, [polygon]).geometry);
+ }
+ return new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.MultiPolygon(components)
+ );
+ },
+
+ /**
+ * Return an array of features given a geometrycollection WKT fragment.
+ * @param {String} A WKT fragment representing the geometrycollection
+ * @returns {Array} An array of OpenLayers.Feature.Vector
+ * @private
+ */
+ 'geometrycollection': function(str) {
+ // separate components of the collection with |
+ str = str.replace(/,\s*([A-Za-z])/g, '|$1');
+ var wktArray = OpenLayers.String.trim(str).split('|');
+ var components = [];
+ for(var i=0, len=wktArray.length; i<len; ++i) {
+ components.push(OpenLayers.Format.WKT.prototype.read.apply(this,[wktArray[i]]));
+ }
+ return components;
+ }
+
+ },
+
+ CLASS_NAME: "OpenLayers.Format.WKT"
+});
+/* ======================================================================
+ OpenLayers/Layer/Boxes.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/Layer.js
+ * @requires OpenLayers/Layer/Markers.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.Boxes
+ * Draw divs as 'boxes' on the layer.
+ *
+ * Inherits from:
+ * - <OpenLayers.Layer.Markers>
+ */
+OpenLayers.Layer.Boxes = OpenLayers.Class(OpenLayers.Layer.Markers, {
+
+ /**
+ * Constructor: OpenLayers.Layer.Boxes
+ *
+ * Parameters:
+ * name - {String}
+ * options - {Object} Hashtable of extra options to tag onto the layer
+ */
+ initialize: function (name, options) {
+ OpenLayers.Layer.Markers.prototype.initialize.apply(this, arguments);
+ },
+
+ /**
+ * Method: drawMarker
+ * Calculate the pixel location for the marker, create it, and
+ * add it to the layer's div
+ *
+ * Parameters:
+ * marker - {<OpenLayers.Marker.Box>}
+ */
+ drawMarker: function(marker) {
+ var bounds = marker.bounds;
+ var topleft = this.map.getLayerPxFromLonLat(
+ new OpenLayers.LonLat(bounds.left, bounds.top));
+ var botright = this.map.getLayerPxFromLonLat(
+ new OpenLayers.LonLat(bounds.right, bounds.bottom));
+ if (botright == null || topleft == null) {
+ marker.display(false);
+ } else {
+ var sz = new OpenLayers.Size(
+ Math.max(1, botright.x - topleft.x),
+ Math.max(1, botright.y - topleft.y));
+ var markerDiv = marker.draw(topleft, sz);
+ if (!marker.drawn) {
+ this.div.appendChild(markerDiv);
+ marker.drawn = true;
+ }
+ }
+ },
+
+
+ /**
+ * APIMethod: removeMarker
+ *
+ * Parameters:
+ * marker - {<OpenLayers.Marker.Box>}
+ */
+ removeMarker: function(marker) {
+ OpenLayers.Util.removeItem(this.markers, marker);
+ if ((marker.div != null) &&
+ (marker.div.parentNode == this.div) ) {
+ this.div.removeChild(marker.div);
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Layer.Boxes"
+});
+/* ======================================================================
+ OpenLayers/Layer/GeoRSS.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/Layer/Markers.js
+ * @requires OpenLayers/Request/XMLHttpRequest.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.GeoRSS
+ * Add GeoRSS Point features to your map.
+ *
+ * Inherits from:
+ * - <OpenLayers.Layer.Markers>
+ * - <OpenLayers.Layer>
+ */
+OpenLayers.Layer.GeoRSS = OpenLayers.Class(OpenLayers.Layer.Markers, {
+
+ /**
+ * Property: location
+ * {String} store url of text file
+ */
+ location: null,
+
+ /**
+ * Property: features
+ * {Array(<OpenLayers.Feature>)}
+ */
+ features: null,
+
+ /**
+ * APIProperty: formatOptions
+ * {Object} Hash of options which should be passed to the format when it is
+ * created. Must be passed in the constructor.
+ */
+ formatOptions: null,
+
+ /**
+ * Property: selectedFeature
+ * {<OpenLayers.Feature>}
+ */
+ selectedFeature: null,
+
+ /**
+ * APIProperty: icon
+ * {<OpenLayers.Icon>}. This determines the Icon to be used on the map
+ * for this GeoRSS layer.
+ */
+ icon: null,
+
+ /**
+ * APIProperty: popupSize
+ * {<OpenLayers.Size>} This determines the size of GeoRSS popups. If
+ * not provided, defaults to 250px by 120px.
+ */
+ popupSize: null,
+
+ /**
+ * APIProperty: useFeedTitle
+ * {Boolean} Set layer.name to the first <title> element in the feed. Default is true.
+ */
+ useFeedTitle: true,
+
+ /**
+ * Constructor: OpenLayers.Layer.GeoRSS
+ * Create a GeoRSS Layer.
+ *
+ * Parameters:
+ * name - {String}
+ * location - {String}
+ * options - {Object}
+ */
+ initialize: function(name, location, options) {
+ OpenLayers.Layer.Markers.prototype.initialize.apply(this, [name, options]);
+ this.location = location;
+ this.features = [];
+ },
+
+ /**
+ * Method: destroy
+ */
+ destroy: function() {
+ // Warning: Layer.Markers.destroy() must be called prior to calling
+ // clearFeatures() here, otherwise we leak memory. Indeed, if
+ // Layer.Markers.destroy() is called after clearFeatures(), it won't be
+ // able to remove the marker image elements from the layer's div since
+ // the markers will have been destroyed by clearFeatures().
+ OpenLayers.Layer.Markers.prototype.destroy.apply(this, arguments);
+ this.clearFeatures();
+ this.features = null;
+ },
+
+ /**
+ * Method: loadRSS
+ * Start the load of the RSS data. Don't do this when we first add the layer,
+ * since we may not be visible at any point, and it would therefore be a waste.
+ */
+ loadRSS: function() {
+ if (!this.loaded) {
+ this.events.triggerEvent("loadstart");
+ OpenLayers.Request.GET({
+ url: this.location,
+ success: this.parseData,
+ scope: this
+ });
+ this.loaded = true;
+ }
+ },
+
+ /**
+ * Method: moveTo
+ * If layer is visible and RSS has not been loaded, load RSS.
+ *
+ * Parameters:
+ * bounds - {Object}
+ * zoomChanged - {Object}
+ * minor - {Object}
+ */
+ moveTo:function(bounds, zoomChanged, minor) {
+ OpenLayers.Layer.Markers.prototype.moveTo.apply(this, arguments);
+ if(this.visibility && !this.loaded){
+ this.loadRSS();
+ }
+ },
+
+ /**
+ * Method: parseData
+ * Parse the data returned from the Events call.
+ *
+ * Parameters:
+ * ajaxRequest - {<OpenLayers.Request.XMLHttpRequest>}
+ */
+ parseData: function(ajaxRequest) {
+ var doc = ajaxRequest.responseXML;
+ if (!doc || !doc.documentElement) {
+ doc = OpenLayers.Format.XML.prototype.read(ajaxRequest.responseText);
+ }
+
+ if (this.useFeedTitle) {
+ var name = null;
+ try {
+ name = doc.getElementsByTagNameNS('*', 'title')[0].firstChild.nodeValue;
+ }
+ catch (e) {
+ name = doc.getElementsByTagName('title')[0].firstChild.nodeValue;
+ }
+ if (name) {
+ this.setName(name);
+ }
+ }
+
+ var options = {};
+
+ OpenLayers.Util.extend(options, this.formatOptions);
+
+ if (this.map && !this.projection.equals(this.map.getProjectionObject())) {
+ options.externalProjection = this.projection;
+ options.internalProjection = this.map.getProjectionObject();
+ }
+
+ var format = new OpenLayers.Format.GeoRSS(options);
+ var features = format.read(doc);
+
+ for (var i=0, len=features.length; i<len; i++) {
+ var data = {};
+ var feature = features[i];
+
+ // we don't support features with no geometry in the GeoRSS
+ // layer at this time.
+ if (!feature.geometry) {
+ continue;
+ }
+
+ var title = feature.attributes.title ?
+ feature.attributes.title : "Untitled";
+
+ var description = feature.attributes.description ?
+ feature.attributes.description : "No description.";
+
+ var link = feature.attributes.link ? feature.attributes.link : "";
+
+ var location = feature.geometry.getBounds().getCenterLonLat();
+
+
+ data.icon = this.icon == null ?
+ OpenLayers.Marker.defaultIcon() :
+ this.icon.clone();
+
+ data.popupSize = this.popupSize ?
+ this.popupSize.clone() :
+ new OpenLayers.Size(250, 120);
+
+ if (title || description) {
+ // we have supplemental data, store them.
+ data.title = title;
+ data.description = description;
+
+ var contentHTML = '<div class="olLayerGeoRSSClose">[x]</div>';
+ contentHTML += '<div class="olLayerGeoRSSTitle">';
+ if (link) {
+ contentHTML += '<a class="link" href="'+link+'" target="_blank">';
+ }
+ contentHTML += title;
+ if (link) {
+ contentHTML += '</a>';
+ }
+ contentHTML += '</div>';
+ contentHTML += '<div style="" class="olLayerGeoRSSDescription">';
+ contentHTML += description;
+ contentHTML += '</div>';
+ data['popupContentHTML'] = contentHTML;
+ }
+ var feature = new OpenLayers.Feature(this, location, data);
+ this.features.push(feature);
+ var marker = feature.createMarker();
+ marker.events.register('click', feature, this.markerClick);
+ this.addMarker(marker);
+ }
+ this.events.triggerEvent("loadend");
+ },
+
+ /**
+ * Method: markerClick
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ markerClick: function(evt) {
+ var sameMarkerClicked = (this == this.layer.selectedFeature);
+ this.layer.selectedFeature = (!sameMarkerClicked) ? this : null;
+ for(var i=0, len=this.layer.map.popups.length; i<len; i++) {
+ this.layer.map.removePopup(this.layer.map.popups[i]);
+ }
+ if (!sameMarkerClicked) {
+ var popup = this.createPopup();
+ OpenLayers.Event.observe(popup.div, "click",
+ OpenLayers.Function.bind(function() {
+ for(var i=0, len=this.layer.map.popups.length; i<len; i++) {
+ this.layer.map.removePopup(this.layer.map.popups[i]);
+ }
+ }, this)
+ );
+ this.layer.map.addPopup(popup);
+ }
+ OpenLayers.Event.stop(evt);
+ },
+
+ /**
+ * Method: clearFeatures
+ * Destroy all features in this layer.
+ */
+ clearFeatures: function() {
+ if (this.features != null) {
+ while(this.features.length > 0) {
+ var feature = this.features[0];
+ OpenLayers.Util.removeItem(this.features, feature);
+ feature.destroy();
+ }
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Layer.GeoRSS"
+});
+/* ======================================================================
+ OpenLayers/Layer/Google.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/Layer/SphericalMercator.js
+ * @requires OpenLayers/Layer/EventPane.js
+ * @requires OpenLayers/Layer/FixedZoomLevels.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.Google
+ *
+ * Inherits from:
+ * - <OpenLayers.Layer.SphericalMercator>
+ * - <OpenLayers.Layer.EventPane>
+ * - <OpenLayers.Layer.FixedZoomLevels>
+ */
+OpenLayers.Layer.Google = OpenLayers.Class(
+ OpenLayers.Layer.EventPane,
+ OpenLayers.Layer.FixedZoomLevels, {
+
+ /**
+ * Constant: MIN_ZOOM_LEVEL
+ * {Integer} 0
+ */
+ MIN_ZOOM_LEVEL: 0,
+
+ /**
+ * Constant: MAX_ZOOM_LEVEL
+ * {Integer} 19
+ */
+ MAX_ZOOM_LEVEL: 19,
+
+ /**
+ * Constant: RESOLUTIONS
+ * {Array(Float)} Hardcode these resolutions so that they are more closely
+ * tied with the standard wms projection
+ */
+ RESOLUTIONS: [
+ 1.40625,
+ 0.703125,
+ 0.3515625,
+ 0.17578125,
+ 0.087890625,
+ 0.0439453125,
+ 0.02197265625,
+ 0.010986328125,
+ 0.0054931640625,
+ 0.00274658203125,
+ 0.001373291015625,
+ 0.0006866455078125,
+ 0.00034332275390625,
+ 0.000171661376953125,
+ 0.0000858306884765625,
+ 0.00004291534423828125,
+ 0.00002145767211914062,
+ 0.00001072883605957031,
+ 0.00000536441802978515,
+ 0.00000268220901489257
+ ],
+
+ /**
+ * APIProperty: type
+ * {GMapType}
+ */
+ type: null,
+
+ /**
+ * APIProperty: sphericalMercator
+ * {Boolean} Should the map act as a mercator-projected map? This will
+ * cause all interactions with the map to be in the actual map
+ * projection, which allows support for vector drawing, overlaying
+ * other maps, etc.
+ */
+ sphericalMercator: false,
+
+ /**
+ * Property: dragObject
+ * {GDraggableObject} Since 2.93, Google has exposed the ability to get
+ * the maps GDraggableObject. We can now use this for smooth panning
+ */
+ dragObject: null,
+
+ /**
+ * Constructor: OpenLayers.Layer.Google
+ *
+ * Parameters:
+ * name - {String} A name for the layer.
+ * options - {Object} An optional object whose properties will be set
+ * on the layer.
+ */
+ initialize: function(name, options) {
+ OpenLayers.Layer.EventPane.prototype.initialize.apply(this, arguments);
+ OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this,
+ arguments);
+ this.addContainerPxFunction();
+ if (this.sphericalMercator) {
+ OpenLayers.Util.extend(this, OpenLayers.Layer.SphericalMercator);
+ this.initMercatorParameters();
+ }
+ },
+
+ /**
+ * Method: loadMapObject
+ * Load the GMap and register appropriate event listeners. If we can't
+ * load GMap2, then display a warning message.
+ */
+ loadMapObject:function() {
+
+ //has gmaps library has been loaded?
+ try {
+ // create GMap, hide nav controls
+ this.mapObject = new GMap2( this.div );
+
+ //since v 2.93 getDragObject is now available.
+ if(typeof this.mapObject.getDragObject == "function") {
+ this.dragObject = this.mapObject.getDragObject();
+ } else {
+ this.dragPanMapObject = null;
+ }
+
+
+ // move the ToS and branding stuff up to the pane
+ // thanks a *mil* Erik for thinking of this
+ var poweredBy = this.div.lastChild;
+ this.div.removeChild(poweredBy);
+ this.pane.appendChild(poweredBy);
+ poweredBy.className = "olLayerGooglePoweredBy gmnoprint";
+ poweredBy.style.left = "";
+ poweredBy.style.bottom = "";
+
+ var termsOfUse = this.div.lastChild;
+ this.div.removeChild(termsOfUse);
+ this.pane.appendChild(termsOfUse);
+ termsOfUse.className = "olLayerGoogleCopyright";
+ termsOfUse.style.right = "";
+ termsOfUse.style.bottom = "";
+
+ } catch (e) {
+ OpenLayers.Console.error(e);
+ }
+
+ },
+
+ /**
+ * APIMethod: setMap
+ * Overridden from EventPane because if a map type has been specified,
+ * we need to attach a listener for the first moveend -- this is how
+ * we will know that the map has been centered. Only once the map has
+ * been centered is it safe to change the gmap object's map type.
+ *
+ * Parameters:
+ * map - {<OpenLayers.Map>}
+ */
+ setMap: function(map) {
+ OpenLayers.Layer.EventPane.prototype.setMap.apply(this, arguments);
+
+ if (this.type != null) {
+ this.map.events.register("moveend", this, this.setMapType);
+ }
+ },
+
+ /**
+ * Method: setMapType
+ * The map has been centered, and a map type was specified, so we
+ * set the map type on the gmap object, then unregister the listener
+ * so that we dont keep doing this every time the map moves.
+ */
+ setMapType: function() {
+ if (this.mapObject.getCenter() != null) {
+
+ // Support for custom map types.
+ if (OpenLayers.Util.indexOf(this.mapObject.getMapTypes(),
+ this.type) == -1) {
+ this.mapObject.addMapType(this.type);
+ }
+
+ this.mapObject.setMapType(this.type);
+ this.map.events.unregister("moveend", this, this.setMapType);
+ }
+ },
+
+ /**
+ * APIMethod: onMapResize
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ onMapResize: function() {
+ if(this.visibility) {
+ this.mapObject.checkResize();
+ } else {
+ this.windowResized = true;
+ }
+ },
+
+ /**
+ * Method: display
+ * Hide or show the layer
+ *
+ * Parameters:
+ * display - {Boolean}
+ */
+ display: function(display) {
+ OpenLayers.Layer.EventPane.prototype.display.apply(this, arguments);
+ if(this.div.style.display == "block" && this.windowResized) {
+ this.mapObject.checkResize();
+ this.windowResized = false;
+ }
+ },
+
+ /**
+ * APIMethod: getZoomForExtent
+ *
+ * Parameters:
+ * bounds - {<OpenLayers.Bounds>}
+ *
+ * Returns:
+ * {Integer} Corresponding zoom level for a specified Bounds.
+ * If mapObject is not loaded or not centered, returns null
+ *
+ getZoomForExtent: function (bounds) {
+ var zoom = null;
+ if (this.mapObject != null) {
+ var moBounds = this.getMapObjectBoundsFromOLBounds(bounds);
+ var moZoom = this.getMapObjectZoomFromMapObjectBounds(moBounds);
+
+ //make sure zoom is within bounds
+ var moZoom = Math.min(Math.max(moZoom, this.minZoomLevel),
+ this.maxZoomLevel);
+
+ zoom = this.getOLZoomFromMapObjectZoom(moZoom);
+ }
+ return zoom;
+ },
+
+ */
+
+ //
+ // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds
+ //
+
+ /**
+ * APIMethod: getOLBoundsFromMapObjectBounds
+ *
+ * Parameters:
+ * moBounds - {Object}
+ *
+ * Returns:
+ * {<OpenLayers.Bounds>} An <OpenLayers.Bounds>, translated from the
+ * passed-in MapObject Bounds.
+ * Returns null if null value is passed in.
+ */
+ getOLBoundsFromMapObjectBounds: function(moBounds) {
+ var olBounds = null;
+ if (moBounds != null) {
+ var sw = moBounds.getSouthWest();
+ var ne = moBounds.getNorthEast();
+ if (this.sphericalMercator) {
+ sw = this.forwardMercator(sw.lng(), sw.lat());
+ ne = this.forwardMercator(ne.lng(), ne.lat());
+ } else {
+ sw = new OpenLayers.LonLat(sw.lng(), sw.lat());
+ ne = new OpenLayers.LonLat(ne.lng(), ne.lat());
+ }
+ olBounds = new OpenLayers.Bounds(sw.lon,
+ sw.lat,
+ ne.lon,
+ ne.lat );
+ }
+ return olBounds;
+ },
+
+ /**
+ * APIMethod: getMapObjectBoundsFromOLBounds
+ *
+ * Parameters:
+ * olBounds - {<OpenLayers.Bounds>}
+ *
+ * Returns:
+ * {Object} A MapObject Bounds, translated from olBounds
+ * Returns null if null value is passed in
+ */
+ getMapObjectBoundsFromOLBounds: function(olBounds) {
+ var moBounds = null;
+ if (olBounds != null) {
+ var sw = this.sphericalMercator ?
+ this.inverseMercator(olBounds.bottom, olBounds.left) :
+ new OpenLayers.LonLat(olBounds.bottom, olBounds.left);
+ var ne = this.sphericalMercator ?
+ this.inverseMercator(olBounds.top, olBounds.right) :
+ new OpenLayers.LonLat(olBounds.top, olBounds.right);
+ moBounds = new GLatLngBounds(new GLatLng(sw.lat, sw.lon),
+ new GLatLng(ne.lat, ne.lon));
+ }
+ return moBounds;
+ },
+
+ /**
+ * Method: addContainerPxFunction
+ * Hack-on function because GMAPS does not give it to us
+ *
+ * Parameters:
+ * gLatLng - {GLatLng}
+ *
+ * Returns:
+ * {GPoint} A GPoint specifying gLatLng translated into "Container" coords
+ */
+ addContainerPxFunction: function() {
+ if ( (typeof GMap2 != "undefined") &&
+ !GMap2.prototype.fromLatLngToContainerPixel) {
+
+ GMap2.prototype.fromLatLngToContainerPixel = function(gLatLng) {
+
+ // first we translate into "DivPixel"
+ var gPoint = this.fromLatLngToDivPixel(gLatLng);
+
+ // locate the sliding "Div" div
+ var div = this.getContainer().firstChild.firstChild;
+
+ // adjust by the offset of "Div" and voila!
+ gPoint.x += div.offsetLeft;
+ gPoint.y += div.offsetTop;
+
+ return gPoint;
+ };
+ }
+ },
+
+ /**
+ * APIMethod: getWarningHTML
+ *
+ * Returns:
+ * {String} String with information on why layer is broken, how to get
+ * it working.
+ */
+ getWarningHTML:function() {
+ return OpenLayers.i18n("googleWarning");
+ },
+
+
+ /************************************
+ * *
+ * MapObject Interface Controls *
+ * *
+ ************************************/
+
+
+ // Get&Set Center, Zoom
+
+ /**
+ * APIMethod: setMapObjectCenter
+ * Set the mapObject to the specified center and zoom
+ *
+ * Parameters:
+ * center - {Object} MapObject LonLat format
+ * zoom - {int} MapObject zoom format
+ */
+ setMapObjectCenter: function(center, zoom) {
+ this.mapObject.setCenter(center, zoom);
+ },
+
+ /**
+ * APIMethod: dragPanMapObject
+ *
+ * Parameters:
+ * dX - {Integer}
+ * dY - {Integer}
+ */
+ dragPanMapObject: function(dX, dY) {
+ this.dragObject.moveBy(new GSize(-dX, dY));
+ },
+
+ /**
+ * APIMethod: getMapObjectCenter
+ *
+ * Returns:
+ * {Object} The mapObject's current center in Map Object format
+ */
+ getMapObjectCenter: function() {
+ return this.mapObject.getCenter();
+ },
+
+ /**
+ * APIMethod: getMapObjectZoom
+ *
+ * Returns:
+ * {Integer} The mapObject's current zoom, in Map Object format
+ */
+ getMapObjectZoom: function() {
+ return this.mapObject.getZoom();
+ },
+
+
+ // LonLat - Pixel Translation
+
+ /**
+ * APIMethod: getMapObjectLonLatFromMapObjectPixel
+ *
+ * Parameters:
+ * moPixel - {Object} MapObject Pixel format
+ *
+ * Returns:
+ * {Object} MapObject LonLat translated from MapObject Pixel
+ */
+ getMapObjectLonLatFromMapObjectPixel: function(moPixel) {
+ return this.mapObject.fromContainerPixelToLatLng(moPixel);
+ },
+
+ /**
+ * APIMethod: getMapObjectPixelFromMapObjectLonLat
+ *
+ * Parameters:
+ * moLonLat - {Object} MapObject LonLat format
+ *
+ * Returns:
+ * {Object} MapObject Pixel transtlated from MapObject LonLat
+ */
+ getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {
+ return this.mapObject.fromLatLngToContainerPixel(moLonLat);
+ },
+
+
+ // Bounds
+
+ /**
+ * APIMethod: getMapObjectZoomFromMapObjectBounds
+ *
+ * Parameters:
+ * moBounds - {Object} MapObject Bounds format
+ *
+ * Returns:
+ * {Object} MapObject Zoom for specified MapObject Bounds
+ */
+ getMapObjectZoomFromMapObjectBounds: function(moBounds) {
+ return this.mapObject.getBoundsZoomLevel(moBounds);
+ },
+
+ /************************************
+ * *
+ * MapObject Primitives *
+ * *
+ ************************************/
+
+
+ // LonLat
+
+ /**
+ * APIMethod: getLongitudeFromMapObjectLonLat
+ *
+ * Parameters:
+ * moLonLat - {Object} MapObject LonLat format
+ *
+ * Returns:
+ * {Float} Longitude of the given MapObject LonLat
+ */
+ getLongitudeFromMapObjectLonLat: function(moLonLat) {
+ return this.sphericalMercator ?
+ this.forwardMercator(moLonLat.lng(), moLonLat.lat()).lon :
+ moLonLat.lng();
+ },
+
+ /**
+ * APIMethod: getLatitudeFromMapObjectLonLat
+ *
+ * Parameters:
+ * moLonLat - {Object} MapObject LonLat format
+ *
+ * Returns:
+ * {Float} Latitude of the given MapObject LonLat
+ */
+ getLatitudeFromMapObjectLonLat: function(moLonLat) {
+ var lat = this.sphericalMercator ?
+ this.forwardMercator(moLonLat.lng(), moLonLat.lat()).lat :
+ moLonLat.lat();
+ return lat;
+ },
+
+ /**
+ * APIMethod: getMapObjectLonLatFromLonLat
+ *
+ * Parameters:
+ * lon - {Float}
+ * lat - {Float}
+ *
+ * Returns:
+ * {Object} MapObject LonLat built from lon and lat params
+ */
+ getMapObjectLonLatFromLonLat: function(lon, lat) {
+ var gLatLng;
+ if(this.sphericalMercator) {
+ var lonlat = this.inverseMercator(lon, lat);
+ gLatLng = new GLatLng(lonlat.lat, lonlat.lon);
+ } else {
+ gLatLng = new GLatLng(lat, lon);
+ }
+ return gLatLng;
+ },
+
+ // Pixel
+
+ /**
+ * APIMethod: getXFromMapObjectPixel
+ *
+ * Parameters:
+ * moPixel - {Object} MapObject Pixel format
+ *
+ * Returns:
+ * {Integer} X value of the MapObject Pixel
+ */
+ getXFromMapObjectPixel: function(moPixel) {
+ return moPixel.x;
+ },
+
+ /**
+ * APIMethod: getYFromMapObjectPixel
+ *
+ * Parameters:
+ * moPixel - {Object} MapObject Pixel format
+ *
+ * Returns:
+ * {Integer} Y value of the MapObject Pixel
+ */
+ getYFromMapObjectPixel: function(moPixel) {
+ return moPixel.y;
+ },
+
+ /**
+ * APIMethod: getMapObjectPixelFromXY
+ *
+ * Parameters:
+ * x - {Integer}
+ * y - {Integer}
+ *
+ * Returns:
+ * {Object} MapObject Pixel from x and y parameters
+ */
+ getMapObjectPixelFromXY: function(x, y) {
+ return new GPoint(x, y);
+ },
+
+ CLASS_NAME: "OpenLayers.Layer.Google"
+});
+/* ======================================================================
OpenLayers/Layer/Grid.js
====================================================================== */
@@ -14037,9 +31891,9 @@
*/
clearGrid:function() {
if (this.grid) {
- for(var iRow=0; iRow < this.grid.length; iRow++) {
+ for(var iRow=0, len=this.grid.length; iRow<len; iRow++) {
var row = this.grid[iRow];
- for(var iCol=0; iCol < row.length; iCol++) {
+ for(var iCol=0, clen=row.length; iCol<clen; iCol++) {
var tile = row[iCol];
this.removeTileMonitoringHooks(tile);
tile.destroy();
@@ -14289,7 +32143,7 @@
var minCols = Math.ceil(viewSize.w/this.tileSize.w) +
Math.max(1, 2 * this.buffer);
- var extent = this.map.getMaxExtent();
+ var extent = this.maxExtent;
var resolution = this.map.getResolution();
var tileLayout = this.calculateGridLayout(bounds, extent, resolution);
@@ -14430,7 +32284,7 @@
}
// now we go through and draw the tiles in forward order
- for(var i=0; i < tileQueue.length; i++) {
+ for(var i=0, len=tileQueue.length; i<len; i++) {
var tile = tileQueue[i];
tile.draw();
//mark tile as unqueued for the next time (since tiles are reused)
@@ -14549,7 +32403,7 @@
var row = (prepend) ? grid.pop() : grid.shift();
- for (var i=0; i < modelRow.length; i++) {
+ for (var i=0, len=modelRow.length; i<len; i++) {
var modelTile = modelRow[i];
var bounds = modelTile.bounds.clone();
var position = modelTile.position.clone();
@@ -14579,7 +32433,7 @@
var resolution = this.map.getResolution();
var deltaLon = resolution * deltaX;
- for (var i=0; i<this.grid.length; i++) {
+ for (var i=0, len=this.grid.length; i<len; i++) {
var row = this.grid[i];
var modelTileIndex = (prepend) ? 0 : (row.length - 1);
var modelTile = row[modelTileIndex];
@@ -14655,7 +32509,7 @@
* {<OpenLayers.Bounds>} Bounds of the tile at the given pixel location.
*/
getTileBounds: function(viewPortPx) {
- var maxExtent = this.map.getMaxExtent();
+ var maxExtent = this.maxExtent;
var resolution = this.getResolution();
var tileMapWidth = resolution * this.tileSize.w;
var tileMapHeight = resolution * this.tileSize.h;
@@ -14676,6 +32530,2379 @@
CLASS_NAME: "OpenLayers.Layer.Grid"
});
/* ======================================================================
+ OpenLayers/Layer/MultiMap.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/Layer/EventPane.js
+ * @requires OpenLayers/Layer/FixedZoomLevels.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.MultiMap
+ * Note that MultiMap does not fully support the sphericalMercator
+ * option. See Ticket #953 for more details.
+ *
+ * Inherits from:
+ * - <OpenLayers.Layer.EventPane>
+ * - <OpenLayers.Layer.FixedZoomLevels>
+ */
+OpenLayers.Layer.MultiMap = OpenLayers.Class(
+ OpenLayers.Layer.EventPane, OpenLayers.Layer.FixedZoomLevels, {
+
+ /**
+ * Constant: MIN_ZOOM_LEVEL
+ * {Integer} 1
+ */
+ MIN_ZOOM_LEVEL: 1,
+
+ /**
+ * Constant: MAX_ZOOM_LEVEL
+ * {Integer} 17
+ */
+ MAX_ZOOM_LEVEL: 17,
+
+ /**
+ * Constant: RESOLUTIONS
+ * {Array(Float)} Hardcode these resolutions so that they are more closely
+ * tied with the standard wms projection
+ */
+ RESOLUTIONS: [
+ 9,
+ 1.40625,
+ 0.703125,
+ 0.3515625,
+ 0.17578125,
+ 0.087890625,
+ 0.0439453125,
+ 0.02197265625,
+ 0.010986328125,
+ 0.0054931640625,
+ 0.00274658203125,
+ 0.001373291015625,
+ 0.0006866455078125,
+ 0.00034332275390625,
+ 0.000171661376953125,
+ 0.0000858306884765625,
+ 0.00004291534423828125
+ ],
+
+ /**
+ * APIProperty: type
+ * {?}
+ */
+ type: null,
+
+ /**
+ * Constructor: OpenLayers.Layer.MultiMap
+ *
+ * Parameters:
+ * name - {String}
+ * options - {Object}
+ */
+ initialize: function(name, options) {
+ OpenLayers.Layer.EventPane.prototype.initialize.apply(this, arguments);
+ OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this,
+ arguments);
+ if (this.sphericalMercator) {
+ OpenLayers.Util.extend(this, OpenLayers.Layer.SphericalMercator);
+ this.initMercatorParameters();
+ this.RESOLUTIONS.unshift(10);
+ }
+ },
+
+ /**
+ * Method: loadMapObject
+ */
+ loadMapObject:function() {
+ try { //crash proofing
+ this.mapObject = new MultimapViewer(this.div);
+ } catch (e) { }
+ },
+
+ /**
+ * APIMethod: getWarningHTML
+ *
+ * Returns:
+ * {String} String with information on why layer is broken, how to get
+ * it working.
+ */
+ getWarningHTML:function() {
+ return OpenLayers.i18n(
+ "getLayerWarning", {'layerType':"MM", 'layerLib':"MultiMap"}
+ );
+ },
+
+
+
+ /************************************
+ * *
+ * MapObject Interface Controls *
+ * *
+ ************************************/
+
+
+ // Get&Set Center, Zoom
+
+ /**
+ * APIMethod: setMapObjectCenter
+ * Set the mapObject to the specified center and zoom
+ *
+ * Parameters:
+ * center - {Object} MapObject LonLat format
+ * zoom - {int} MapObject zoom format
+ */
+ setMapObjectCenter: function(center, zoom) {
+ this.mapObject.goToPosition(center, zoom);
+ },
+
+ /**
+ * APIMethod: getMapObjectCenter
+ *
+ * Returns:
+ * {Object} The mapObject's current center in Map Object format
+ */
+ getMapObjectCenter: function() {
+ return this.mapObject.getCurrentPosition();
+ },
+
+ /**
+ * APIMethod: getMapObjectZoom
+ *
+ * Returns:
+ * {Integer} The mapObject's current zoom, in Map Object format
+ */
+ getMapObjectZoom: function() {
+ return this.mapObject.getZoomFactor();
+ },
+
+
+ // LonLat - Pixel Translation
+
+ /**
+ * APIMethod: getMapObjectLonLatFromMapObjectPixel
+ *
+ * Parameters:
+ * moPixel - {Object} MapObject Pixel format
+ *
+ * Returns:
+ * {Object} MapObject LonLat translated from MapObject Pixel
+ */
+ getMapObjectLonLatFromMapObjectPixel: function(moPixel) {
+ moPixel.x = moPixel.x - (this.map.getSize().w/2);
+ moPixel.y = moPixel.y - (this.map.getSize().h/2);
+ return this.mapObject.getMapPositionAt(moPixel);
+ },
+
+ /**
+ * APIMethod: getMapObjectPixelFromMapObjectLonLat
+ *
+ * Parameters:
+ * moLonLat - {Object} MapObject LonLat format
+ *
+ * Returns:
+ * {Object} MapObject Pixel transtlated from MapObject LonLat
+ */
+ getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {
+ return this.mapObject.geoPosToContainerPixels(moLonLat);
+ },
+
+
+ /************************************
+ * *
+ * MapObject Primitives *
+ * *
+ ************************************/
+
+
+ // LonLat
+
+ /**
+ * APIMethod: getLongitudeFromMapObjectLonLat
+ *
+ * Parameters:
+ * moLonLat - {Object} MapObject LonLat format
+ *
+ * Returns:
+ * {Float} Longitude of the given MapObject LonLat
+ */
+ getLongitudeFromMapObjectLonLat: function(moLonLat) {
+ return this.sphericalMercator ?
+ this.forwardMercator(moLonLat.lon, moLonLat.lat).lon :
+ moLonLat.lon;
+ },
+
+ /**
+ * APIMethod: getLatitudeFromMapObjectLonLat
+ *
+ * Parameters:
+ * moLonLat - {Object} MapObject LonLat format
+ *
+ * Returns:
+ * {Float} Latitude of the given MapObject LonLat
+ */
+ getLatitudeFromMapObjectLonLat: function(moLonLat) {
+ return this.sphericalMercator ?
+ this.forwardMercator(moLonLat.lon, moLonLat.lat).lat :
+ moLonLat.lat;
+ },
+
+ /**
+ * APIMethod: getMapObjectLonLatFromLonLat
+ *
+ * Parameters:
+ * lon - {Float}
+ * lat - {Float}
+ *
+ * Returns:
+ * {Object} MapObject LonLat built from lon and lat params
+ */
+ getMapObjectLonLatFromLonLat: function(lon, lat) {
+ var mmLatLon;
+ if(this.sphericalMercator) {
+ var lonlat = this.inverseMercator(lon, lat);
+ mmLatLon = new MMLatLon(lonlat.lat, lonlat.lon);
+ } else {
+ mmLatLon = new MMLatLon(lat, lon);
+ }
+ return mmLatLon;
+ },
+
+ // Pixel
+
+ /**
+ * APIMethod: getXFromMapObjectPixel
+ *
+ * Parameters:
+ * moPixel - {Object} MapObject Pixel format
+ *
+ * Returns:
+ * {Integer} X value of the MapObject Pixel
+ */
+ getXFromMapObjectPixel: function(moPixel) {
+ return moPixel.x;
+ },
+
+ /**
+ * APIMethod: getYFromMapObjectPixel
+ *
+ * Parameters:
+ * moPixel - {Object} MapObject Pixel format
+ *
+ * Returns:
+ * {Integer} Y value of the MapObject Pixel
+ */
+ getYFromMapObjectPixel: function(moPixel) {
+ return moPixel.y;
+ },
+
+ /**
+ * APIMethod: getMapObjectPixelFromXY
+ *
+ * Parameters:
+ * x - {Integer}
+ * y - {Integer}
+ *
+ * Returns:
+ * {Object} MapObject Pixel from x and y parameters
+ */
+ getMapObjectPixelFromXY: function(x, y) {
+ return new MMPoint(x, y);
+ },
+
+ CLASS_NAME: "OpenLayers.Layer.MultiMap"
+});
+/* ======================================================================
+ OpenLayers/Layer/Text.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/Layer/Markers.js
+ * @requires OpenLayers/Request/XMLHttpRequest.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.Text
+ * This layer creates markers given data in a text file. The <location>
+ * property of the layer (specified as a property of the options argument
+ * in the <OpenLayers.Layer.Text> constructor) points to a tab delimited
+ * file with data used to create markers.
+ *
+ * The first row of the data file should be a header line with the column names
+ * of the data. Each column should be delimited by a tab space. The
+ * possible columns are:
+ * - *point* lat,lon of the point where a marker is to be placed
+ * - *lat* Latitude of the point where a marker is to be placed
+ * - *lon* Longitude of the point where a marker is to be placed
+ * - *icon* or *image* URL of marker icon to use.
+ * - *iconSize* Size of Icon to use.
+ * - *iconOffset* Where the top-left corner of the icon is to be placed
+ * relative to the latitude and longitude of the point.
+ * - *title* The text of the 'title' is placed inside an 'h2' marker
+ * inside a popup, which opens when the marker is clicked.
+ * - *description* The text of the 'description' is placed below the h2
+ * in the popup. this can be plain text or HTML.
+ *
+ * Example text file:
+ * (code)
+ * lat lon title description iconSize iconOffset icon
+ * 10 20 title description 21,25 -10,-25 http://www.openlayers.org/dev/img/marker.png
+ * (end)
+ *
+ * Inherits from:
+ * - <OpenLayers.Layer.Markers>
+ */
+OpenLayers.Layer.Text = OpenLayers.Class(OpenLayers.Layer.Markers, {
+
+ /**
+ * APIProperty: location
+ * {String} URL of text file. Must be specified in the "options" argument
+ * of the constructor. Can not be changed once passed in.
+ */
+ location:null,
+
+ /**
+ * Property: features
+ * {Array(<OpenLayers.Feature>)}
+ */
+ features: null,
+
+ /**
+ * APIProperty: formatOptions
+ * {Object} Hash of options which should be passed to the format when it is
+ * created. Must be passed in the constructor.
+ */
+ formatOptions: null,
+
+ /**
+ * Property: selectedFeature
+ * {<OpenLayers.Feature>}
+ */
+ selectedFeature: null,
+
+ /**
+ * Constructor: OpenLayers.Layer.Text
+ * Create a text layer.
+ *
+ * Parameters:
+ * name - {String}
+ * options - {Object} Object with properties to be set on the layer.
+ * Must include <location> property.
+ */
+ initialize: function(name, options) {
+ OpenLayers.Layer.Markers.prototype.initialize.apply(this, arguments);
+ this.features = new Array();
+ },
+
+ /**
+ * APIMethod: destroy
+ */
+ destroy: function() {
+ // Warning: Layer.Markers.destroy() must be called prior to calling
+ // clearFeatures() here, otherwise we leak memory. Indeed, if
+ // Layer.Markers.destroy() is called after clearFeatures(), it won't be
+ // able to remove the marker image elements from the layer's div since
+ // the markers will have been destroyed by clearFeatures().
+ OpenLayers.Layer.Markers.prototype.destroy.apply(this, arguments);
+ this.clearFeatures();
+ this.features = null;
+ },
+
+ /**
+ * Method: loadText
+ * Start the load of the Text data. Don't do this when we first add the layer,
+ * since we may not be visible at any point, and it would therefore be a waste.
+ */
+ loadText: function() {
+ if (!this.loaded) {
+ if (this.location != null) {
+
+ var onFail = function(e) {
+ this.events.triggerEvent("loadend");
+ };
+
+ this.events.triggerEvent("loadstart");
+ OpenLayers.Request.GET({
+ url: this.location,
+ success: this.parseData,
+ failure: onFail,
+ scope: this
+ });
+ this.loaded = true;
+ }
+ }
+ },
+
+ /**
+ * Method: moveTo
+ * If layer is visible and Text has not been loaded, load Text.
+ *
+ * Parameters:
+ * bounds - {Object}
+ * zoomChanged - {Object}
+ * minor - {Object}
+ */
+ moveTo:function(bounds, zoomChanged, minor) {
+ OpenLayers.Layer.Markers.prototype.moveTo.apply(this, arguments);
+ if(this.visibility && !this.loaded){
+ this.loadText();
+ }
+ },
+
+ /**
+ * Method: parseData
+ *
+ * Parameters:
+ * ajaxRequest - {<OpenLayers.Request.XMLHttpRequest>}
+ */
+ parseData: function(ajaxRequest) {
+ var text = ajaxRequest.responseText;
+
+ var options = {};
+
+ OpenLayers.Util.extend(options, this.formatOptions);
+
+ if (this.map && !this.projection.equals(this.map.getProjectionObject())) {
+ options.externalProjection = this.projection;
+ options.internalProjection = this.map.getProjectionObject();
+ }
+
+ var parser = new OpenLayers.Format.Text(options);
+ features = parser.read(text);
+ for (var i=0, len=features.length; i<len; i++) {
+ var data = {};
+ var feature = features[i];
+ var location;
+ var iconSize, iconOffset;
+
+ location = new OpenLayers.LonLat(feature.geometry.x,
+ feature.geometry.y);
+
+ if (feature.style.graphicWidth
+ && feature.style.graphicHeight) {
+ iconSize = new OpenLayers.Size(
+ feature.style.graphicWidth,
+ feature.style.graphicHeight);
+ }
+
+ // FIXME: At the moment, we only use this if we have an
+ // externalGraphic, because icon has no setOffset API Method.
+ /**
+ * FIXME FIRST!!
+ * The Text format does all sorts of parseFloating
+ * The result of a parseFloat for a bogus string is NaN. That
+ * means the three possible values here are undefined, NaN, or a
+ * number. The previous check was an identity check for null. This
+ * means it was failing for all undefined or NaN. A slightly better
+ * check is for undefined. An even better check is to see if the
+ * value is a number (see #1441).
+ */
+ if (feature.style.graphicXOffset !== undefined
+ && feature.style.graphicYOffset !== undefined) {
+ iconOffset = new OpenLayers.Pixel(
+ feature.style.graphicXOffset,
+ feature.style.graphicYOffset);
+ }
+
+ if (feature.style.externalGraphic != null) {
+ data.icon = new OpenLayers.Icon(feature.style.externalGraphic,
+ iconSize,
+ iconOffset);
+ } else {
+ data.icon = OpenLayers.Marker.defaultIcon();
+
+ //allows for the case where the image url is not
+ // specified but the size is. use a default icon
+ // but change the size
+ if (iconSize != null) {
+ data.icon.setSize(iconSize);
+ }
+ }
+
+ if ((feature.attributes.title != null)
+ && (feature.attributes.description != null)) {
+ data['popupContentHTML'] =
+ '<h2>'+feature.attributes.title+'</h2>' +
+ '<p>'+feature.attributes.description+'</p>';
+ }
+
+ data['overflow'] = feature.attributes.overflow || "auto";
+
+ var markerFeature = new OpenLayers.Feature(this, location, data);
+ this.features.push(markerFeature);
+ var marker = markerFeature.createMarker();
+ if ((feature.attributes.title != null)
+ && (feature.attributes.description != null)) {
+ marker.events.register('click', markerFeature, this.markerClick);
+ }
+ this.addMarker(marker);
+ }
+ this.events.triggerEvent("loadend");
+ },
+
+ /**
+ * Property: markerClick
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ markerClick: function(evt) {
+ var sameMarkerClicked = (this == this.layer.selectedFeature);
+ this.layer.selectedFeature = (!sameMarkerClicked) ? this : null;
+ for(var i=0, len=this.layer.map.popups.length; i<len; i++) {
+ this.layer.map.removePopup(this.layer.map.popups[i]);
+ }
+ if (!sameMarkerClicked) {
+ this.layer.map.addPopup(this.createPopup());
+ }
+ OpenLayers.Event.stop(evt);
+ },
+
+ /**
+ * Method: clearFeatures
+ */
+ clearFeatures: function() {
+ if (this.features != null) {
+ while(this.features.length > 0) {
+ var feature = this.features[0];
+ OpenLayers.Util.removeItem(this.features, feature);
+ feature.destroy();
+ }
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Layer.Text"
+});
+/* ======================================================================
+ OpenLayers/Layer/VirtualEarth.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/Layer/EventPane.js
+ * @requires OpenLayers/Layer/FixedZoomLevels.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.VirtualEarth
+ *
+ * Inherits from:
+ * - <OpenLayers.Layer.EventPane>
+ * - <OpenLayers.Layer.FixedZoomLevels>
+ */
+OpenLayers.Layer.VirtualEarth = OpenLayers.Class(
+ OpenLayers.Layer.EventPane,
+ OpenLayers.Layer.FixedZoomLevels, {
+
+ /**
+ * Constant: MIN_ZOOM_LEVEL
+ * {Integer} 1
+ */
+ MIN_ZOOM_LEVEL: 1,
+
+ /**
+ * Constant: MAX_ZOOM_LEVEL
+ * {Integer} 17
+ */
+ MAX_ZOOM_LEVEL: 17,
+
+ /**
+ * Constant: RESOLUTIONS
+ * {Array(Float)} Hardcode these resolutions so that they are more closely
+ * tied with the standard wms projection
+ */
+ RESOLUTIONS: [
+ 1.40625,
+ 0.703125,
+ 0.3515625,
+ 0.17578125,
+ 0.087890625,
+ 0.0439453125,
+ 0.02197265625,
+ 0.010986328125,
+ 0.0054931640625,
+ 0.00274658203125,
+ 0.001373291015625,
+ 0.0006866455078125,
+ 0.00034332275390625,
+ 0.000171661376953125,
+ 0.0000858306884765625,
+ 0.00004291534423828125
+ ],
+
+ /**
+ * APIProperty: type
+ * {VEMapType}
+ */
+ type: null,
+
+ /**
+ * APIProperty: sphericalMercator
+ * {Boolean} Should the map act as a mercator-projected map? This will
+ * cause all interactions with the map to be in the actual map
+ * projection, which allows support for vector drawing, overlaying
+ * other maps, etc.
+ */
+ sphericalMercator: false,
+
+ /**
+ * Constructor: OpenLayers.Layer.VirtualEarth
+ *
+ * Parameters:
+ * name - {String}
+ * options - {Object}
+ */
+ initialize: function(name, options) {
+ OpenLayers.Layer.EventPane.prototype.initialize.apply(this, arguments);
+ OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this,
+ arguments);
+ if(this.sphericalMercator) {
+ OpenLayers.Util.extend(this, OpenLayers.Layer.SphericalMercator);
+ this.initMercatorParameters();
+ }
+ },
+
+ /**
+ * Method: loadMapObject
+ */
+ loadMapObject:function() {
+
+ // create div and set to same size as map
+ var veDiv = OpenLayers.Util.createDiv(this.name);
+ var sz = this.map.getSize();
+ veDiv.style.width = sz.w + "px";
+ veDiv.style.height = sz.h + "px";
+ this.div.appendChild(veDiv);
+
+ try { // crash prevention
+ this.mapObject = new VEMap(this.name);
+ } catch (e) { }
+
+ if (this.mapObject != null) {
+ try { // this is to catch a Mozilla bug without falling apart
+
+ // The fourth argument is whether the map is 'fixed' -- not
+ // draggable. See:
+ // http://blogs.msdn.com/virtualearth/archive/2007/09/28/locking-a-virtual-earth-map.aspx
+ //
+ this.mapObject.LoadMap(null, null, this.type, true);
+ this.mapObject.AttachEvent("onmousedown", function() {return true; });
+
+ } catch (e) { }
+ this.mapObject.HideDashboard();
+ }
+
+ //can we do smooth panning? this is an unpublished method, so we need
+ // to be careful
+ if ( !this.mapObject ||
+ !this.mapObject.vemapcontrol ||
+ !this.mapObject.vemapcontrol.PanMap ||
+ (typeof this.mapObject.vemapcontrol.PanMap != "function")) {
+
+ this.dragPanMapObject = null;
+ }
+
+ },
+
+ /**
+ * APIMethod: getWarningHTML
+ *
+ * Returns:
+ * {String} String with information on why layer is broken, how to get
+ * it working.
+ */
+ getWarningHTML:function() {
+ return OpenLayers.i18n(
+ "getLayerWarning", {'layerType':'VE', 'layerLib':'VirtualEarth'}
+ );
+ },
+
+
+
+ /************************************
+ * *
+ * MapObject Interface Controls *
+ * *
+ ************************************/
+
+
+ // Get&Set Center, Zoom
+
+ /**
+ * APIMethod: setMapObjectCenter
+ * Set the mapObject to the specified center and zoom
+ *
+ * Parameters:
+ * center - {Object} MapObject LonLat format
+ * zoom - {int} MapObject zoom format
+ */
+ setMapObjectCenter: function(center, zoom) {
+ this.mapObject.SetCenterAndZoom(center, zoom);
+ },
+
+ /**
+ * APIMethod: getMapObjectCenter
+ *
+ * Returns:
+ * {Object} The mapObject's current center in Map Object format
+ */
+ getMapObjectCenter: function() {
+ return this.mapObject.GetCenter();
+ },
+
+ /**
+ * APIMethod: dragPanMapObject
+ *
+ * Parameters:
+ * dX - {Integer}
+ * dY - {Integer}
+ */
+ dragPanMapObject: function(dX, dY) {
+ this.mapObject.vemapcontrol.PanMap(dX, -dY);
+ },
+
+ /**
+ * APIMethod: getMapObjectZoom
+ *
+ * Returns:
+ * {Integer} The mapObject's current zoom, in Map Object format
+ */
+ getMapObjectZoom: function() {
+ return this.mapObject.GetZoomLevel();
+ },
+
+
+ // LonLat - Pixel Translation
+
+ /**
+ * APIMethod: getMapObjectLonLatFromMapObjectPixel
+ *
+ * Parameters:
+ * moPixel - {Object} MapObject Pixel format
+ *
+ * Returns:
+ * {Object} MapObject LonLat translated from MapObject Pixel
+ */
+ getMapObjectLonLatFromMapObjectPixel: function(moPixel) {
+ //the conditional here is to test if we are running the v6 of VE
+ return (typeof VEPixel != 'undefined')
+ ? this.mapObject.PixelToLatLong(moPixel)
+ : this.mapObject.PixelToLatLong(moPixel.x, moPixel.y);
+ },
+
+ /**
+ * APIMethod: getMapObjectPixelFromMapObjectLonLat
+ *
+ * Parameters:
+ * moLonLat - {Object} MapObject LonLat format
+ *
+ * Returns:
+ * {Object} MapObject Pixel transtlated from MapObject LonLat
+ */
+ getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {
+ return this.mapObject.LatLongToPixel(moLonLat);
+ },
+
+
+ /************************************
+ * *
+ * MapObject Primitives *
+ * *
+ ************************************/
+
+
+ // LonLat
+
+ /**
+ * APIMethod: getLongitudeFromMapObjectLonLat
+ *
+ * Parameters:
+ * moLonLat - {Object} MapObject LonLat format
+ *
+ * Returns:
+ * {Float} Longitude of the given MapObject LonLat
+ */
+ getLongitudeFromMapObjectLonLat: function(moLonLat) {
+ return this.sphericalMercator ?
+ this.forwardMercator(moLonLat.Longitude, moLonLat.Latitude).lon :
+ moLonLat.Longitude;
+ },
+
+ /**
+ * APIMethod: getLatitudeFromMapObjectLonLat
+ *
+ * Parameters:
+ * moLonLat - {Object} MapObject LonLat format
+ *
+ * Returns:
+ * {Float} Latitude of the given MapObject LonLat
+ */
+ getLatitudeFromMapObjectLonLat: function(moLonLat) {
+ return this.sphericalMercator ?
+ this.forwardMercator(moLonLat.Longitude, moLonLat.Latitude).lat :
+ moLonLat.Latitude;
+ },
+
+ /**
+ * APIMethod: getMapObjectLonLatFromLonLat
+ *
+ * Parameters:
+ * lon - {Float}
+ * lat - {Float}
+ *
+ * Returns:
+ * {Object} MapObject LonLat built from lon and lat params
+ */
+ getMapObjectLonLatFromLonLat: function(lon, lat) {
+ var veLatLong;
+ if(this.sphericalMercator) {
+ var lonlat = this.inverseMercator(lon, lat);
+ veLatLong = new VELatLong(lonlat.lat, lonlat.lon);
+ } else {
+ veLatLong = new VELatLong(lat, lon);
+ }
+ return veLatLong;
+ },
+
+ // Pixel
+
+ /**
+ * APIMethod: getXFromMapObjectPixel
+ *
+ * Parameters:
+ * moPixel - {Object} MapObject Pixel format
+ *
+ * Returns:
+ * {Integer} X value of the MapObject Pixel
+ */
+ getXFromMapObjectPixel: function(moPixel) {
+ return moPixel.x;
+ },
+
+ /**
+ * APIMethod: getYFromMapObjectPixel
+ *
+ * Parameters:
+ * moPixel - {Object} MapObject Pixel format
+ *
+ * Returns:
+ * {Integer} Y value of the MapObject Pixel
+ */
+ getYFromMapObjectPixel: function(moPixel) {
+ return moPixel.y;
+ },
+
+ /**
+ * APIMethod: getMapObjectPixelFromXY
+ *
+ * Parameters:
+ * x - {Integer}
+ * y - {Integer}
+ *
+ * Returns:
+ * {Object} MapObject Pixel from x and y parameters
+ */
+ getMapObjectPixelFromXY: function(x, y) {
+ //the conditional here is to test if we are running the v6 of VE
+ return (typeof VEPixel != 'undefined') ? new VEPixel(x, y)
+ : new Msn.VE.Pixel(x, y);
+ },
+
+ CLASS_NAME: "OpenLayers.Layer.VirtualEarth"
+});
+/* ======================================================================
+ OpenLayers/Layer/Yahoo.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/Layer/EventPane.js
+ * @requires OpenLayers/Layer/FixedZoomLevels.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.Yahoo
+ *
+ * Inherits from:
+ * - <OpenLayers.Layer.EventPane>
+ * - <OpenLayers.Layer.FixedZoomLevels>
+ */
+OpenLayers.Layer.Yahoo = OpenLayers.Class(
+ OpenLayers.Layer.EventPane, OpenLayers.Layer.FixedZoomLevels, {
+
+ /**
+ * Constant: MIN_ZOOM_LEVEL
+ * {Integer} 0
+ */
+ MIN_ZOOM_LEVEL: 0,
+
+ /**
+ * Constant: MAX_ZOOM_LEVEL
+ * {Integer} 15
+ */
+ MAX_ZOOM_LEVEL: 15,
+
+ /**
+ * Constant: RESOLUTIONS
+ * {Array(Float)} Hardcode these resolutions so that they are more closely
+ * tied with the standard wms projection
+ */
+ RESOLUTIONS: [
+ 1.40625,
+ 0.703125,
+ 0.3515625,
+ 0.17578125,
+ 0.087890625,
+ 0.0439453125,
+ 0.02197265625,
+ 0.010986328125,
+ 0.0054931640625,
+ 0.00274658203125,
+ 0.001373291015625,
+ 0.0006866455078125,
+ 0.00034332275390625,
+ 0.000171661376953125,
+ 0.0000858306884765625,
+ 0.00004291534423828125
+ ],
+
+ /**
+ * APIProperty: type
+ * {YahooMapType}
+ */
+ type: null,
+
+ /**
+ * APIProperty: sphericalMercator
+ * {Boolean} Should the map act as a mercator-projected map? This will
+ * cause all interactions with the map to be in the actual map projection,
+ * which allows support for vector drawing, overlaying other maps, etc.
+ */
+ sphericalMercator: false,
+
+ /**
+ * Constructor: OpenLayers.Layer.Yahoo
+ *
+ * Parameters:
+ * name - {String}
+ * options - {Object}
+ */
+ initialize: function(name, options) {
+ OpenLayers.Layer.EventPane.prototype.initialize.apply(this, arguments);
+ OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this,
+ arguments);
+ if(this.sphericalMercator) {
+ OpenLayers.Util.extend(this, OpenLayers.Layer.SphericalMercator);
+ this.initMercatorParameters();
+ }
+ },
+
+ /**
+ * Method: loadMapObject
+ */
+ loadMapObject:function() {
+ try { //do not crash!
+ var size = this.getMapObjectSizeFromOLSize(this.map.getSize());
+ this.mapObject = new YMap(this.div, this.type, size);
+ this.mapObject.disableKeyControls();
+ this.mapObject.disableDragMap();
+
+ //can we do smooth panning? (moveByXY is not an API function)
+ if ( !this.mapObject.moveByXY ||
+ (typeof this.mapObject.moveByXY != "function" ) ) {
+
+ this.dragPanMapObject = null;
+ }
+ } catch(e) {}
+ },
+
+ /**
+ * Method: onMapResize
+ *
+ */
+ onMapResize: function() {
+ try {
+ var size = this.getMapObjectSizeFromOLSize(this.map.getSize());
+ this.mapObject.resizeTo(size);
+ } catch(e) {}
+ },
+
+
+ /**
+ * APIMethod: setMap
+ * Overridden from EventPane because we need to remove this yahoo event
+ * pane which prohibits our drag and drop, and we can only do this
+ * once the map has been loaded and centered.
+ *
+ * Parameters:
+ * map - {<OpenLayers.Map>}
+ */
+ setMap: function(map) {
+ OpenLayers.Layer.EventPane.prototype.setMap.apply(this, arguments);
+
+ this.map.events.register("moveend", this, this.fixYahooEventPane);
+ },
+
+ /**
+ * Method: fixYahooEventPane
+ * The map has been centered, so the mysterious yahoo eventpane has been
+ * added. we remove it so that it doesnt mess with *our* event pane.
+ */
+ fixYahooEventPane: function() {
+ var yahooEventPane = OpenLayers.Util.getElement("ygddfdiv");
+ if (yahooEventPane != null) {
+ if (yahooEventPane.parentNode != null) {
+ yahooEventPane.parentNode.removeChild(yahooEventPane);
+ }
+ this.map.events.unregister("moveend", this,
+ this.fixYahooEventPane);
+ }
+ },
+
+ /**
+ * APIMethod: getWarningHTML
+ *
+ * Returns:
+ * {String} String with information on why layer is broken, how to get
+ * it working.
+ */
+ getWarningHTML:function() {
+ return OpenLayers.i18n(
+ "getLayerWarning", {'layerType':'Yahoo', 'layerLib':'Yahoo'}
+ );
+ },
+
+ /********************************************************/
+ /* */
+ /* Translation Functions */
+ /* */
+ /* The following functions translate GMaps and OL */
+ /* formats for Pixel, LonLat, Bounds, and Zoom */
+ /* */
+ /********************************************************/
+
+
+ //
+ // TRANSLATION: MapObject Zoom <-> OpenLayers Zoom
+ //
+
+ /**
+ * APIMethod: getOLZoomFromMapObjectZoom
+ *
+ * Parameters:
+ * gZoom - {Integer}
+ *
+ * Returns:
+ * {Integer} An OpenLayers Zoom level, translated from the passed in gZoom
+ * Returns null if null value is passed in.
+ */
+ getOLZoomFromMapObjectZoom: function(moZoom) {
+ var zoom = null;
+ if (moZoom != null) {
+ zoom = OpenLayers.Layer.FixedZoomLevels.prototype.getOLZoomFromMapObjectZoom.apply(this, [moZoom]);
+ zoom = 18 - zoom;
+ }
+ return zoom;
+ },
+
+ /**
+ * APIMethod: getMapObjectZoomFromOLZoom
+ *
+ * Parameters:
+ * olZoom - {Integer}
+ *
+ * Returns:
+ * {Integer} A MapObject level, translated from the passed in olZoom
+ * Returns null if null value is passed in
+ */
+ getMapObjectZoomFromOLZoom: function(olZoom) {
+ var zoom = null;
+ if (olZoom != null) {
+ zoom = OpenLayers.Layer.FixedZoomLevels.prototype.getMapObjectZoomFromOLZoom.apply(this, [olZoom]);
+ zoom = 18 - zoom;
+ }
+ return zoom;
+ },
+
+ /************************************
+ * *
+ * MapObject Interface Controls *
+ * *
+ ************************************/
+
+
+ // Get&Set Center, Zoom
+
+ /**
+ * APIMethod: setMapObjectCenter
+ * Set the mapObject to the specified center and zoom
+ *
+ * Parameters:
+ * center - {Object} MapObject LonLat format
+ * zoom - {int} MapObject zoom format
+ */
+ setMapObjectCenter: function(center, zoom) {
+ this.mapObject.drawZoomAndCenter(center, zoom);
+ },
+
+ /**
+ * APIMethod: getMapObjectCenter
+ *
+ * Returns:
+ * {Object} The mapObject's current center in Map Object format
+ */
+ getMapObjectCenter: function() {
+ return this.mapObject.getCenterLatLon();
+ },
+
+ /**
+ * APIMethod: dragPanMapObject
+ *
+ * Parameters:
+ * dX - {Integer}
+ * dY - {Integer}
+ */
+ dragPanMapObject: function(dX, dY) {
+ this.mapObject.moveByXY({
+ 'x': -dX,
+ 'y': dY
+ });
+ },
+
+ /**
+ * APIMethod: getMapObjectZoom
+ *
+ * Returns:
+ * {Integer} The mapObject's current zoom, in Map Object format
+ */
+ getMapObjectZoom: function() {
+ return this.mapObject.getZoomLevel();
+ },
+
+
+ // LonLat - Pixel Translation
+
+ /**
+ * APIMethod: getMapObjectLonLatFromMapObjectPixel
+ *
+ * Parameters:
+ * moPixel - {Object} MapObject Pixel format
+ *
+ * Returns:
+ * {Object} MapObject LonLat translated from MapObject Pixel
+ */
+ getMapObjectLonLatFromMapObjectPixel: function(moPixel) {
+ return this.mapObject.convertXYLatLon(moPixel);
+ },
+
+ /**
+ * APIMethod: getMapObjectPixelFromMapObjectLonLat
+ *
+ * Parameters:
+ * moLonLat - {Object} MapObject LonLat format
+ *
+ * Returns:
+ * {Object} MapObject Pixel transtlated from MapObject LonLat
+ */
+ getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {
+ return this.mapObject.convertLatLonXY(moLonLat);
+ },
+
+
+ /************************************
+ * *
+ * MapObject Primitives *
+ * *
+ ************************************/
+
+
+ // LonLat
+
+ /**
+ * APIMethod: getLongitudeFromMapObjectLonLat
+ *
+ * Parameters:
+ * moLonLat - {Object} MapObject LonLat format
+ *
+ * Returns:
+ * {Float} Longitude of the given MapObject LonLat
+ */
+ getLongitudeFromMapObjectLonLat: function(moLonLat) {
+ return this.sphericalMercator ?
+ this.forwardMercator(moLonLat.Lon, moLonLat.Lat).lon :
+ moLonLat.Lon;
+ },
+
+ /**
+ * APIMethod: getLatitudeFromMapObjectLonLat
+ *
+ * Parameters:
+ * moLonLat - {Object} MapObject LonLat format
+ *
+ * Returns:
+ * {Float} Latitude of the given MapObject LonLat
+ */
+ getLatitudeFromMapObjectLonLat: function(moLonLat) {
+ return this.sphericalMercator ?
+ this.forwardMercator(moLonLat.Lon, moLonLat.Lat).lat :
+ moLonLat.Lat;
+ },
+
+ /**
+ * APIMethod: getMapObjectLonLatFromLonLat
+ *
+ * Parameters:
+ * lon - {Float}
+ * lat - {Float}
+ *
+ * Returns:
+ * {Object} MapObject LonLat built from lon and lat params
+ */
+ getMapObjectLonLatFromLonLat: function(lon, lat) {
+ var yLatLong;
+ if(this.sphericalMercator) {
+ var lonlat = this.inverseMercator(lon, lat);
+ yLatLong = new YGeoPoint(lonlat.lat, lonlat.lon);
+ } else {
+ yLatLong = new YGeoPoint(lat, lon);
+ }
+ return yLatLong;
+ },
+
+ // Pixel
+
+ /**
+ * APIMethod: getXFromMapObjectPixel
+ *
+ * Parameters:
+ * moPixel - {Object} MapObject Pixel format
+ *
+ * Returns:
+ * {Integer} X value of the MapObject Pixel
+ */
+ getXFromMapObjectPixel: function(moPixel) {
+ return moPixel.x;
+ },
+
+ /**
+ * APIMethod: getYFromMapObjectPixel
+ *
+ * Parameters:
+ * moPixel - {Object} MapObject Pixel format
+ *
+ * Returns:
+ * {Integer} Y value of the MapObject Pixel
+ */
+ getYFromMapObjectPixel: function(moPixel) {
+ return moPixel.y;
+ },
+
+ /**
+ * APIMethod: getMapObjectPixelFromXY
+ *
+ * Parameters:
+ * x - {Integer}
+ * y - {Integer}
+ *
+ * Returns:
+ * {Object} MapObject Pixel from x and y parameters
+ */
+ getMapObjectPixelFromXY: function(x, y) {
+ return new YCoordPoint(x, y);
+ },
+
+ // Size
+
+ /**
+ * APIMethod: getMapObjectSizeFromOLSize
+ *
+ * Parameters:
+ * olSize - {<OpenLayers.Size>}
+ *
+ * Returns:
+ * {Object} MapObject Size from olSize parameter
+ */
+ getMapObjectSizeFromOLSize: function(olSize) {
+ return new YSize(olSize.w, olSize.h);
+ },
+
+ CLASS_NAME: "OpenLayers.Layer.Yahoo"
+});
+/* ======================================================================
+ OpenLayers/Style.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/Util.js
+ * @requires OpenLayers/Feature/Vector.js
+ */
+
+/**
+ * Class: OpenLayers.Style
+ * This class represents a UserStyle obtained
+ * from a SLD, containing styling rules.
+ */
+OpenLayers.Style = OpenLayers.Class({
+
+ /**
+ * APIProperty: name
+ * {String}
+ */
+ name: null,
+
+ /**
+ * Property: title
+ * {String} Title of this style (set if included in SLD)
+ */
+ title: null,
+
+ /**
+ * Property: description
+ * {String} Description of this style (set if abstract is included in SLD)
+ */
+ description: null,
+
+ /**
+ * APIProperty: layerName
+ * {<String>} name of the layer that this style belongs to, usually
+ * according to the NamedLayer attribute of an SLD document.
+ */
+ layerName: null,
+
+ /**
+ * APIProperty: isDefault
+ * {Boolean}
+ */
+ isDefault: false,
+
+ /**
+ * Property: rules
+ * {Array(<OpenLayers.Rule>)}
+ */
+ rules: null,
+
+ /**
+ * Property: context
+ * {Object} An optional object with properties that symbolizers' property
+ * values should be evaluated against. If no context is specified,
+ * feature.attributes will be used
+ */
+ context: null,
+
+ /**
+ * Property: defaultStyle
+ * {Object} hash of style properties to use as default for merging
+ * rule-based style symbolizers onto. If no rules are defined,
+ * createSymbolizer will return this style.
+ */
+ defaultStyle: null,
+
+ /**
+ * Property: propertyStyles
+ * {Hash of Boolean} cache of style properties that need to be parsed for
+ * propertyNames. Property names are keys, values won't be used.
+ */
+ propertyStyles: null,
+
+
+ /**
+ * Constructor: OpenLayers.Style
+ * Creates a UserStyle.
+ *
+ * Parameters:
+ * style - {Object} Optional hash of style properties that will be
+ * used as default style for this style object. This style
+ * applies if no rules are specified. Symbolizers defined in
+ * rules will extend this default style.
+ * options - {Object} An optional object with properties to set on the
+ * userStyle
+ *
+ * Return:
+ * {<OpenLayers.Style>}
+ */
+ initialize: function(style, options) {
+ this.rules = [];
+
+ // use the default style from OpenLayers.Feature.Vector if no style
+ // was given in the constructor
+ this.setDefaultStyle(style ||
+ OpenLayers.Feature.Vector.style["default"]);
+
+ OpenLayers.Util.extend(this, options);
+ },
+
+ /**
+ * APIMethod: destroy
+ * nullify references to prevent circular references and memory leaks
+ */
+ destroy: function() {
+ for (var i=0, len=this.rules.length; i<len; i++) {
+ this.rules[i].destroy();
+ this.rules[i] = null;
+ }
+ this.rules = null;
+ this.defaultStyle = null;
+ },
+
+ /**
+ * Method: createSymbolizer
+ * creates a style by applying all feature-dependent rules to the base
+ * style.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature>} feature to evaluate rules for
+ *
+ * Returns:
+ * {Object} symbolizer hash
+ */
+ createSymbolizer: function(feature) {
+ var style = this.createLiterals(
+ OpenLayers.Util.extend({}, this.defaultStyle), feature);
+
+ var rules = this.rules;
+
+ var rule, context;
+ var elseRules = [];
+ var appliedRules = false;
+ for(var i=0, len=rules.length; i<len; i++) {
+ rule = rules[i];
+ // does the rule apply?
+ var applies = rule.evaluate(feature);
+
+ if(applies) {
+ if(rule instanceof OpenLayers.Rule && rule.elseFilter) {
+ elseRules.push(rule);
+ } else {
+ appliedRules = true;
+ this.applySymbolizer(rule, style, feature);
+ }
+ }
+ }
+
+ // if no other rules apply, apply the rules with else filters
+ if(appliedRules == false && elseRules.length > 0) {
+ appliedRules = true;
+ for(var i=0, len=elseRules.length; i<len; i++) {
+ this.applySymbolizer(elseRules[i], style, feature);
+ }
+ }
+
+ // don't display if there were rules but none applied
+ if(rules.length > 0 && appliedRules == false) {
+ style.display = "none";
+ } else {
+ style.display = "";
+ }
+
+ return style;
+ },
+
+ /**
+ * Method: applySymbolizer
+ *
+ * Parameters:
+ * rule - {OpenLayers.Rule}
+ * style - {Object}
+ * feature - {<OpenLayer.Feature.Vector>}
+ *
+ * Returns:
+ * {Object} A style with new symbolizer applied.
+ */
+ applySymbolizer: function(rule, style, feature) {
+ var symbolizerPrefix = feature.geometry ?
+ this.getSymbolizerPrefix(feature.geometry) :
+ OpenLayers.Style.SYMBOLIZER_PREFIXES[0];
+
+ var symbolizer = rule.symbolizer[symbolizerPrefix] || rule.symbolizer;
+
+ // merge the style with the current style
+ return this.createLiterals(
+ OpenLayers.Util.extend(style, symbolizer), feature);
+ },
+
+ /**
+ * Method: createLiterals
+ * creates literals for all style properties that have an entry in
+ * <this.propertyStyles>.
+ *
+ * Parameters:
+ * style - {Object} style to create literals for. Will be modified
+ * inline.
+ * feature - {Object}
+ *
+ * Returns:
+ * {Object} the modified style
+ */
+ createLiterals: function(style, feature) {
+ var context = this.context || feature.attributes || feature.data;
+
+ for (var i in this.propertyStyles) {
+ style[i] = OpenLayers.Style.createLiteral(style[i], context, feature);
+ }
+ return style;
+ },
+
+ /**
+ * Method: findPropertyStyles
+ * Looks into all rules for this style and the defaultStyle to collect
+ * all the style hash property names containing ${...} strings that have
+ * to be replaced using the createLiteral method before returning them.
+ *
+ * Returns:
+ * {Object} hash of property names that need createLiteral parsing. The
+ * name of the property is the key, and the value is true;
+ */
+ findPropertyStyles: function() {
+ var propertyStyles = {};
+
+ // check the default style
+ var style = this.defaultStyle;
+ this.addPropertyStyles(propertyStyles, style);
+
+ // walk through all rules to check for properties in their symbolizer
+ var rules = this.rules;
+ var symbolizer, value;
+ for (var i=0, len=rules.length; i<len; i++) {
+ var symbolizer = rules[i].symbolizer;
+ for (var key in symbolizer) {
+ value = symbolizer[key];
+ if (typeof value == "object") {
+ // symbolizer key is "Point", "Line" or "Polygon"
+ this.addPropertyStyles(propertyStyles, value);
+ } else {
+ // symbolizer is a hash of style properties
+ this.addPropertyStyles(propertyStyles, symbolizer);
+ break;
+ }
+ }
+ }
+ return propertyStyles;
+ },
+
+ /**
+ * Method: addPropertyStyles
+ *
+ * Parameters:
+ * propertyStyles - {Object} hash to add new property styles to. Will be
+ * modified inline
+ * symbolizer - {Object} search this symbolizer for property styles
+ *
+ * Returns:
+ * {Object} propertyStyles hash
+ */
+ addPropertyStyles: function(propertyStyles, symbolizer) {
+ var property;
+ for (var key in symbolizer) {
+ property = symbolizer[key];
+ if (typeof property == "string" &&
+ property.match(/\$\{\w+\}/)) {
+ propertyStyles[key] = true;
+ }
+ }
+ return propertyStyles;
+ },
+
+ /**
+ * APIMethod: addRules
+ * Adds rules to this style.
+ *
+ * Parameters:
+ * rules - {Array(<OpenLayers.Rule>)}
+ */
+ addRules: function(rules) {
+ this.rules = this.rules.concat(rules);
+ this.propertyStyles = this.findPropertyStyles();
+ },
+
+ /**
+ * APIMethod: setDefaultStyle
+ * Sets the default style for this style object.
+ *
+ * Parameters:
+ * style - {Object} Hash of style properties
+ */
+ setDefaultStyle: function(style) {
+ this.defaultStyle = style;
+ this.propertyStyles = this.findPropertyStyles();
+ },
+
+ /**
+ * Method: getSymbolizerPrefix
+ * Returns the correct symbolizer prefix according to the
+ * geometry type of the passed geometry
+ *
+ * Parameters:
+ * geometry {<OpenLayers.Geometry>}
+ *
+ * Returns:
+ * {String} key of the according symbolizer
+ */
+ getSymbolizerPrefix: function(geometry) {
+ var prefixes = OpenLayers.Style.SYMBOLIZER_PREFIXES;
+ for (var i=0, len=prefixes.length; i<len; i++) {
+ if (geometry.CLASS_NAME.indexOf(prefixes[i]) != -1) {
+ return prefixes[i];
+ }
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Style"
+});
+
+
+/**
+ * Function: createLiteral
+ * converts a style value holding a combination of PropertyName and Literal
+ * into a Literal, taking the property values from the passed features.
+ *
+ * Parameters:
+ * value - {String} value to parse. If this string contains a construct like
+ * "foo ${bar}", then "foo " will be taken as literal, and "${bar}"
+ * will be replaced by the value of the "bar" attribute of the passed
+ * feature.
+ * context - {Object} context to take attribute values from
+ * feature - {OpenLayers.Feature.Vector} The feature that will be passed
+ * to <OpenLayers.String.format> for evaluating functions in the context.
+ *
+ * Returns:
+ * {String} the parsed value. In the example of the value parameter above, the
+ * result would be "foo valueOfBar", assuming that the passed feature has an
+ * attribute named "bar" with the value "valueOfBar".
+ */
+OpenLayers.Style.createLiteral = function(value, context, feature) {
+ if (typeof value == "string" && value.indexOf("${") != -1) {
+ value = OpenLayers.String.format(value, context, [feature]);
+ value = (isNaN(value) || !value) ? value : parseFloat(value);
+ }
+ return value;
+};
+
+/**
+ * Constant: OpenLayers.Style.SYMBOLIZER_PREFIXES
+ * {Array} prefixes of the sld symbolizers. These are the
+ * same as the main geometry types
+ */
+OpenLayers.Style.SYMBOLIZER_PREFIXES = ['Point', 'Line', 'Polygon', 'Text'];
+/* ======================================================================
+ OpenLayers/Control/ModifyFeature.js
+ ====================================================================== */
+
+/* Copyright (c) 2006 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/DragFeature.js
+ * @requires OpenLayers/Control/SelectFeature.js
+ * @requires OpenLayers/Handler/Keyboard.js
+ */
+
+/**
+ * Class: OpenLayers.Control.ModifyFeature
+ * Control to modify features. When activated, a click renders the vertices
+ * of a feature - these vertices can then be dragged. By default, the
+ * delete key will delete the vertex under the mouse. New features are
+ * added by dragging "virtual vertices" between vertices. Create a new
+ * control with the <OpenLayers.Control.ModifyFeature> constructor.
+ *
+ * Inherits From:
+ * - <OpenLayers.Control>
+ */
+OpenLayers.Control.ModifyFeature = OpenLayers.Class(OpenLayers.Control, {
+
+ /**
+ * APIProperty: geometryTypes
+ * {Array(String)} To restrict modification to a limited set of geometry
+ * types, send a list of strings corresponding to the geometry class
+ * names.
+ */
+ geometryTypes: null,
+
+ /**
+ * APIProperty: clickout
+ * {Boolean} Unselect features when clicking outside any feature.
+ * Default is true.
+ */
+ clickout: true,
+
+ /**
+ * APIProperty: toggle
+ * {Boolean} Unselect a selected feature on click.
+ * Default is true.
+ */
+ toggle: true,
+
+ /**
+ * Property: layer
+ * {<OpenLayers.Layer.Vector>}
+ */
+ layer: null,
+
+ /**
+ * Property: feature
+ * {<OpenLayers.Feature.Vector>} Feature currently available for modification.
+ */
+ feature: null,
+
+ /**
+ * Property: vertices
+ * {Array(<OpenLayers.Feature.Vector>)} Verticies currently available
+ * for dragging.
+ */
+ vertices: null,
+
+ /**
+ * Property: virtualVertices
+ * {Array(<OpenLayers.Feature.Vector>)} Virtual vertices in the middle
+ * of each edge.
+ */
+ virtualVertices: null,
+
+ /**
+ * Property: selectControl
+ * {<OpenLayers.Control.SelectFeature>}
+ */
+ selectControl: null,
+
+ /**
+ * Property: dragControl
+ * {<OpenLayers.Control.DragFeature>}
+ */
+ dragControl: null,
+
+ /**
+ * Property: handlers
+ * {Object}
+ */
+ handlers: null,
+
+ /**
+ * APIProperty: deleteCodes
+ * {Array(Integer)} Keycodes for deleting verticies. Set to null to disable
+ * vertex deltion by keypress. If non-null, keypresses with codes
+ * in this array will delete vertices under the mouse. Default
+ * is 46 and 68, the 'delete' and lowercase 'd' keys.
+ */
+ deleteCodes: null,
+
+ /**
+ * APIProperty: virtualStyle
+ * {Object} A symbolizer to be used for virtual vertices.
+ */
+ virtualStyle: null,
+
+ /**
+ * APIProperty: mode
+ * {Integer} Bitfields specifying the modification mode. Defaults to
+ * OpenLayers.Control.ModifyFeature.RESHAPE. To set the mode to a
+ * combination of options, use the | operator. or example, to allow
+ * the control to both resize and rotate features, use the following
+ * syntax
+ * (code)
+ * control.mode = OpenLayers.Control.ModifyFeature.RESIZE |
+ * OpenLayers.Control.ModifyFeature.ROTATE;
+ * (end)
+ */
+ mode: null,
+
+ /**
+ * Property: radiusHandle
+ * {<OpenLayers.Feature.Vector>} A handle for rotating/resizing a feature.
+ */
+ radiusHandle: null,
+
+ /**
+ * Property: dragHandle
+ * {<OpenLayers.Feature.Vector>} A handle for dragging a feature.
+ */
+ dragHandle: null,
+
+ /**
+ * APIProperty: onModificationStart
+ * {Function} *Deprecated*. Register for "beforefeaturemodified" instead.
+ * The "beforefeaturemodified" event is triggered on the layer before
+ * any modification begins.
+ *
+ * Optional function to be called when a feature is selected
+ * to be modified. The function should expect to be called with a
+ * feature. This could be used for example to allow to lock the
+ * feature on server-side.
+ */
+ onModificationStart: function() {},
+
+ /**
+ * APIProperty: onModification
+ * {Function} *Deprecated*. Register for "featuremodified" instead.
+ * The "featuremodified" event is triggered on the layer with each
+ * feature modification.
+ *
+ * Optional function to be called when a feature has been
+ * modified. The function should expect to be called with a feature.
+ */
+ onModification: function() {},
+
+ /**
+ * APIProperty: onModificationEnd
+ * {Function} *Deprecated*. Register for "afterfeaturemodified" instead.
+ * The "afterfeaturemodified" event is triggered on the layer after
+ * a feature has been modified.
+ *
+ * Optional function to be called when a feature is finished
+ * being modified. The function should expect to be called with a
+ * feature.
+ */
+ onModificationEnd: function() {},
+
+ /**
+ * Constructor: OpenLayers.Control.ModifyFeature
+ * Create a new modify feature control.
+ *
+ * Parameters:
+ * layer - {<OpenLayers.Layer.Vector>} Layer that contains features that
+ * will be modified.
+ * options - {Object} Optional object whose properties will be set on the
+ * control.
+ */
+ initialize: function(layer, options) {
+ this.layer = layer;
+ this.vertices = [];
+ this.virtualVertices = [];
+ this.virtualStyle = OpenLayers.Util.extend({},
+ this.layer.style || this.layer.styleMap.createSymbolizer());
+ this.virtualStyle.fillOpacity = 0.3;
+ this.virtualStyle.strokeOpacity = 0.3;
+ this.deleteCodes = [46, 68];
+ this.mode = OpenLayers.Control.ModifyFeature.RESHAPE;
+ OpenLayers.Control.prototype.initialize.apply(this, [options]);
+ if(!(this.deleteCodes instanceof Array)) {
+ this.deleteCodes = [this.deleteCodes];
+ }
+ var control = this;
+
+ // configure the select control
+ var selectOptions = {
+ geometryTypes: this.geometryTypes,
+ clickout: this.clickout,
+ toggle: this.toggle
+ };
+ this.selectControl = new OpenLayers.Control.SelectFeature(
+ layer, selectOptions
+ );
+ this.layer.events.on({
+ "beforefeatureselected": this.beforeSelectFeature,
+ "featureselected": this.selectFeature,
+ "featureunselected": this.unselectFeature,
+ scope: this
+ });
+
+ // configure the drag control
+ var dragOptions = {
+ geometryTypes: ["OpenLayers.Geometry.Point"],
+ snappingOptions: this.snappingOptions,
+ onStart: function(feature, pixel) {
+ control.dragStart.apply(control, [feature, pixel]);
+ },
+ onDrag: function(feature) {
+ control.dragVertex.apply(control, [feature]);
+ },
+ onComplete: function(feature) {
+ control.dragComplete.apply(control, [feature]);
+ }
+ };
+ this.dragControl = new OpenLayers.Control.DragFeature(
+ layer, dragOptions
+ );
+
+ // configure the keyboard handler
+ var keyboardOptions = {
+ keydown: this.handleKeypress
+ };
+ this.handlers = {
+ keyboard: new OpenLayers.Handler.Keyboard(this, keyboardOptions)
+ };
+ },
+
+ /**
+ * APIMethod: destroy
+ * Take care of things that are not handled in superclass.
+ */
+ destroy: function() {
+ this.layer.events.un({
+ "beforefeatureselected": this.beforeSelectFeature,
+ "featureselected": this.selectFeature,
+ "featureunselected": this.unselectFeature,
+ scope: this
+ });
+ this.layer = null;
+ this.selectControl.destroy();
+ this.dragControl.destroy();
+ OpenLayers.Control.prototype.destroy.apply(this, []);
+ },
+
+ /**
+ * APIMethod: activate
+ * Activate the control.
+ *
+ * Returns:
+ * {Boolean} Successfully activated the control.
+ */
+ activate: function() {
+ return (this.selectControl.activate() &&
+ this.handlers.keyboard.activate() &&
+ OpenLayers.Control.prototype.activate.apply(this, arguments));
+ },
+
+ /**
+ * APIMethod: deactivate
+ * Deactivate the control.
+ *
+ * Returns:
+ * {Boolean} Successfully deactivated the control.
+ */
+ deactivate: function() {
+ var deactivated = false;
+ // the return from the controls is unimportant in this case
+ if(OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {
+ this.layer.removeFeatures(this.vertices, {silent: true});
+ this.layer.removeFeatures(this.virtualVertices, {silent: true});
+ this.vertices = [];
+ this.dragControl.deactivate();
+ if(this.feature && this.feature.geometry) {
+ this.selectControl.unselect.apply(this.selectControl,
+ [this.feature]);
+ }
+ this.selectControl.deactivate();
+ this.handlers.keyboard.deactivate();
+ deactivated = true;
+ }
+ return deactivated;
+ },
+
+ /**
+ * Method: beforeSelectFeature
+ * Called before a feature is selected.
+ *
+ * Parameters:
+ * object - {Object} Object with a feature property referencing the
+ * selected feature.
+ */
+ beforeSelectFeature: function(object) {
+ return this.layer.events.triggerEvent(
+ "beforefeaturemodified", {feature: object.feature}
+ );
+ },
+
+ /**
+ * Method: selectFeature
+ * Called when the select feature control selects a feature.
+ *
+ * Parameters:
+ * object - {Object} Object with a feature property referencing the
+ * selected feature.
+ */
+ selectFeature: function(object) {
+ this.feature = object.feature;
+ this.resetVertices();
+ this.dragControl.activate();
+ this.onModificationStart(this.feature);
+ },
+
+ /**
+ * Method: unselectFeature
+ * Called when the select feature control unselects a feature.
+ *
+ * Parameters:
+ * object - {Object} Object with a feature property referencing the
+ * unselected feature.
+ */
+ unselectFeature: function(object) {
+ this.layer.removeFeatures(this.vertices, {silent: true});
+ this.vertices = [];
+ this.layer.destroyFeatures(this.virtualVertices, {silent: true});
+ this.virtualVertices = [];
+ if(this.dragHandle) {
+ this.layer.destroyFeatures([this.dragHandle], {silent: true});
+ delete this.dragHandle;
+ }
+ if(this.radiusHandle) {
+ this.layer.destroyFeatures([this.radiusHandle], {silent: true});
+ delete this.radiusHandle;
+ }
+ this.feature = null;
+ this.dragControl.deactivate();
+ this.onModificationEnd(object.feature);
+ this.layer.events.triggerEvent("afterfeaturemodified",
+ {feature: object.feature});
+ },
+
+ /**
+ * Method: dragStart
+ * Called by the drag feature control with before a feature is dragged.
+ * This method is used to differentiate between points and vertices
+ * of higher order geometries. This respects the <geometryTypes>
+ * property and forces a select of points when the drag control is
+ * already active (and stops events from propagating to the select
+ * control).
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>} The point or vertex about to be
+ * dragged.
+ * pixel - {<OpenLayers.Pixel>} Pixel location of the mouse event.
+ */
+ dragStart: function(feature, pixel) {
+ // only change behavior if the feature is not in the vertices array
+ if(feature != this.feature && !feature.geometry.parent &&
+ feature != this.dragHandle && feature != this.radiusHandle) {
+ if(this.feature) {
+ // unselect the currently selected feature
+ this.selectControl.clickFeature.apply(this.selectControl,
+ [this.feature]);
+ }
+ // check any constraints on the geometry type
+ if(this.geometryTypes == null ||
+ OpenLayers.Util.indexOf(this.geometryTypes,
+ feature.geometry.CLASS_NAME) != -1) {
+ // select the point
+ this.selectControl.clickFeature.apply(this.selectControl,
+ [feature]);
+ /**
+ * TBD: These lines improve workflow by letting the user
+ * immediately start dragging after the mouse down.
+ * However, it is very ugly to be messing with controls
+ * and their handlers in this way. I'd like a better
+ * solution if the workflow change is necessary.
+ */
+ // prepare the point for dragging
+ this.dragControl.overFeature.apply(this.dragControl,
+ [feature]);
+ this.dragControl.lastPixel = pixel;
+ this.dragControl.handlers.drag.started = true;
+ this.dragControl.handlers.drag.start = pixel;
+ this.dragControl.handlers.drag.last = pixel;
+ }
+ }
+ },
+
+ /**
+ * Method: dragVertex
+ * Called by the drag feature control with each drag move of a vertex.
+ *
+ * Parameters:
+ * vertex - {<OpenLayers.Feature.Vector>} The vertex being dragged.
+ */
+ dragVertex: function(vertex) {
+ /**
+ * Five cases:
+ * 1) dragging a simple point
+ * 2) dragging a virtual vertex
+ * 3) dragging a drag handle
+ * 4) dragging a radius handle
+ * 5) dragging a real vertex
+ */
+ if(this.feature.geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
+ // dragging a simple point
+ if(this.feature != vertex) {
+ this.feature = vertex;
+ }
+ } else {
+ if(vertex._index) {
+ // dragging a virtual vertex
+ vertex.geometry.parent.addComponent(vertex.geometry,
+ vertex._index);
+ // move from virtual to real vertex
+ delete vertex._index;
+ OpenLayers.Util.removeItem(this.virtualVertices, vertex);
+ this.vertices.push(vertex);
+ } else if(vertex == this.dragHandle) {
+ // dragging a drag handle
+ this.layer.removeFeatures(this.vertices, {silent: true});
+ this.vertices = [];
+ if(this.radiusHandle) {
+ this.layer.destroyFeatures([this.radiusHandle], {silent: true});
+ this.radiusHandle = null;
+ }
+ }
+ // dragging a radius handle - no special treatment
+ // dragging a real vertex - no special treatment
+ if(this.virtualVertices.length > 0) {
+ this.layer.destroyFeatures(this.virtualVertices, {silent: true});
+ this.virtualVertices = [];
+ }
+ this.layer.drawFeature(this.feature, this.selectControl.renderIntent);
+ }
+ // keep the vertex on top so it gets the mouseout after dragging
+ // this should be removed in favor of an option to draw under or
+ // maintain node z-index
+ this.layer.drawFeature(vertex);
+ },
+
+ /**
+ * Method: dragComplete
+ * Called by the drag feature control when the feature dragging is complete.
+ *
+ * Parameters:
+ * vertex - {<OpenLayers.Feature.Vector>} The vertex being dragged.
+ */
+ dragComplete: function(vertex) {
+ this.resetVertices();
+ this.onModification(this.feature);
+ this.layer.events.triggerEvent("featuremodified",
+ {feature: this.feature});
+ },
+
+ /**
+ * Method: resetVertices
+ */
+ resetVertices: function() {
+ // if coming from a drag complete we're about to destroy the vertex
+ // that was just dragged. For that reason, the drag feature control
+ // will never detect a mouse-out on that vertex, meaning that the drag
+ // handler won't be deactivated. This can cause errors because the drag
+ // feature control still has a feature to drag but that feature is
+ // destroyed. To prevent this, we call outFeature on the drag feature
+ // control if the control actually has a feature to drag.
+ if(this.dragControl.feature) {
+ this.dragControl.outFeature(this.dragControl.feature);
+ }
+ if(this.vertices.length > 0) {
+ this.layer.removeFeatures(this.vertices, {silent: true});
+ this.vertices = [];
+ }
+ if(this.virtualVertices.length > 0) {
+ this.layer.removeFeatures(this.virtualVertices, {silent: true});
+ this.virtualVertices = [];
+ }
+ if(this.dragHandle) {
+ this.layer.destroyFeatures([this.dragHandle], {silent: true});
+ this.dragHandle = null;
+ }
+ if(this.radiusHandle) {
+ this.layer.destroyFeatures([this.radiusHandle], {silent: true});
+ this.radiusHandle = null;
+ }
+ if(this.feature &&
+ this.feature.geometry.CLASS_NAME != "OpenLayers.Geometry.Point") {
+ if((this.mode & OpenLayers.Control.ModifyFeature.DRAG)) {
+ this.collectDragHandle();
+ }
+ if((this.mode & (OpenLayers.Control.ModifyFeature.ROTATE |
+ OpenLayers.Control.ModifyFeature.RESIZE))) {
+ this.collectRadiusHandle();
+ }
+ if((this.mode & OpenLayers.Control.ModifyFeature.RESHAPE)) {
+ this.collectVertices();
+ }
+ }
+ },
+
+ /**
+ * Method: handleKeypress
+ * Called by the feature handler on keypress. This is used to delete
+ * vertices. If the <deleteCode> property is set, vertices will
+ * be deleted when a feature is selected for modification and
+ * the mouse is over a vertex.
+ *
+ * Parameters:
+ * {Integer} Key code corresponding to the keypress event.
+ */
+ handleKeypress: function(evt) {
+ var code = evt.keyCode;
+
+ // check for delete key
+ if(this.feature &&
+ OpenLayers.Util.indexOf(this.deleteCodes, code) != -1) {
+ var vertex = this.dragControl.feature;
+ if(vertex &&
+ OpenLayers.Util.indexOf(this.vertices, vertex) != -1 &&
+ !this.dragControl.handlers.drag.dragging &&
+ vertex.geometry.parent) {
+ // remove the vertex
+ vertex.geometry.parent.removeComponent(vertex.geometry);
+ this.layer.drawFeature(this.feature,
+ this.selectControl.renderIntent);
+ this.resetVertices();
+ this.onModification(this.feature);
+ this.layer.events.triggerEvent("featuremodified",
+ {feature: this.feature});
+ }
+ }
+ },
+
+ /**
+ * Method: collectVertices
+ * Collect the vertices from the modifiable feature's geometry and push
+ * them on to the control's vertices array.
+ */
+ collectVertices: function() {
+ this.vertices = [];
+ this.virtualVertices = [];
+ var control = this;
+ function collectComponentVertices(geometry) {
+ var i, vertex, component, len;
+ if(geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
+ vertex = new OpenLayers.Feature.Vector(geometry);
+ control.vertices.push(vertex);
+ } else {
+ var numVert = geometry.components.length;
+ if(geometry.CLASS_NAME == "OpenLayers.Geometry.LinearRing") {
+ numVert -= 1;
+ }
+ for(i=0; i<numVert; ++i) {
+ component = geometry.components[i];
+ if(component.CLASS_NAME == "OpenLayers.Geometry.Point") {
+ vertex = new OpenLayers.Feature.Vector(component);
+ control.vertices.push(vertex);
+ } else {
+ collectComponentVertices(component);
+ }
+ }
+
+ // add virtual vertices in the middle of each edge
+ if(geometry.CLASS_NAME != "OpenLayers.Geometry.MultiPoint") {
+ for(i=0, len=geometry.components.length; i<len-1; ++i) {
+ var prevVertex = geometry.components[i];
+ var nextVertex = geometry.components[i + 1];
+ if(prevVertex.CLASS_NAME == "OpenLayers.Geometry.Point" &&
+ nextVertex.CLASS_NAME == "OpenLayers.Geometry.Point") {
+ var x = (prevVertex.x + nextVertex.x) / 2;
+ var y = (prevVertex.y + nextVertex.y) / 2;
+ var point = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(x, y),
+ null, control.virtualStyle
+ );
+ // set the virtual parent and intended index
+ point.geometry.parent = geometry;
+ point._index = i + 1;
+ control.virtualVertices.push(point);
+ }
+ }
+ }
+ }
+ }
+ collectComponentVertices.call(this, this.feature.geometry);
+ this.layer.addFeatures(this.virtualVertices, {silent: true});
+ this.layer.addFeatures(this.vertices, {silent: true});
+ },
+
+ /**
+ * Method: collectDragHandle
+ * Collect the drag handle for the selected geometry.
+ */
+ collectDragHandle: function() {
+ var geometry = this.feature.geometry;
+ var center = geometry.getBounds().getCenterLonLat();
+ var originGeometry = new OpenLayers.Geometry.Point(
+ center.lon, center.lat
+ );
+ var origin = new OpenLayers.Feature.Vector(originGeometry);
+ originGeometry.move = function(x, y) {
+ OpenLayers.Geometry.Point.prototype.move.call(this, x, y);
+ geometry.move(x, y);
+ };
+ this.dragHandle = origin;
+ this.layer.addFeatures([this.dragHandle], {silent: true});
+ },
+
+ /**
+ * Method: collectRadiusHandle
+ * Collect the radius handle for the selected geometry.
+ */
+ collectRadiusHandle: function() {
+ var geometry = this.feature.geometry;
+ var bounds = geometry.getBounds();
+ var center = bounds.getCenterLonLat();
+ var originGeometry = new OpenLayers.Geometry.Point(
+ center.lon, center.lat
+ );
+ var radiusGeometry = new OpenLayers.Geometry.Point(
+ bounds.right, bounds.bottom
+ );
+ var radius = new OpenLayers.Feature.Vector(radiusGeometry);
+ var resize = (this.mode & OpenLayers.Control.ModifyFeature.RESIZE);
+ var rotate = (this.mode & OpenLayers.Control.ModifyFeature.ROTATE);
+ radiusGeometry.move = function(x, y) {
+ OpenLayers.Geometry.Point.prototype.move.call(this, x, y);
+ var dx1 = this.x - originGeometry.x;
+ var dy1 = this.y - originGeometry.y;
+ var dx0 = dx1 - x;
+ var dy0 = dy1 - y;
+ if(rotate) {
+ var a0 = Math.atan2(dy0, dx0);
+ var a1 = Math.atan2(dy1, dx1);
+ var angle = a1 - a0;
+ angle *= 180 / Math.PI;
+ geometry.rotate(angle, originGeometry);
+ }
+ if(resize) {
+ var l0 = Math.sqrt((dx0 * dx0) + (dy0 * dy0));
+ var l1 = Math.sqrt((dx1 * dx1) + (dy1 * dy1));
+ geometry.resize(l1 / l0, originGeometry);
+ }
+ };
+ this.radiusHandle = radius;
+ this.layer.addFeatures([this.radiusHandle], {silent: true});
+ },
+
+ /**
+ * Method: setMap
+ * Set the map property for the control and all handlers.
+ *
+ * Parameters:
+ * map - {<OpenLayers.Map>} The control's map.
+ */
+ setMap: function(map) {
+ this.selectControl.setMap(map);
+ this.dragControl.setMap(map);
+ OpenLayers.Control.prototype.setMap.apply(this, arguments);
+ },
+
+ CLASS_NAME: "OpenLayers.Control.ModifyFeature"
+});
+
+/**
+ * Constant: RESHAPE
+ * {Integer} Constant used to make the control work in reshape mode
+ */
+OpenLayers.Control.ModifyFeature.RESHAPE = 1;
+/**
+ * Constant: RESIZE
+ * {Integer} Constant used to make the control work in resize mode
+ */
+OpenLayers.Control.ModifyFeature.RESIZE = 2;
+/**
+ * Constant: ROTATE
+ * {Integer} Constant used to make the control work in rotate mode
+ */
+OpenLayers.Control.ModifyFeature.ROTATE = 4;
+/**
+ * Constant: DRAG
+ * {Integer} Constant used to make the control work in drag mode
+ */
+OpenLayers.Control.ModifyFeature.DRAG = 8;
+/* ======================================================================
OpenLayers/Control/Navigation.js
====================================================================== */
@@ -14711,6 +34938,12 @@
*/
dragPan: null,
+ /**
+ * APIProprety: dragPanOptions
+ * {Object} Options passed to the DragPan control.
+ */
+ dragPanOptions: null,
+
/**
* Property: zoomBox
* {<OpenLayers.Control.ZoomBox>}
@@ -14724,6 +34957,12 @@
zoomWheelEnabled: true,
/**
+ * APIProperty: handleRightClicks
+ * {Boolean} Whether or not to handle right clicks. Default is false.
+ */
+ handleRightClicks: true,
+
+ /**
* Constructor: OpenLayers.Control.Navigation
* Create a new navigation control
*
@@ -14785,13 +35024,25 @@
* Method: draw
*/
draw: function() {
- this.handlers.click = new OpenLayers.Handler.Click(this,
- { 'dblclick': this.defaultDblClick },
- {
- 'double': true,
- 'stopDouble': true
- });
- this.dragPan = new OpenLayers.Control.DragPan({map: this.map});
+ // disable right mouse context menu for support of right click events
+ if (this.handleRightClicks) {
+ this.map.div.oncontextmenu = function () { return false;};
+ }
+
+ var clickCallbacks = {
+ 'dblclick': this.defaultDblClick,
+ 'dblrightclick': this.defaultDblRightClick
+ };
+ var clickOptions = {
+ 'double': true,
+ 'stopDouble': true
+ };
+ this.handlers.click = new OpenLayers.Handler.Click(
+ this, clickCallbacks, clickOptions
+ );
+ this.dragPan = new OpenLayers.Control.DragPan(
+ OpenLayers.Util.extend({map: this.map}, this.dragPanOptions)
+ );
this.zoomBox = new OpenLayers.Control.ZoomBox(
{map: this.map, keyMask: OpenLayers.Handler.MOD_SHIFT});
this.dragPan.draw();
@@ -14814,6 +35065,17 @@
},
/**
+ * Method: defaultRightDblClick
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ defaultDblRightClick: function (evt) {
+ var newCenter = this.map.getLonLatFromViewPortPx( evt.xy );
+ this.map.setCenter(newCenter, this.map.zoom - 1);
+ },
+
+ /**
* Method: wheelChange
*
* Parameters:
@@ -14881,6 +35143,546 @@
CLASS_NAME: "OpenLayers.Control.Navigation"
});
/* ======================================================================
+ OpenLayers/Filter.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.Filter
+ * This class represents an OGC Filter.
+ */
+OpenLayers.Filter = OpenLayers.Class({
+
+ /**
+ * Constructor: OpenLayers.Filter
+ * This is an abstract class. Create an instance of a filter subclass.
+ *
+ * Parameters:
+ * options - {Object} Optional object whose properties will be set on the
+ * instance.
+ *
+ * Returns:
+ * {<OpenLayers.Filter>}
+ */
+ initialize: function(options) {
+ OpenLayers.Util.extend(this, options);
+ },
+
+ /**
+ * APIMethod: destroy
+ * Remove reference to anything added.
+ */
+ destroy: function() {
+ },
+
+ /**
+ * APIMethod: evaluate
+ * Evaluates this filter in a specific context. Should be implemented by
+ * subclasses.
+ *
+ * Parameters:
+ * context - {Object} Context to use in evaluating the filter.
+ *
+ * Returns:
+ * {Boolean} The filter applies.
+ */
+ evaluate: function(context) {
+ return true;
+ },
+
+ CLASS_NAME: "OpenLayers.Filter"
+});
+/* ======================================================================
+ OpenLayers/Geometry.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/Format/WKT.js
+ * @requires OpenLayers/Feature/Vector.js
+ */
+
+/**
+ * Class: OpenLayers.Geometry
+ * A Geometry is a description of a geographic object. Create an instance of
+ * this class with the <OpenLayers.Geometry> constructor. This is a base class,
+ * typical geometry types are described by subclasses of this class.
+ */
+OpenLayers.Geometry = OpenLayers.Class({
+
+ /**
+ * Property: id
+ * {String} A unique identifier for this geometry.
+ */
+ id: null,
+
+ /**
+ * Property: parent
+ * {<OpenLayers.Geometry>}This is set when a Geometry is added as component
+ * of another geometry
+ */
+ parent: null,
+
+ /**
+ * Property: bounds
+ * {<OpenLayers.Bounds>} The bounds of this geometry
+ */
+ bounds: null,
+
+ /**
+ * Constructor: OpenLayers.Geometry
+ * Creates a geometry object.
+ */
+ initialize: function() {
+ this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME+ "_");
+ },
+
+ /**
+ * Method: destroy
+ * Destroy this geometry.
+ */
+ destroy: function() {
+ this.id = null;
+ this.bounds = null;
+ },
+
+ /**
+ * APIMethod: clone
+ * Create a clone of this geometry. Does not set any non-standard
+ * properties of the cloned geometry.
+ *
+ * Returns:
+ * {<OpenLayers.Geometry>} An exact clone of this geometry.
+ */
+ clone: function() {
+ return new OpenLayers.Geometry();
+ },
+
+ /**
+ * Set the bounds for this Geometry.
+ *
+ * Parameters:
+ * object - {<OpenLayers.Bounds>}
+ */
+ setBounds: function(bounds) {
+ if (bounds) {
+ this.bounds = bounds.clone();
+ }
+ },
+
+ /**
+ * Method: clearBounds
+ * Nullify this components bounds and that of its parent as well.
+ */
+ clearBounds: function() {
+ this.bounds = null;
+ if (this.parent) {
+ this.parent.clearBounds();
+ }
+ },
+
+ /**
+ * Method: extendBounds
+ * Extend the existing bounds to include the new bounds.
+ * If geometry's bounds is not yet set, then set a new Bounds.
+ *
+ * Parameters:
+ * newBounds - {<OpenLayers.Bounds>}
+ */
+ extendBounds: function(newBounds){
+ var bounds = this.getBounds();
+ if (!bounds) {
+ this.setBounds(newBounds);
+ } else {
+ this.bounds.extend(newBounds);
+ }
+ },
+
+ /**
+ * APIMethod: getBounds
+ * Get the bounds for this Geometry. If bounds is not set, it
+ * is calculated again, this makes queries faster.
+ *
+ * Returns:
+ * {<OpenLayers.Bounds>}
+ */
+ getBounds: function() {
+ if (this.bounds == null) {
+ this.calculateBounds();
+ }
+ return this.bounds;
+ },
+
+ /**
+ * APIMethod: calculateBounds
+ * Recalculate the bounds for the geometry.
+ */
+ calculateBounds: function() {
+ //
+ // This should be overridden by subclasses.
+ //
+ },
+
+ /**
+ * Method: atPoint
+ * Note - This is only an approximation based on the bounds of the
+ * geometry.
+ *
+ * Parameters:
+ * lonlat - {<OpenLayers.LonLat>}
+ * toleranceLon - {float} Optional tolerance in Geometric Coords
+ * toleranceLat - {float} Optional tolerance in Geographic Coords
+ *
+ * Returns:
+ * {Boolean} Whether or not the geometry is at the specified location
+ */
+ atPoint: function(lonlat, toleranceLon, toleranceLat) {
+ var atPoint = false;
+ var bounds = this.getBounds();
+ if ((bounds != null) && (lonlat != null)) {
+
+ var dX = (toleranceLon != null) ? toleranceLon : 0;
+ var dY = (toleranceLat != null) ? toleranceLat : 0;
+
+ var toleranceBounds =
+ new OpenLayers.Bounds(this.bounds.left - dX,
+ this.bounds.bottom - dY,
+ this.bounds.right + dX,
+ this.bounds.top + dY);
+
+ atPoint = toleranceBounds.containsLonLat(lonlat);
+ }
+ return atPoint;
+ },
+
+ /**
+ * Method: getLength
+ * Calculate the length of this geometry. This method is defined in
+ * subclasses.
+ *
+ * Returns:
+ * {Float} The length of the collection by summing its parts
+ */
+ getLength: function() {
+ //to be overridden by geometries that actually have a length
+ //
+ return 0.0;
+ },
+
+ /**
+ * Method: getArea
+ * Calculate the area of this geometry. This method is defined in subclasses.
+ *
+ * Returns:
+ * {Float} The area of the collection by summing its parts
+ */
+ getArea: function() {
+ //to be overridden by geometries that actually have an area
+ //
+ return 0.0;
+ },
+
+ /**
+ * Method: toString
+ * Returns the Well-Known Text representation of a geometry
+ *
+ * Returns:
+ * {String} Well-Known Text
+ */
+ toString: function() {
+ return OpenLayers.Format.WKT.prototype.write(
+ new OpenLayers.Feature.Vector(this)
+ );
+ },
+
+ CLASS_NAME: "OpenLayers.Geometry"
+});
+
+
+/**
+ * Method: OpenLayers.Geometry.segmentsIntersect
+ * Determine whether two line segments intersect. Optionally calculates
+ * and returns the intersection point. This function is optimized for
+ * cases where seg1.x2 >= seg2.x1 || seg2.x2 >= seg1.x1. In those
+ * obvious cases where there is no intersection, the function should
+ * not be called.
+ *
+ * Parameters:
+ * seg1 - {Object} Object representing a segment with properties x1, y1, x2,
+ * and y2. The start point is represented by x1 and y1. The end point
+ * is represented by x2 and y2. Start and end are ordered so that x1 < x2.
+ * seg2 - {Object} Object representing a segment with properties x1, y1, x2,
+ * and y2. The start point is represented by x1 and y1. The end point
+ * is represented by x2 and y2. Start and end are ordered so that x1 < x2.
+ * point - {Boolean} Return the intersection point. If false, the actual
+ * intersection point will not be calculated. If true and the segments
+ * intersect, the intersection point will be returned. If true and
+ * the segments do not intersect, false will be returned. If true and
+ * the segments are coincident, true will be returned.
+ *
+ * Returns:
+ * {Boolean | <OpenLayers.Geometry.Point>} The two segments intersect.
+ * If the point argument is true, the return will be the intersection
+ * point or false if none exists. If point is true and the segments
+ * are coincident, return will be true (and the instersection is equal
+ * to the shorter segment).
+ */
+OpenLayers.Geometry.segmentsIntersect = function(seg1, seg2, point) {
+ var intersection = false;
+ var x11_21 = seg1.x1 - seg2.x1;
+ var y11_21 = seg1.y1 - seg2.y1;
+ var x12_11 = seg1.x2 - seg1.x1;
+ var y12_11 = seg1.y2 - seg1.y1;
+ var y22_21 = seg2.y2 - seg2.y1;
+ var x22_21 = seg2.x2 - seg2.x1;
+ var d = (y22_21 * x12_11) - (x22_21 * y12_11);
+ var n1 = (x22_21 * y11_21) - (y22_21 * x11_21);
+ var n2 = (x12_11 * y11_21) - (y12_11 * x11_21);
+ if(d == 0) {
+ // parallel
+ if(n1 == 0 && n2 == 0) {
+ // coincident
+ intersection = true;
+ }
+ } else {
+ var along1 = n1 / d;
+ var along2 = n2 / d;
+ if(along1 >= 0 && along1 <= 1 && along2 >=0 && along2 <= 1) {
+ // intersect
+ if(!point) {
+ intersection = true;
+ } else {
+ // calculate the intersection point
+ var x = seg1.x1 + (along1 * x12_11);
+ var y = seg1.y1 + (along1 * y12_11);
+ intersection = new OpenLayers.Geometry.Point(x, y);
+ }
+ }
+ }
+ return intersection;
+};
+/* ======================================================================
+ OpenLayers/Layer/KaMap.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/Layer/Grid.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.KaMap
+ *
+ * Inherits from:
+ * - <OpenLayers.Layer.Grid>
+ */
+OpenLayers.Layer.KaMap = OpenLayers.Class(OpenLayers.Layer.Grid, {
+
+ /**
+ * APIProperty: isBaseLayer
+ * {Boolean} KaMap Layer is always a base layer
+ */
+ isBaseLayer: true,
+
+ /**
+ * APIProperty: units
+ * {?}
+ */
+ units: null,
+
+ /**
+ * APIProperty: resolution
+ * {Float}
+ */
+ resolution: OpenLayers.DOTS_PER_INCH,
+
+ /**
+ * Constant: DEFAULT_PARAMS
+ * {Object} parameters set by default. The default parameters set
+ * the format via the 'i' parameter to 'jpeg'.
+ */
+ DEFAULT_PARAMS: {
+ i: 'jpeg',
+ map: ''
+ },
+
+ /**
+ * Constructor: OpenLayers.Layer.KaMap
+ *
+ * Parameters:
+ * name - {String}
+ * url - {String}
+ * params - {Object} Parameters to be sent to the HTTP server in the
+ * query string for the tile. The format can be set via the 'i'
+ * parameter (defaults to jpg) , and the map should be set via
+ * the 'map' parameter. It has been reported that ka-Map may behave
+ * inconsistently if your format parameter does not match the format
+ * parameter configured in your config.php. (See ticket #327 for more
+ * information.)
+ * options - {Object} Additional options for the layer. Any of the
+ * APIProperties listed on this layer, and any layer types it
+ * extends, can be overridden through the options parameter.
+ */
+ initialize: function(name, url, params, options) {
+ var newArguments = [];
+ newArguments.push(name, url, params, options);
+ OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);
+ this.params = OpenLayers.Util.applyDefaults(
+ this.params, this.DEFAULT_PARAMS
+ );
+ },
+
+ /**
+ * Method: getURL
+ *
+ * Parameters:
+ * bounds - {<OpenLayers.Bounds>}
+ *
+ * Returns:
+ * {String} A string with the layer's url and parameters and also the
+ * passed-in bounds and appropriate tile size specified as
+ * parameters
+ */
+ getURL: function (bounds) {
+ bounds = this.adjustBounds(bounds);
+ var mapRes = this.map.getResolution();
+ var scale = Math.round((this.map.getScale() * 10000)) / 10000;
+ var pX = Math.round(bounds.left / mapRes);
+ var pY = -Math.round(bounds.top / mapRes);
+ return this.getFullRequestString(
+ { t: pY,
+ l: pX,
+ s: scale
+ });
+ },
+
+ /**
+ * Method: addTile
+ *
+ * Parameters:
+ * bounds - {<OpenLayers.Bounds>}
+ * position - {<OpenLayers.Pixel>}
+ *
+ * Returns:
+ * {<OpenLayers.Tile.Image>}
+ */
+ addTile:function(bounds,position) {
+ var url = this.getURL(bounds);
+ return new OpenLayers.Tile.Image(this, position, bounds,
+ url, this.tileSize);
+ },
+
+ /**
+ * Method: calculateGridLayout
+ * ka-Map uses the center point of the map as an origin for
+ * its tiles. Override calculateGridLayout to center tiles
+ * correctly for this case.
+ *
+ * Parameters:
+ * bounds - {<OpenLayers.Bound>}
+ * extent - {<OpenLayers.Bounds>}
+ * resolution - {Number}
+ *
+ * Returns:
+ * Object containing properties tilelon, tilelat, tileoffsetlat,
+ * tileoffsetlat, tileoffsetx, tileoffsety
+ */
+ calculateGridLayout: function(bounds, extent, resolution) {
+ var tilelon = resolution*this.tileSize.w;
+ var tilelat = resolution*this.tileSize.h;
+
+ var offsetlon = bounds.left;
+ var tilecol = Math.floor(offsetlon/tilelon) - this.buffer;
+ var tilecolremain = offsetlon/tilelon - tilecol;
+ var tileoffsetx = -tilecolremain * this.tileSize.w;
+ var tileoffsetlon = tilecol * tilelon;
+
+ var offsetlat = bounds.top;
+ var tilerow = Math.ceil(offsetlat/tilelat) + this.buffer;
+ var tilerowremain = tilerow - offsetlat/tilelat;
+ var tileoffsety = -(tilerowremain+1) * this.tileSize.h;
+ var tileoffsetlat = tilerow * tilelat;
+
+ return {
+ tilelon: tilelon, tilelat: tilelat,
+ tileoffsetlon: tileoffsetlon, tileoffsetlat: tileoffsetlat,
+ tileoffsetx: tileoffsetx, tileoffsety: tileoffsety
+ };
+ },
+
+ /**
+ * APIMethod: clone
+ *
+ * Parameters:
+ * obj - {Object}
+ *
+ * Returns:
+ * {<OpenLayers.Layer.Kamap>} An exact clone of this OpenLayers.Layer.KaMap
+ */
+ clone: function (obj) {
+
+ if (obj == null) {
+ obj = new OpenLayers.Layer.KaMap(this.name,
+ this.url,
+ this.params,
+ this.options);
+ }
+
+ //get all additions from superclasses
+ obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);
+
+ // copy/set any non-init, non-simple values here
+ if (this.tileSize != null) {
+ obj.tileSize = this.tileSize.clone();
+ }
+
+ // we do not want to copy reference to grid, so we make a new array
+ obj.grid = [];
+
+ return obj;
+ },
+
+ /**
+ * APIMethod: getTileBounds
+ * Returns The tile bounds for a layer given a pixel location.
+ *
+ * Parameters:
+ * viewPortPx - {<OpenLayers.Pixel>} The location in the viewport.
+ *
+ * Returns:
+ * {<OpenLayers.Bounds>} Bounds of the tile at the given pixel location.
+ */
+ getTileBounds: function(viewPortPx) {
+ var resolution = this.getResolution();
+ var tileMapWidth = resolution * this.tileSize.w;
+ var tileMapHeight = resolution * this.tileSize.h;
+ var mapPoint = this.getLonLatFromViewPortPx(viewPortPx);
+ var tileLeft = tileMapWidth * Math.floor(mapPoint.lon / tileMapWidth);
+ var tileBottom = tileMapHeight * Math.floor(mapPoint.lat / tileMapHeight);
+ return new OpenLayers.Bounds(tileLeft, tileBottom,
+ tileLeft + tileMapWidth,
+ tileBottom + tileMapHeight);
+ },
+
+ CLASS_NAME: "OpenLayers.Layer.KaMap"
+});
+/* ======================================================================
OpenLayers/Layer/MapGuide.js
====================================================================== */
@@ -14889,7 +35691,7 @@
* full text of the license. */
/**
- * @requires OpenLayers/Ajax.js
+ * @requires OpenLayers/Request/XMLHttpRequest.js
* @requires OpenLayers/Layer/Grid.js
*/
@@ -14914,7 +35716,7 @@
* {Boolean} use tile server or request single tile image. Note that using
* singleTile *and* isBaseLayer false is *not recommend*: it uses synchronous
* XMLHttpRequests to load tiles, and this will *lock up users browsers*
- * during requests.
+ * during requests if the server fails to respond.
**/
singleTile: false,
@@ -14949,18 +35751,29 @@
* Constructor: OpenLayers.Layer.MapGuide
* Create a new Mapguide layer, either tiled or untiled.
*
- * For tiled layers, the 'groupName' and 'mapDefnition' options
- * must be specified as options.
+ * For tiled layers, the 'groupName' and 'mapDefinition' values
+ * must be specified as parameters in the constructor.
*
- * For untiled layers, specify either combination of 'mapName' and
- * 'session', or 'mapDefinition' and 'locale'.
+ * For untiled base layers, specify either combination of 'mapName' and
+ * 'session', or 'mapDefinition' and 'locale'.
*
+ * For untiled overlay layers (singleTile=true and isBaseLayer=false),
+ * mapName and session are required parameters for the Layer constructor.
+ * Also NOTE: untiled overlay layers issues a synchronous AJAX request
+ * before the image request can be issued so the users browser may lock
+ * up if the MG Web tier does not respond in a timely fashion.
+ *
+ * NOTE: MapGuide OS uses a DPI value and degrees to meters conversion
+ * factor that are different than the defaults used in OpenLayers,
+ * so these must be adjusted accordingly in your application.
+ * See the MapGuide example for how to set these values for MGOS.
+ *
* Parameters:
* name - {String} Name of the layer displayed in the interface
* url - {String} Location of the MapGuide mapagent executable
* (e.g. http://localhost:8008/mapguide/mapagent/mapagent.fcgi)
* params - {Object} hashtable of additional parameters to use. Some
- * parameters may require additional code on the serer. The ones that
+ * parameters may require additional code on the server. The ones that
* you may want to use are:
* - mapDefinition - {String} The MapGuide resource definition
* (e.g. Library://Samples/Gmap/Maps/gmapTiled.MapDefinition)
@@ -15002,6 +35815,7 @@
this.params,
this.SINGLE_TILE_PARAMS
);
+
} else {
//initialize for tiled layers
OpenLayers.Util.applyDefaults(
@@ -15082,18 +35896,15 @@
//but we first need to call GETVISIBLEMAPEXTENT to set the extent
var getVisParams = {};
+ getVisParams = OpenLayers.Util.extend(getVisParams, params);
getVisParams.operation = "GETVISIBLEMAPEXTENT";
getVisParams.version = "1.0.0";
getVisParams.session = this.params.session;
getVisParams.mapName = this.params.mapName;
getVisParams.format = 'text/xml';
- getVisParams = OpenLayers.Util.extend(getVisParams, params);
-
- new OpenLayers.Ajax.Request(this.url,
- { parameters: getVisParams,
- method: 'get',
- asynchronous: false //must be synchronous call to return control here
- });
+ url = this.getFullRequestString( getVisParams );
+
+ OpenLayers.Request.GET({url: url, async: false});
}
//construct the full URL
@@ -15113,7 +35924,7 @@
tilerow: rowidx,
scaleindex: this.resolutions.length - this.map.zoom - 1
});
- }
+ }
return url;
},
@@ -15268,12 +36079,9 @@
newArguments.push(name, url, params, options);
OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);
- if (arguments.length > 0) {
- OpenLayers.Util.applyDefaults(
- this.params,
- this.DEFAULT_PARAMS
- );
- }
+ this.params = OpenLayers.Util.applyDefaults(
+ this.params, this.DEFAULT_PARAMS
+ );
// unless explicitly set in options, if the layer is transparent,
// it will be an overlay
@@ -15426,6 +36234,334 @@
CLASS_NAME: "OpenLayers.Layer.MapServer"
});
/* ======================================================================
+ OpenLayers/Layer/TMS.js
+ ====================================================================== */
+
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * licence. See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+
+/**
+ * @requires OpenLayers/Layer/Grid.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.TMS
+ *
+ * Inherits from:
+ * - <OpenLayers.Layer.Grid>
+ */
+OpenLayers.Layer.TMS = OpenLayers.Class(OpenLayers.Layer.Grid, {
+
+ /**
+ * APIProperty: serviceVersion
+ * {String}
+ */
+ serviceVersion: "1.0.0",
+
+ /**
+ * APIProperty: isBaseLayer
+ * {Boolean}
+ */
+ isBaseLayer: true,
+
+ /**
+ * APIProperty: tileOrigin
+ * {<OpenLayers.Pixel>}
+ */
+ tileOrigin: null,
+
+ /**
+ * Constructor: OpenLayers.Layer.TMS
+ *
+ * Parameters:
+ * name - {String}
+ * url - {String}
+ * options - {Object} Hashtable of extra options to tag onto the layer
+ */
+ initialize: function(name, url, options) {
+ var newArguments = [];
+ newArguments.push(name, url, {}, options);
+ OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);
+ },
+
+ /**
+ * APIMethod:destroy
+ */
+ destroy: function() {
+ // for now, nothing special to do here.
+ OpenLayers.Layer.Grid.prototype.destroy.apply(this, arguments);
+ },
+
+
+ /**
+ * APIMethod: clone
+ *
+ * Parameters:
+ * obj - {Object}
+ *
+ * Returns:
+ * {<OpenLayers.Layer.TMS>} An exact clone of this <OpenLayers.Layer.TMS>
+ */
+ clone: function (obj) {
+
+ if (obj == null) {
+ obj = new OpenLayers.Layer.TMS(this.name,
+ this.url,
+ this.options);
+ }
+
+ //get all additions from superclasses
+ obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);
+
+ // copy/set any non-init, non-simple values here
+
+ return obj;
+ },
+
+ /**
+ * Method: getURL
+ *
+ * Parameters:
+ * bounds - {<OpenLayers.Bounds>}
+ *
+ * Returns:
+ * {String} A string with the layer's url and parameters and also the
+ * passed-in bounds and appropriate tile size specified as
+ * parameters
+ */
+ getURL: function (bounds) {
+ bounds = this.adjustBounds(bounds);
+ var res = this.map.getResolution();
+ var x = Math.round((bounds.left - this.tileOrigin.lon) / (res * this.tileSize.w));
+ var y = Math.round((bounds.bottom - this.tileOrigin.lat) / (res * this.tileSize.h));
+ var z = this.map.getZoom();
+ var path = this.serviceVersion + "/" + this.layername + "/" + z + "/" + x + "/" + y + "." + this.type;
+ var url = this.url;
+ if (url instanceof Array) {
+ url = this.selectUrl(path, url);
+ }
+ return url + path;
+ },
+
+ /**
+ * Method: addTile
+ * addTile creates a tile, initializes it, and adds it to the layer div.
+ *
+ * Parameters:
+ * bounds - {<OpenLayers.Bounds>}
+ * position - {<OpenLayers.Pixel>}
+ *
+ * Returns:
+ * {<OpenLayers.Tile.Image>} The added OpenLayers.Tile.Image
+ */
+ addTile:function(bounds,position) {
+ return new OpenLayers.Tile.Image(this, position, bounds,
+ null, this.tileSize);
+ },
+
+ /**
+ * APIMethod: setMap
+ * When the layer is added to a map, then we can fetch our origin
+ * (if we don't have one.)
+ *
+ * Parameters:
+ * map - {<OpenLayers.Map>}
+ */
+ setMap: function(map) {
+ OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);
+ if (!this.tileOrigin) {
+ this.tileOrigin = new OpenLayers.LonLat(this.map.maxExtent.left,
+ this.map.maxExtent.bottom);
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Layer.TMS"
+});
+/* ======================================================================
+ OpenLayers/Layer/TileCache.js
+ ====================================================================== */
+
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * licence. See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+
+/**
+ * @requires OpenLayers/Layer/Grid.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.TileCache
+ * A read only TileCache layer. Used to requests tiles cached by TileCache in
+ * a web accessible cache. This means that you have to pre-populate your
+ * cache before this layer can be used. It is meant only to read tiles
+ * created by TileCache, and not to make calls to TileCache for tile
+ * creation. Create a new instance with the
+ * <OpenLayers.Layer.TileCache> constructor.
+ *
+ * Inherits from:
+ * - <OpenLayers.Layer.Grid>
+ */
+OpenLayers.Layer.TileCache = OpenLayers.Class(OpenLayers.Layer.Grid, {
+
+ /**
+ * APIProperty: isBaseLayer
+ * {Boolean} Treat this layer as a base layer. Default is true.
+ */
+ isBaseLayer: true,
+
+ /**
+ * APIProperty: tileOrigin
+ * {<OpenLayers.LonLat>} Location of the tile lattice origin. Default is
+ * bottom left of the maxExtent.
+ */
+ tileOrigin: null,
+
+ /**
+ * APIProperty: format
+ * {String} Mime type of the images returned. Default is image/png.
+ */
+ format: 'image/png',
+
+ /**
+ * Constructor: OpenLayers.Layer.TileCache
+ * Create a new read only TileCache layer.
+ *
+ * Parameters:
+ * name - {String} Name of the layer displayed in the interface
+ * url - {String} Location of the web accessible cache (not the location of
+ * your tilecache script!)
+ * layername - {String} Layer name as defined in the TileCache
+ * configuration
+ * options - {Object} Optional object with properties to be set on the
+ * layer. Note that you should speficy your resolutions to match
+ * your TileCache configuration. This can be done by setting
+ * the resolutions array directly (here or on the map), by setting
+ * maxResolution and numZoomLevels, or by using scale based properties.
+ */
+ initialize: function(name, url, layername, options) {
+ this.layername = layername;
+ OpenLayers.Layer.Grid.prototype.initialize.apply(this,
+ [name, url, {}, options]);
+ this.extension = this.format.split('/')[1].toLowerCase();
+ this.extension = (this.extension == 'jpg') ? 'jpeg' : this.extension;
+ },
+
+ /**
+ * APIMethod: clone
+ * obj - {Object}
+ *
+ * Returns:
+ * {<OpenLayers.Layer.TileCache>} An exact clone of this
+ * <OpenLayers.Layer.TileCache>
+ */
+ clone: function (obj) {
+
+ if (obj == null) {
+ obj = new OpenLayers.Layer.TileCache(this.name,
+ this.url,
+ this.layername,
+ this.options);
+ }
+
+ //get all additions from superclasses
+ obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);
+
+ // copy/set any non-init, non-simple values here
+
+ return obj;
+ },
+
+ /**
+ * Method: getURL
+ *
+ * Parameters:
+ * bounds - {<OpenLayers.Bounds>}
+ *
+ * Returns:
+ * {String} A string with the layer's url and parameters and also the
+ * passed-in bounds and appropriate tile size specified as parameters.
+ */
+ getURL: function(bounds) {
+ var res = this.map.getResolution();
+ var bbox = this.maxExtent;
+ var size = this.tileSize;
+ var tileX = Math.round((bounds.left - bbox.left) / (res * size.w));
+ var tileY = Math.round((bounds.bottom - bbox.bottom) / (res * size.h));
+ var tileZ = this.map.zoom;
+ /**
+ * Zero-pad a positive integer.
+ * number - {Int}
+ * length - {Int}
+ *
+ * Returns:
+ * {String} A zero-padded string
+ */
+ function zeroPad(number, length) {
+ number = String(number);
+ var zeros = [];
+ for(var i=0; i<length; ++i) {
+ zeros.push('0');
+ }
+ return zeros.join('').substring(0, length - number.length) + number;
+ }
+ var components = [
+ this.layername,
+ zeroPad(tileZ, 2),
+ zeroPad(parseInt(tileX / 1000000), 3),
+ zeroPad((parseInt(tileX / 1000) % 1000), 3),
+ zeroPad((parseInt(tileX) % 1000), 3),
+ zeroPad(parseInt(tileY / 1000000), 3),
+ zeroPad((parseInt(tileY / 1000) % 1000), 3),
+ zeroPad((parseInt(tileY) % 1000), 3) + '.' + this.extension
+ ];
+ var path = components.join('/');
+ var url = this.url;
+ if (url instanceof Array) {
+ url = this.selectUrl(path, url);
+ }
+ url = (url.charAt(url.length - 1) == '/') ? url : url + '/';
+ return url + path;
+ },
+
+ /**
+ * Method: addTile
+ * Create a tile, initialize it, and add it to the layer div.
+ *
+ * Parameters:
+ * bounds - {<OpenLayers.Bounds>}
+ * position - {<OpenLayers.Pixel>}
+ *
+ * Returns:
+ * {<OpenLayers.Tile.Image>} The added <OpenLayers.Tile.Image>
+ */
+ addTile:function(bounds, position) {
+ var url = this.getURL(bounds);
+ return new OpenLayers.Tile.Image(this, position, bounds,
+ url, this.tileSize);
+ },
+
+ /**
+ * Method: setMap
+ * When the layer is added to a map, then we can fetch our origin
+ * (if we don't have one.)
+ *
+ * Parameters:
+ * map - {<OpenLayers.Map>}
+ */
+ setMap: function(map) {
+ OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);
+ if (!this.tileOrigin) {
+ this.tileOrigin = new OpenLayers.LonLat(this.map.maxExtent.left,
+ this.map.maxExtent.bottom);
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Layer.TileCache"
+});
+/* ======================================================================
OpenLayers/Layer/WMS.js
====================================================================== */
@@ -15617,7 +36753,8 @@
* Catch changeParams and uppercase the new params to be merged in
* before calling changeParams on the super class.
*
- * Once params have been changed, we will need to re-init our tiles.
+ * Once params have been changed, the tiles will be reloaded with
+ * the new parameters.
*
* Parameters:
* newParams - {Object} Hashtable of new params to use
@@ -15630,7 +36767,7 @@
},
/**
- * Method: getFullRequestString
+ * APIMethod: getFullRequestString
* Combine the layer's url with its params and these newParams.
*
* Add the SRS parameter from projection -- this is probably
@@ -15654,3 +36791,10969 @@
CLASS_NAME: "OpenLayers.Layer.WMS"
});
+/* ======================================================================
+ OpenLayers/Layer/WorldWind.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/Layer/Grid.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.WorldWind
+ *
+ * Inherits from:
+ * - <OpenLayers.Layer.Grid>
+ */
+OpenLayers.Layer.WorldWind = OpenLayers.Class(OpenLayers.Layer.Grid, {
+
+ DEFAULT_PARAMS: {
+ },
+
+ /**
+ * APIProperty: isBaseLayer
+ * WorldWind layer is a base layer by default.
+ */
+ isBaseLayer: true,
+
+
+ /**
+ * APIProperty: lzd
+ * LevelZeroTileSizeDegrees
+ */
+ lzd: null,
+
+ /**
+ * APIProperty: zoomLevels
+ * Number of zoom levels.
+ */
+ zoomLevels: null,
+
+ /**
+ * Constructor: OpenLayers.Layer.WorldWind
+ *
+ * Parameters:
+ * name - {String} Name of Layer
+ * url - {String} Base URL
+ * lzd - {Float} Level zero tile size degrees
+ * zoomLevels - {Int} number of zoom levels
+ * params - {Object} additional parameters
+ * options - {Object} additional options
+ */
+ initialize: function(name, url, lzd, zoomLevels, params, options) {
+ this.lzd = lzd;
+ this.zoomLevels = zoomLevels;
+ var newArguments = [];
+ newArguments.push(name, url, params, options);
+ OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);
+ this.params = OpenLayers.Util.applyDefaults(
+ this.params, this.DEFAULT_PARAMS
+ );
+ },
+ /**
+ * Method: addTile
+ *
+ * Parameters:
+ * bounds - {<OpenLayers.Bounds>}
+ * position - {<OpenLayers.Pixel>}
+ *
+ * Returns:
+ * {<OpenLayers.Tile.Image>} The added OpenLayers.Tile.Image
+ */
+ addTile:function(bounds,position) {
+ return new OpenLayers.Tile.Image(this, position, bounds,
+ null, this.tileSize);
+ },
+
+ /**
+ * Method: getZoom
+ * Convert map zoom to WW zoom.
+ */
+ getZoom: function () {
+ var zoom = this.map.getZoom();
+ var extent = this.map.getMaxExtent();
+ zoom = zoom - Math.log(this.maxResolution / (this.lzd/512))/Math.log(2);
+ return zoom;
+ },
+
+ /**
+ * Method: getURL
+ *
+ * Parameters:
+ * bounds - {<OpenLayers.Bounds>}
+ *
+ * Returns:
+ * {String} A string with the layer's url and parameters and also the
+ * passed-in bounds and appropriate tile size specified as
+ * parameters
+ */
+ getURL: function (bounds) {
+ bounds = this.adjustBounds(bounds);
+ var zoom = this.getZoom();
+ var extent = this.map.getMaxExtent();
+ var deg = this.lzd/Math.pow(2,this.getZoom());
+ var x = Math.floor((bounds.left - extent.left)/deg);
+ var y = Math.floor((bounds.bottom - extent.bottom)/deg);
+ if (this.map.getResolution() <= (this.lzd/512)
+ && this.getZoom() <= this.zoomLevels) {
+ return this.getFullRequestString(
+ { L: zoom,
+ X: x,
+ Y: y
+ });
+ } else {
+ return OpenLayers.Util.getImagesLocation() + "blank.gif";
+ }
+
+ },
+
+ CLASS_NAME: "OpenLayers.Layer.WorldWind"
+});
+/* ======================================================================
+ 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
+ ====================================================================== */
+
+/* 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/Style.js
+ * @requires OpenLayers/Feature/Vector.js
+ */
+
+/**
+ * Class: OpenLayers.StyleMap
+ */
+OpenLayers.StyleMap = OpenLayers.Class({
+
+ /**
+ * Property: styles
+ * Hash of {<OpenLayers.Style>}, keyed by names of well known
+ * rendering intents (e.g. "default", "temporary", "select").
+ */
+ styles: null,
+
+ /**
+ * Property: extendDefault
+ * {Boolean} if true, every render intent will extend the symbolizers
+ * specified for the "default" intent at rendering time. Otherwise, every
+ * rendering intent will be treated as a completely independent style.
+ */
+ extendDefault: true,
+
+ /**
+ * Constructor: OpenLayers.StyleMap
+ *
+ * Parameters:
+ * style - {Object} Optional. Either a style hash, or a style object, or
+ * a hash of style objects (style hashes) keyed by rendering
+ * intent. If just one style hash or style object is passed,
+ * this will be used for all known render intents (default,
+ * select, temporary)
+ * options - {Object} optional hash of additional options for this
+ * instance
+ */
+ initialize: function (style, options) {
+ this.styles = {
+ "default": new OpenLayers.Style(
+ OpenLayers.Feature.Vector.style["default"]),
+ "select": new OpenLayers.Style(
+ OpenLayers.Feature.Vector.style["select"]),
+ "temporary": new OpenLayers.Style(
+ OpenLayers.Feature.Vector.style["temporary"])
+ };
+
+ // take whatever the user passed as style parameter and convert it
+ // into parts of stylemap.
+ if(style instanceof OpenLayers.Style) {
+ // user passed a style object
+ this.styles["default"] = style;
+ this.styles["select"] = style;
+ this.styles["temporary"] = style;
+ } else if(typeof style == "object") {
+ for(var key in style) {
+ if(style[key] instanceof OpenLayers.Style) {
+ // user passed a hash of style objects
+ this.styles[key] = style[key];
+ } else if(typeof style[key] == "object") {
+ // user passsed a hash of style hashes
+ this.styles[key] = new OpenLayers.Style(style[key]);
+ } else {
+ // user passed a style hash (i.e. symbolizer)
+ this.styles["default"] = new OpenLayers.Style(style);
+ this.styles["select"] = new OpenLayers.Style(style);
+ this.styles["temporary"] = new OpenLayers.Style(style);
+ break;
+ }
+ }
+ }
+ OpenLayers.Util.extend(this, options);
+ },
+
+ /**
+ * Method: destroy
+ */
+ destroy: function() {
+ for(var key in this.styles) {
+ this.styles[key].destroy();
+ }
+ this.styles = null;
+ },
+
+ /**
+ * Method: createSymbolizer
+ * Creates the symbolizer for a feature for a render intent.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature>} The feature to evaluate the rules
+ * of the intended style against.
+ * intent - {String} The intent determines the symbolizer that will be
+ * used to draw the feature. Well known intents are "default"
+ * (for just drawing the features), "select" (for selected
+ * features) and "temporary" (for drawing features).
+ *
+ * Returns:
+ * {Object} symbolizer hash
+ */
+ createSymbolizer: function(feature, intent) {
+ if(!feature) {
+ feature = new OpenLayers.Feature.Vector();
+ }
+ if(!this.styles[intent]) {
+ intent = "default";
+ }
+ feature.renderIntent = intent;
+ var defaultSymbolizer = {};
+ if(this.extendDefault && intent != "default") {
+ defaultSymbolizer = this.styles["default"].createSymbolizer(feature);
+ }
+ return OpenLayers.Util.extend(defaultSymbolizer,
+ this.styles[intent].createSymbolizer(feature));
+ },
+
+ /**
+ * Method: addUniqueValueRules
+ * Convenience method to create comparison rules for unique values of a
+ * property. The rules will be added to the style object for a specified
+ * rendering intent. This method is a shortcut for creating something like
+ * the "unique value legends" familiar from well known desktop GIS systems
+ *
+ * Parameters:
+ * renderIntent - {String} rendering intent to add the rules to
+ * property - {String} values of feature attributes to create the
+ * rules for
+ * symbolizers - {Object} Hash of symbolizers, keyed by the desired
+ * property values
+ * context - {Object} An optional object with properties that
+ * symbolizers' property values should be evaluated
+ * against. If no context is specified, feature.attributes
+ * will be used
+ */
+ addUniqueValueRules: function(renderIntent, property, symbolizers, context) {
+ var rules = [];
+ for (var value in symbolizers) {
+ rules.push(new OpenLayers.Rule({
+ symbolizer: symbolizers[value],
+ context: context,
+ filter: new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.EQUAL_TO,
+ property: property,
+ value: value
+ })
+ }));
+ }
+ this.styles[renderIntent].addRules(rules);
+ },
+
+ CLASS_NAME: "OpenLayers.StyleMap"
+});
+/* ======================================================================
+ OpenLayers/Control/NavToolbar.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/Panel.js
+ * @requires OpenLayers/Control/Navigation.js
+ * @requires OpenLayers/Control/ZoomBox.js
+ */
+
+/**
+ * Class: OpenLayers.Control.NavToolbar
+ *
+ * Inherits from:
+ * - <OpenLayers.Control.Panel>
+ */
+OpenLayers.Control.NavToolbar = OpenLayers.Class(OpenLayers.Control.Panel, {
+
+ /**
+ * Constructor: OpenLayers.Control.NavToolbar
+ * Add our two mousedefaults controls.
+ *
+ * Parameters:
+ * options - {Object} An optional object whose properties will be used
+ * to extend the control.
+ */
+ initialize: function(options) {
+ OpenLayers.Control.Panel.prototype.initialize.apply(this, [options]);
+ this.addControls([
+ new OpenLayers.Control.Navigation(),
+ new OpenLayers.Control.ZoomBox()
+ ]);
+ },
+
+ /**
+ * Method: draw
+ * calls the default draw, and then activates mouse defaults.
+ */
+ draw: function() {
+ var div = OpenLayers.Control.Panel.prototype.draw.apply(this, arguments);
+ this.activateControl(this.controls[0]);
+ return div;
+ },
+
+ CLASS_NAME: "OpenLayers.Control.NavToolbar"
+});
+/* ======================================================================
+ OpenLayers/Filter/Comparison.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/Filter.js
+ */
+
+/**
+ * Class: OpenLayers.Filter.Comparison
+ * This class represents a comparison filter.
+ *
+ * Inherits from
+ * - <OpenLayers.Filter>
+ */
+OpenLayers.Filter.Comparison = OpenLayers.Class(OpenLayers.Filter, {
+
+ /**
+ * APIProperty: type
+ * {String} type: type of the comparison. This is one of
+ * - OpenLayers.Filter.Comparison.EQUAL_TO = "==";
+ * - OpenLayers.Filter.Comparison.NOT_EQUAL_TO = "!=";
+ * - OpenLayers.Filter.Comparison.LESS_THAN = "<";
+ * - OpenLayers.Filter.Comparison.GREATER_THAN = ">";
+ * - OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO = "<=";
+ * - OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO = ">=";
+ * - OpenLayers.Filter.Comparison.BETWEEN = "..";
+ * - OpenLayers.Filter.Comparison.LIKE = "~";
+ */
+ type: null,
+
+ /**
+ * APIProperty: property
+ * {String}
+ * name of the context property to compare
+ */
+ property: null,
+
+ /**
+ * APIProperty: value
+ * {Number} or {String}
+ * comparison value for binary comparisons. In the case of a String, this
+ * can be a combination of text and propertyNames in the form
+ * "literal ${propertyName}"
+ */
+ value: null,
+
+ /**
+ * APIProperty: lowerBoundary
+ * {Number} or {String}
+ * lower boundary for between comparisons. In the case of a String, this
+ * can be a combination of text and propertyNames in the form
+ * "literal ${propertyName}"
+ */
+ lowerBoundary: null,
+
+ /**
+ * APIProperty: upperBoundary
+ * {Number} or {String}
+ * upper boundary for between comparisons. In the case of a String, this
+ * can be a combination of text and propertyNames in the form
+ * "literal ${propertyName}"
+ */
+ upperBoundary: null,
+
+ /**
+ * Constructor: OpenLayers.Filter.Comparison
+ * Creates a comparison rule.
+ *
+ * Parameters:
+ * options - {Object} An optional object with properties to set on the
+ * rule
+ *
+ * Returns:
+ * {<OpenLayers.Filter.Comparison>}
+ */
+ initialize: function(options) {
+ OpenLayers.Filter.prototype.initialize.apply(this, [options]);
+ },
+
+ /**
+ * APIMethod: evaluate
+ * Evaluates this filter in a specific context. Should be implemented by
+ * subclasses.
+ *
+ * Parameters:
+ * context - {Object} Context to use in evaluating the filter.
+ *
+ * Returns:
+ * {Boolean} The filter applies.
+ */
+ evaluate: function(context) {
+ switch(this.type) {
+ case OpenLayers.Filter.Comparison.EQUAL_TO:
+ case OpenLayers.Filter.Comparison.LESS_THAN:
+ case OpenLayers.Filter.Comparison.GREATER_THAN:
+ case OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO:
+ case OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO:
+ return this.binaryCompare(context, this.property, this.value);
+
+ case OpenLayers.Filter.Comparison.BETWEEN:
+ var result =
+ context[this.property] >= this.lowerBoundary;
+ result = result &&
+ context[this.property] <= this.upperBoundary;
+ return result;
+ case OpenLayers.Filter.Comparison.LIKE:
+ var regexp = new RegExp(this.value,
+ "gi");
+ return regexp.test(context[this.property]);
+ }
+ },
+
+ /**
+ * APIMethod: value2regex
+ * Converts the value of this rule into a regular expression string,
+ * according to the wildcard characters specified. This method has to
+ * be called after instantiation of this class, if the value is not a
+ * regular expression already.
+ *
+ * Parameters:
+ * wildCard - {<Char>} wildcard character in the above value, default
+ * is "*"
+ * singleChar - {<Char>) single-character wildcard in the above value
+ * default is "."
+ * escape - {<Char>) escape character in the above value, default is
+ * "!"
+ *
+ * Returns:
+ * {String} regular expression string
+ */
+ value2regex: function(wildCard, singleChar, escapeChar) {
+ if (wildCard == ".") {
+ var msg = "'.' is an unsupported wildCard character for "+
+ "OpenLayers.Filter.Comparison";
+ OpenLayers.Console.error(msg);
+ return null;
+ }
+
+
+ // set UMN MapServer defaults for unspecified parameters
+ wildCard = wildCard ? wildCard : "*";
+ singleChar = singleChar ? singleChar : ".";
+ escapeChar = escapeChar ? escapeChar : "!";
+
+ this.value = this.value.replace(
+ new RegExp("\\"+escapeChar+"(.|$)", "g"), "\\$1")
+ this.value = this.value.replace(
+ new RegExp("\\"+singleChar, "g"), ".");
+ this.value = this.value.replace(
+ new RegExp("\\"+wildCard, "g"), ".*");
+ this.value = this.value.replace(
+ new RegExp("\\\\.\\*", "g"), "\\"+wildCard);
+ this.value = this.value.replace(
+ new RegExp("\\\\\\.", "g"), "\\"+singleChar);
+
+ return this.value;
+ },
+
+ /**
+ * Method: regex2value
+ * Convert the value of this rule from a regular expression string into an
+ * ogc literal string using a wildCard of *, a singleChar of ., and an
+ * escape of !. Leaves the <value> property unmodified.
+ *
+ * Returns:
+ * {String} A string value.
+ */
+ regex2value: function() {
+
+ var value = this.value;
+
+ // replace ! with !!
+ value = value.replace(/!/g, "!!");
+
+ // replace \. with !. (watching out for \\.)
+ value = value.replace(/(\\)?\\\./g, function($0, $1) {
+ return $1 ? $0 : "!.";
+ });
+
+ // replace \* with #* (watching out for \\*)
+ value = value.replace(/(\\)?\\\*/g, function($0, $1) {
+ return $1 ? $0 : "!*";
+ });
+
+ // replace \\ with \
+ value = value.replace(/\\\\/g, "\\");
+
+ // convert .* to * (the sequence #.* is not allowed)
+ value = value.replace(/\.\*/g, "*");
+
+ return value;
+ },
+
+ /**
+ * Function: binaryCompare
+ * Compares a feature property to a rule value
+ *
+ * Parameters:
+ * context - {Object}
+ * property - {String} or {Number}
+ * value - {String} or {Number}, same as property
+ *
+ * Returns:
+ * {Boolean}
+ */
+ binaryCompare: function(context, property, value) {
+ switch (this.type) {
+ case OpenLayers.Filter.Comparison.EQUAL_TO:
+ return context[property] == value;
+ case OpenLayers.Filter.Comparison.NOT_EQUAL_TO:
+ return context[property] != value;
+ case OpenLayers.Filter.Comparison.LESS_THAN:
+ return context[property] < value;
+ case OpenLayers.Filter.Comparison.GREATER_THAN:
+ return context[property] > value;
+ case OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO:
+ return context[property] <= value;
+ case OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO:
+ return context[property] >= value;
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Filter.Comparison"
+});
+
+
+OpenLayers.Filter.Comparison.EQUAL_TO = "==";
+OpenLayers.Filter.Comparison.NOT_EQUAL_TO = "!=";
+OpenLayers.Filter.Comparison.LESS_THAN = "<";
+OpenLayers.Filter.Comparison.GREATER_THAN = ">";
+OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO = "<=";
+OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO = ">=";
+OpenLayers.Filter.Comparison.BETWEEN = "..";
+OpenLayers.Filter.Comparison.LIKE = "~";
+/* ======================================================================
+ OpenLayers/Filter/FeatureId.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/Filter.js
+ */
+
+/**
+ * Class: OpenLayers.Filter.FeatureId
+ * This class represents a ogc:FeatureId Filter, as being used for rule-based SLD
+ * styling
+ *
+ * Inherits from
+ * - <OpenLayers.Filter>
+ */
+OpenLayers.Filter.FeatureId = OpenLayers.Class(OpenLayers.Filter, {
+
+ /**
+ * APIProperty: fids
+ * {Array(String)} Feature Ids to evaluate this rule against. To be passed
+ * To be passed inside the params object.
+ */
+ fids: null,
+
+ /**
+ * Constructor: OpenLayers.Filter.FeatureId
+ * Creates an ogc:FeatureId rule.
+ *
+ * Parameters:
+ * options - {Object} An optional object with properties to set on the
+ * rule
+ *
+ * Returns:
+ * {<OpenLayers.Filter.FeatureId>}
+ */
+ initialize: function(options) {
+ this.fids = [];
+ OpenLayers.Filter.prototype.initialize.apply(this, [options]);
+ },
+
+ /**
+ * APIMethod: evaluate
+ * evaluates this rule for a specific feature
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature>} feature to apply the rule to.
+ * For vector features, the check is run against the fid,
+ * for plain features against the id.
+ *
+ * Returns:
+ * {Boolean} true if the rule applies, false if it does not
+ */
+ evaluate: function(feature) {
+ for (var i=0, len=this.fids.length; i<len; i++) {
+ var fid = feature.fid || feature.id;
+ if (fid == this.fids[i]) {
+ return true;
+ }
+ }
+ return false;
+ },
+
+ CLASS_NAME: "OpenLayers.Filter.FeatureId"
+});
+/* ======================================================================
+ OpenLayers/Filter/Logical.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/Filter.js
+ */
+
+/**
+ * Class: OpenLayers.Filter.Logical
+ * This class represents ogc:And, ogc:Or and ogc:Not rules.
+ *
+ * Inherits from
+ * - <OpenLayers.Filter>
+ */
+OpenLayers.Filter.Logical = OpenLayers.Class(OpenLayers.Filter, {
+
+ /**
+ * APIProperty: filters
+ * {Array(<OpenLayers.Filter>)} Child filters for this filter.
+ */
+ filters: null,
+
+ /**
+ * APIProperty: type
+ * {String} type of logical operator. Available types are:
+ * - OpenLayers.Filter.Locical.AND = "&&";
+ * - OpenLayers.Filter.Logical.OR = "||";
+ * - OpenLayers.Filter.Logical.NOT = "!";
+ */
+ type: null,
+
+ /**
+ * Constructor: OpenLayers.Filter.Logical
+ * Creates a logical filter (And, Or, Not).
+ *
+ * Parameters:
+ * options - {Object} An optional object with properties to set on the
+ * filter.
+ *
+ * Returns:
+ * {<OpenLayers.Filter.Logical>}
+ */
+ initialize: function(options) {
+ this.filters = [];
+ OpenLayers.Filter.prototype.initialize.apply(this, [options]);
+ },
+
+ /**
+ * APIMethod: destroy
+ * Remove reference to child filters.
+ */
+ destroy: function() {
+ this.filters = null;
+ OpenLayers.Filter.prototype.destroy.apply(this);
+ },
+
+ /**
+ * APIMethod: evaluate
+ * Evaluates this filter in a specific context. Should be implemented by
+ * subclasses.
+ *
+ * Parameters:
+ * context - {Object} Context to use in evaluating the filter.
+ *
+ * Returns:
+ * {Boolean} The filter applies.
+ */
+ evaluate: function(context) {
+ switch(this.type) {
+ case OpenLayers.Filter.Logical.AND:
+ for (var i=0, len=this.filters.length; i<len; i++) {
+ if (this.filters[i].evaluate(context) == false) {
+ return false;
+ }
+ }
+ return true;
+
+ case OpenLayers.Filter.Logical.OR:
+ for (var i=0, len=this.filters.length; i<len; i++) {
+ if (this.filters[i].evaluate(context) == true) {
+ return true;
+ }
+ }
+ return false;
+
+ case OpenLayers.Filter.Logical.NOT:
+ return (!this.filters[0].evaluate(context));
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Filter.Logical"
+});
+
+
+OpenLayers.Filter.Logical.AND = "&&";
+OpenLayers.Filter.Logical.OR = "||";
+OpenLayers.Filter.Logical.NOT = "!";
+/* ======================================================================
+ OpenLayers/Geometry/Collection.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.js
+ */
+
+/**
+ * Class: OpenLayers.Geometry.Collection
+ * A Collection is exactly what it sounds like: A collection of different
+ * Geometries. These are stored in the local parameter <components> (which
+ * can be passed as a parameter to the constructor).
+ *
+ * As new geometries are added to the collection, they are NOT cloned.
+ * When removing geometries, they need to be specified by reference (ie you
+ * have to pass in the *exact* geometry to be removed).
+ *
+ * The <getArea> and <getLength> functions here merely iterate through
+ * the components, summing their respective areas and lengths.
+ *
+ * Create a new instance with the <OpenLayers.Geometry.Collection> constructor.
+ *
+ * Inerhits from:
+ * - <OpenLayers.Geometry>
+ */
+OpenLayers.Geometry.Collection = OpenLayers.Class(OpenLayers.Geometry, {
+
+ /**
+ * APIProperty: components
+ * {Array(<OpenLayers.Geometry>)} The component parts of this geometry
+ */
+ components: null,
+
+ /**
+ * 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: null,
+
+ /**
+ * Constructor: OpenLayers.Geometry.Collection
+ * Creates a Geometry Collection -- a list of geoms.
+ *
+ * Parameters:
+ * components - {Array(<OpenLayers.Geometry>)} Optional array of geometries
+ *
+ */
+ initialize: function (components) {
+ OpenLayers.Geometry.prototype.initialize.apply(this, arguments);
+ this.components = [];
+ if (components != null) {
+ this.addComponents(components);
+ }
+ },
+
+ /**
+ * APIMethod: destroy
+ * Destroy this geometry.
+ */
+ destroy: function () {
+ this.components.length = 0;
+ this.components = null;
+ },
+
+ /**
+ * APIMethod: clone
+ * Clone this geometry.
+ *
+ * Returns:
+ * {<OpenLayers.Geometry.Collection>} An exact clone of this collection
+ */
+ clone: function() {
+ var geometry = eval("new " + this.CLASS_NAME + "()");
+ for(var i=0, len=this.components.length; i<len; i++) {
+ geometry.addComponent(this.components[i].clone());
+ }
+
+ // catch any randomly tagged-on properties
+ OpenLayers.Util.applyDefaults(geometry, this);
+
+ return geometry;
+ },
+
+ /**
+ * Method: getComponentsString
+ * Get a string representing the components for this collection
+ *
+ * Returns:
+ * {String} A string representation of the components of this geometry
+ */
+ getComponentsString: function(){
+ var strings = [];
+ for(var i=0, len=this.components.length; i<len; i++) {
+ strings.push(this.components[i].toShortString());
+ }
+ return strings.join(",");
+ },
+
+ /**
+ * APIMethod: calculateBounds
+ * Recalculate the bounds by iterating through the components and
+ * calling calling extendBounds() on each item.
+ */
+ calculateBounds: function() {
+ this.bounds = null;
+ if ( this.components && this.components.length > 0) {
+ this.setBounds(this.components[0].getBounds());
+ for (var i=1, len=this.components.length; i<len; i++) {
+ this.extendBounds(this.components[i].getBounds());
+ }
+ }
+ },
+
+ /**
+ * APIMethod: addComponents
+ * Add components to this geometry.
+ *
+ * Parameters:
+ * components - {Array(<OpenLayers.Geometry>)} An array of geometries to add
+ */
+ addComponents: function(components){
+ if(!(components instanceof Array)) {
+ components = [components];
+ }
+ for(var i=0, len=components.length; i<len; i++) {
+ this.addComponent(components[i]);
+ }
+ },
+
+ /**
+ * Method: addComponent
+ * Add a new component (geometry) to the collection. If this.componentTypes
+ * is set, then the component class name must be in the componentTypes array.
+ *
+ * The bounds cache is reset.
+ *
+ * Parameters:
+ * component - {<OpenLayers.Geometry>} A geometry to add
+ * index - {int} Optional index into the array to insert the component
+ *
+ * Returns:
+ * {Boolean} The component geometry was successfully added
+ */
+ addComponent: function(component, index) {
+ var added = false;
+ if(component) {
+ if(this.componentTypes == null ||
+ (OpenLayers.Util.indexOf(this.componentTypes,
+ component.CLASS_NAME) > -1)) {
+
+ if(index != null && (index < this.components.length)) {
+ var components1 = this.components.slice(0, index);
+ var components2 = this.components.slice(index,
+ this.components.length);
+ components1.push(component);
+ this.components = components1.concat(components2);
+ } else {
+ this.components.push(component);
+ }
+ component.parent = this;
+ this.clearBounds();
+ added = true;
+ }
+ }
+ return added;
+ },
+
+ /**
+ * APIMethod: removeComponents
+ * Remove components from this geometry.
+ *
+ * Parameters:
+ * components - {Array(<OpenLayers.Geometry>)} The components to be removed
+ */
+ removeComponents: function(components) {
+ if(!(components instanceof Array)) {
+ components = [components];
+ }
+ for(var i=components.length-1; i>=0; --i) {
+ this.removeComponent(components[i]);
+ }
+ },
+
+ /**
+ * Method: removeComponent
+ * Remove a component from this geometry.
+ *
+ * Parameters:
+ * component - {<OpenLayers.Geometry>}
+ */
+ removeComponent: function(component) {
+
+ OpenLayers.Util.removeItem(this.components, component);
+
+ // clearBounds() so that it gets recalculated on the next call
+ // to this.getBounds();
+ this.clearBounds();
+ },
+
+ /**
+ * APIMethod: getLength
+ * Calculate the length of this geometry
+ *
+ * Returns:
+ * {Float} The length of the geometry
+ */
+ getLength: function() {
+ var length = 0.0;
+ for (var i=0, len=this.components.length; i<len; i++) {
+ length += this.components[i].getLength();
+ }
+ return length;
+ },
+
+ /**
+ * APIMethod: getArea
+ * Calculate the area of this geometry. Note how this function is overridden
+ * in <OpenLayers.Geometry.Polygon>.
+ *
+ * Returns:
+ * {Float} The area of the collection by summing its parts
+ */
+ getArea: function() {
+ var area = 0.0;
+ for (var i=0, len=this.components.length; i<len; i++) {
+ area += this.components[i].getArea();
+ }
+ return area;
+ },
+
+ /**
+ * 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; 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; ++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; i<this.components.length; ++i) {
+ this.components[i].resize(scale, origin, ratio);
+ }
+ },
+
+ /**
+ * APIMethod: equals
+ * Tests for equivalent geometries
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry>}
+ *
+ * Returns:
+ * {Boolean} The coordinates are equivalent
+ */
+ equals: function(geometry) {
+ var equivalent = true;
+ if(!geometry || !geometry.CLASS_NAME ||
+ (this.CLASS_NAME != geometry.CLASS_NAME)) {
+ equivalent = false;
+ } else if(!(geometry.components instanceof Array) ||
+ (geometry.components.length != this.components.length)) {
+ equivalent = false;
+ } else {
+ for(var i=0, len=this.components.length; i<len; ++i) {
+ if(!this.components[i].equals(geometry.components[i])) {
+ equivalent = false;
+ break;
+ }
+ }
+ }
+ return equivalent;
+ },
+
+ /**
+ * 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; i++) {
+ var component = this.components[i];
+ component.transform(source, dest);
+ }
+ this.bounds = null;
+ }
+ return this;
+ },
+
+ /**
+ * 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;
+ for(var i=0, len=this.components.length; i<len; ++ i) {
+ intersect = geometry.intersects(this.components[i]);
+ if(intersect) {
+ break;
+ }
+ }
+ return intersect;
+ },
+
+ CLASS_NAME: "OpenLayers.Geometry.Collection"
+});
+/* ======================================================================
+ OpenLayers/Geometry/Point.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.js
+ */
+
+/**
+ * Class: OpenLayers.Geometry.Point
+ * Point geometry class.
+ *
+ * Inherits from:
+ * - <OpenLayers.Geometry>
+ */
+OpenLayers.Geometry.Point = OpenLayers.Class(OpenLayers.Geometry, {
+
+ /**
+ * APIProperty: x
+ * {float}
+ */
+ x: null,
+
+ /**
+ * APIProperty: y
+ * {float}
+ */
+ y: null,
+
+ /**
+ * Constructor: OpenLayers.Geometry.Point
+ * Construct a point geometry.
+ *
+ * Parameters:
+ * x - {float}
+ * y - {float}
+ *
+ */
+ initialize: function(x, y) {
+ OpenLayers.Geometry.prototype.initialize.apply(this, arguments);
+
+ this.x = parseFloat(x);
+ this.y = parseFloat(y);
+ },
+
+ /**
+ * APIMethod: clone
+ *
+ * Returns:
+ * {<OpenLayers.Geometry.Point>} An exact clone of this OpenLayers.Geometry.Point
+ */
+ clone: function(obj) {
+ if (obj == null) {
+ obj = new OpenLayers.Geometry.Point(this.x, this.y);
+ }
+
+ // catch any randomly tagged-on properties
+ OpenLayers.Util.applyDefaults(obj, this);
+
+ return obj;
+ },
+
+ /**
+ * Method: calculateBounds
+ * Create a new Bounds based on the lon/lat
+ */
+ calculateBounds: function () {
+ this.bounds = new OpenLayers.Bounds(this.x, this.y,
+ this.x, this.y);
+ },
+
+ /**
+ * APIMethod: distanceTo
+ *
+ * Parameters:
+ * point - {<OpenLayers.Geometry.Point>}
+ */
+ distanceTo: function(point) {
+ var distance = 0.0;
+ if ( (this.x != null) && (this.y != null) &&
+ (point != null) && (point.x != null) && (point.y != null) ) {
+
+ var dx2 = Math.pow(this.x - point.x, 2);
+ var dy2 = Math.pow(this.y - point.y, 2);
+ distance = Math.sqrt( dx2 + dy2 );
+ }
+ return distance;
+ },
+
+ /**
+ * APIMethod: equals
+ *
+ * Parameters:
+ * xy - {<OpenLayers.Geometry>}
+ *
+ * Returns:
+ * {Boolean} Boolean value indicating whether the passed-in
+ * {<OpenLayers.Geometry>} object has the same components as this
+ * note that if ll passed in is null, returns false
+ *
+ */
+ equals:function(geom) {
+ var equals = false;
+ if (geom != null) {
+ equals = ((this.x == geom.x && this.y == geom.y) ||
+ (isNaN(this.x) && isNaN(this.y) && isNaN(geom.x) && isNaN(geom.y)));
+ }
+ return equals;
+ },
+
+ /**
+ * Method: toShortString
+ *
+ * Returns:
+ * {String} Shortened String representation of Point object.
+ * (ex. <i>"5, 42"</i>)
+ */
+ toShortString: function() {
+ return (this.x + ", " + this.y);
+ },
+
+ /**
+ * APIMethod: move
+ * Moves a point in place
+ *
+ * Parameters:
+ * x - {Float}
+ * y - {Float}
+ */
+ move: function(x, y) {
+ this.x = this.x + x;
+ this.y = this.y + y;
+ this.clearBounds();
+ },
+
+ /**
+ * APIMethod: rotate
+ * Rotate a point around another.
+ *
+ * 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) {
+ angle *= Math.PI / 180;
+ var radius = this.distanceTo(origin);
+ var theta = angle + Math.atan2(this.y - origin.y, this.x - origin.x);
+ this.x = origin.x + (radius * Math.cos(theta));
+ this.y = origin.y + (radius * Math.sin(theta));
+ this.clearBounds();
+ },
+
+ /**
+ * APIMethod: resize
+ * Resize a point relative to some origin. For points, this has the effect
+ * of scaling a vector (from the origin to the point). This method is
+ * more useful on geometry collection subclasses.
+ *
+ * Parameters:
+ * scale - {Float} Ratio of the new distance from the origin to the old
+ * distance from the origin. A scale of 2 doubles the
+ * distance between the point and origin.
+ * 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) {
+ ratio = (ratio == undefined) ? 1 : ratio;
+ this.x = origin.x + (scale * ratio * (this.x - origin.x));
+ this.y = origin.y + (scale * (this.y - origin.y));
+ this.clearBounds();
+ },
+
+ /**
+ * 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.equals(geometry);
+ } else {
+ intersect = geometry.intersects(this);
+ }
+ return intersect;
+ },
+
+ /**
+ * APIMethod: transform
+ * Translate the x,y properties of the point from source to dest.
+ *
+ * Parameters:
+ * source - {<OpenLayers.Projection>}
+ * dest - {<OpenLayers.Projection>}
+ *
+ * Returns:
+ * {<OpenLayers.Geometry>}
+ */
+ transform: function(source, dest) {
+ if ((source && dest)) {
+ OpenLayers.Projection.transform(
+ this, source, dest);
+ this.bounds = null;
+ }
+ return this;
+ },
+
+ CLASS_NAME: "OpenLayers.Geometry.Point"
+});
+/* ======================================================================
+ OpenLayers/Geometry/Rectangle.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.js
+ */
+
+/**
+ * Class: OpenLayers.Geometry.Rectangle
+ * This class is *not supported*, and probably isn't what you're looking for.
+ * Instead, most users probably want something like:
+ * (code)
+ * var poly = new OpenLayers.Bounds(0,0,10,10).toGeometry();
+ * (end)
+ * This will create a rectangular Polygon geometry.
+ *
+ * Inherits:
+ * - <OpenLayers.Geometry>
+ */
+
+OpenLayers.Geometry.Rectangle = OpenLayers.Class(OpenLayers.Geometry, {
+
+ /**
+ * Property: x
+ * {Float}
+ */
+ x: null,
+
+ /**
+ * Property: y
+ * {Float}
+ */
+ y: null,
+
+ /**
+ * Property: width
+ * {Float}
+ */
+ width: null,
+
+ /**
+ * Property: height
+ * {Float}
+ */
+ height: null,
+
+ /**
+ * Constructor: OpenLayers.Geometry.Rectangle
+ *
+ * Parameters:
+ * points - {Array(<OpenLayers.Geometry.Point>}
+ */
+ initialize: function(x, y, width, height) {
+ OpenLayers.Geometry.prototype.initialize.apply(this, arguments);
+
+ this.x = x;
+ this.y = y;
+
+ this.width = width;
+ this.height = height;
+ },
+
+ /**
+ * Method: calculateBounds
+ * Recalculate the bounds for the geometry.
+ */
+ calculateBounds: function() {
+ this.bounds = new OpenLayers.Bounds(this.x, this.y,
+ this.x + this.width,
+ this.y + this.height);
+ },
+
+
+ /**
+ * APIMethod: getLength
+ *
+ * Returns:
+ * {Float} The length of the geometry
+ */
+ getLength: function() {
+ var length = (2 * this.width) + (2 * this.height);
+ return length;
+ },
+
+ /**
+ * APIMethod: getArea
+ *
+ * Returns:
+ * {Float} The area of the geometry
+ */
+ getArea: function() {
+ var area = this.width * this.height;
+ return area;
+ },
+
+ CLASS_NAME: "OpenLayers.Geometry.Rectangle"
+});
+/* ======================================================================
+ OpenLayers/Geometry/Surface.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.js
+ */
+
+OpenLayers.Geometry.Surface = OpenLayers.Class(OpenLayers.Geometry, {
+
+ initialize: function() {
+ OpenLayers.Geometry.prototype.initialize.apply(this, arguments);
+ },
+
+ CLASS_NAME: "OpenLayers.Geometry.Surface"
+});
+/* ======================================================================
+ OpenLayers/Layer/MapServer/Untiled.js
+ ====================================================================== */
+
+/* Copyright 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/Layer/MapServer.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.MapServer.Untiled
+ * *Deprecated*. To be removed in 3.0. Instead use OpenLayers.Layer.MapServer
+ * and pass the option 'singleTile' as true.
+ *
+ * Inherits from:
+ * - <OpenLayers.Layer.MapServer>
+ */
+OpenLayers.Layer.MapServer.Untiled = OpenLayers.Class(OpenLayers.Layer.MapServer, {
+
+ /**
+ * APIProperty: singleTile
+ * {singleTile} Always true for untiled.
+ */
+ singleTile: true,
+
+ /**
+ * Constructor: OpenLayers.Layer.MapServer.Untiled
+ *
+ * Parameters:
+ * name - {String}
+ * url - {String}
+ * params - {Object}
+ * options - {Object}
+ */
+ initialize: function(name, url, params, options) {
+ OpenLayers.Layer.MapServer.prototype.initialize.apply(this, arguments);
+
+ var msg = "The OpenLayers.Layer.MapServer.Untiled class is deprecated and " +
+ "will be removed in 3.0. Instead, you should use the " +
+ "normal OpenLayers.Layer.MapServer class, passing it the option " +
+ "'singleTile' as true.";
+ OpenLayers.Console.warn(msg);
+ },
+
+ /**
+ * Method: clone
+ * Create a clone of this layer
+ *
+ * Returns:
+ * {<OpenLayers.Layer.MapServer.Untiled>} An exact clone of this layer
+ */
+ clone: function (obj) {
+
+ if (obj == null) {
+ obj = new OpenLayers.Layer.MapServer.Untiled(this.name,
+ this.url,
+ this.params,
+ this.options);
+ }
+
+ //get all additions from superclasses
+ obj = OpenLayers.Layer.MapServer.prototype.clone.apply(this, [obj]);
+
+ // copy/set any non-init, non-simple values here
+
+ return obj;
+ },
+
+ CLASS_NAME: "OpenLayers.Layer.MapServer.Untiled"
+});
+/* ======================================================================
+ OpenLayers/Layer/Vector.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/Layer.js
+ * @requires OpenLayers/Renderer.js
+ * @requires OpenLayers/StyleMap.js
+ * @requires OpenLayers/Feature/Vector.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.Vector
+ * Instances of OpenLayers.Layer.Vector are used to render vector data from
+ * a variety of sources. Create a new vector layer with the
+ * <OpenLayers.Layer.Vector> constructor.
+ *
+ * Inherits from:
+ * - <OpenLayers.Layer>
+ */
+OpenLayers.Layer.Vector = OpenLayers.Class(OpenLayers.Layer, {
+
+ /**
+ * Constant: EVENT_TYPES
+ * {Array(String)} Supported application event types. Register a listener
+ * for a particular event with the following syntax:
+ * (code)
+ * layer.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.
+ *
+ * All event objects have at least the following properties:
+ * - *object* {Object} A reference to layer.events.object.
+ * - *element* {DOMElement} A reference to layer.events.element.
+ *
+ * Supported map event types (in addition to those from <OpenLayers.Layer>):
+ * - *beforefeatureadded* Triggered before a feature is added. Listeners
+ * will receive an object with a *feature* property referencing the
+ * feature to be added.
+ * - *featureadded* Triggered after a feature is added. The event
+ * object passed to listeners will have a *feature* property with a
+ * reference to the added feature.
+ * - *featuresadded* Triggered after features are added. The event
+ * object passed to listeners will have a *features* property with a
+ * reference to an array of added features.
+ * - *beforefeatureremoved* Triggered before a feature is removed. Listeners
+ * will receive an object with a *feature* property referencing the
+ * feature to be removed.
+ * - *featureremoved* Triggerd after a feature is removed. The event
+ * object passed to listeners will have a *feature* property with a
+ * reference to the removed feature.
+ * - *featuresremoved* Triggered after features are removed. The event
+ * object passed to listeners will have a *features* property with a
+ * reference to an array of removed features.
+ * - *featureselected* Triggered after a feature is selected. Listeners
+ * will receive an object with a *feature* property referencing the
+ * selected feature.
+ * - *featureunselected* Triggered after a feature is unselected.
+ * Listeners will receive an object with a *feature* property
+ * referencing the unselected feature.
+ * - *beforefeaturemodified* Triggered when a feature is selected to
+ * be modified. Listeners will receive an object with a *feature*
+ * property referencing the selected feature.
+ * - *featuremodified* Triggered when a feature has been modified.
+ * Listeners will receive an object with a *feature* property referencing
+ * the modified feature.
+ * - *afterfeaturemodified* Triggered when a feature is finished being modified.
+ * Listeners will receive an object with a *feature* property referencing
+ * the modified feature.
+ */
+ EVENT_TYPES: ["beforefeatureadded", "featureadded", "featuresadded",
+ "beforefeatureremoved", "featureremoved", "featuresremoved",
+ "beforefeatureselected", "featureselected", "featureunselected",
+ "beforefeaturemodified", "featuremodified", "afterfeaturemodified"],
+
+ /**
+ * APIProperty: isBaseLayer
+ * {Boolean} The layer is a base layer. Default is true. Set this property
+ * in the layer options
+ */
+ isBaseLayer: false,
+
+ /**
+ * APIProperty: isFixed
+ * {Boolean} Whether the layer remains in one place while dragging the
+ * map.
+ */
+ isFixed: false,
+
+ /**
+ * APIProperty: isVector
+ * {Boolean} Whether the layer is a vector layer.
+ */
+ isVector: true,
+
+ /**
+ * APIProperty: features
+ * {Array(<OpenLayers.Feature.Vector>)}
+ */
+ features: null,
+
+ /**
+ * Property: selectedFeatures
+ * {Array(<OpenLayers.Feature.Vector>)}
+ */
+ selectedFeatures: null,
+
+ /**
+ * Property: unrenderedFeatures
+ * {Object} hash of features, keyed by feature.id, that the renderer
+ * failed to draw
+ */
+ unrenderedFeatures: null,
+
+ /**
+ * APIProperty: reportError
+ * {Boolean} report friendly error message when loading of renderer
+ * fails.
+ */
+ reportError: true,
+
+ /**
+ * APIProperty: style
+ * {Object} Default style for the layer
+ */
+ style: null,
+
+ /**
+ * Property: styleMap
+ * {<OpenLayers.StyleMap>}
+ */
+ styleMap: null,
+
+ /**
+ * Property: strategies
+ * {Array(<OpenLayers.Strategy>})} Optional list of strategies for the layer.
+ */
+ strategies: null,
+
+ /**
+ * Property: protocol
+ * {<OpenLayers.Protocol>} Optional protocol for the layer.
+ */
+ protocol: null,
+
+ /**
+ * Property: renderers
+ * {Array(String)} List of supported Renderer classes. Add to this list to
+ * add support for additional renderers. This list is ordered:
+ * the first renderer which returns true for the 'supported()'
+ * method will be used, if not defined in the 'renderer' option.
+ */
+ renderers: ['SVG', 'VML', 'Canvas'],
+
+ /**
+ * Property: renderer
+ * {<OpenLayers.Renderer>}
+ */
+ renderer: null,
+
+ /**
+ * APIProperty: rendererOptions
+ * {Object} Options for the renderer. See {<OpenLayers.Renderer>} for
+ * supported options.
+ */
+ rendererOptions: null,
+
+ /**
+ * APIProperty: geometryType
+ * {String} geometryType allows you to limit the types of geometries this
+ * layer supports. This should be set to something like
+ * "OpenLayers.Geometry.Point" to limit types.
+ */
+ geometryType: null,
+
+ /**
+ * Property: drawn
+ * {Boolean} Whether the Vector Layer features have been drawn yet.
+ */
+ drawn: false,
+
+ /**
+ * Constructor: OpenLayers.Layer.Vector
+ * Create a new vector layer
+ *
+ * Parameters:
+ * name - {String} A name for the layer
+ * options - {Object} Optional object with non-default properties to set on
+ * the layer.
+ *
+ * Returns:
+ * {<OpenLayers.Layer.Vector>} A new vector layer
+ */
+ initialize: function(name, options) {
+
+ // concatenate events specific to vector with those from the base
+ this.EVENT_TYPES =
+ OpenLayers.Layer.Vector.prototype.EVENT_TYPES.concat(
+ OpenLayers.Layer.prototype.EVENT_TYPES
+ );
+
+ OpenLayers.Layer.prototype.initialize.apply(this, arguments);
+
+ // allow user-set renderer, otherwise assign one
+ if (!this.renderer || !this.renderer.supported()) {
+ this.assignRenderer();
+ }
+
+ // if no valid renderer found, display error
+ if (!this.renderer || !this.renderer.supported()) {
+ this.renderer = null;
+ this.displayError();
+ }
+
+ if (!this.styleMap) {
+ this.styleMap = new OpenLayers.StyleMap();
+ }
+
+ this.features = [];
+ this.selectedFeatures = [];
+ this.unrenderedFeatures = {};
+
+ // Allow for custom layer behavior
+ if(this.strategies){
+ for(var i=0, len=this.strategies.length; i<len; i++) {
+ this.strategies[i].setLayer(this);
+ }
+ }
+
+ },
+
+ /**
+ * APIMethod: destroy
+ * Destroy this layer
+ */
+ destroy: function() {
+ if (this.strategies) {
+ var strategy, i, len;
+ for(i=0, len=this.strategies.length; i<len; i++) {
+ strategy = this.strategies[i];
+ if(strategy.autoDestroy) {
+ strategy.destroy();
+ }
+ }
+ this.strategies = null;
+ }
+ if (this.protocol) {
+ if(this.protocol.autoDestroy) {
+ this.protocol.destroy();
+ }
+ this.protocol = null;
+ }
+ this.destroyFeatures();
+ this.features = null;
+ this.selectedFeatures = null;
+ this.unrenderedFeatures = null;
+ if (this.renderer) {
+ this.renderer.destroy();
+ }
+ this.renderer = null;
+ this.geometryType = null;
+ this.drawn = null;
+ OpenLayers.Layer.prototype.destroy.apply(this, arguments);
+ },
+
+ /**
+ * Method: assignRenderer
+ * Iterates through the available renderer implementations and selects
+ * and assigns the first one whose "supported()" function returns true.
+ */
+ assignRenderer: function() {
+ for (var i=0, len=this.renderers.length; i<this.renderers.length; i++) {
+ var rendererClass = OpenLayers.Renderer[this.renderers[i]];
+ if (rendererClass && rendererClass.prototype.supported()) {
+ this.renderer = new rendererClass(this.div,
+ this.rendererOptions);
+ break;
+ }
+ }
+ },
+
+ /**
+ * Method: displayError
+ * Let the user know their browser isn't supported.
+ */
+ displayError: function() {
+ if (this.reportError) {
+ OpenLayers.Console.userError(OpenLayers.i18n("browserNotSupported",
+ {'renderers':this.renderers.join("\n")}));
+ }
+ },
+
+ /**
+ * Method: setMap
+ * The layer has been added to the map.
+ *
+ * If there is no renderer set, the layer can't be used. Remove it.
+ * Otherwise, give the renderer a reference to the map and set its size.
+ *
+ * Parameters:
+ * map - {<OpenLayers.Map>}
+ */
+ setMap: function(map) {
+ OpenLayers.Layer.prototype.setMap.apply(this, arguments);
+
+ if (!this.renderer) {
+ this.map.removeLayer(this);
+ } else {
+ this.renderer.map = this.map;
+ this.renderer.setSize(this.map.getSize());
+ }
+ if(this.strategies) {
+ var strategy, i, len;
+ for(i=0, len=this.strategies.length; i<len; i++) {
+ strategy = this.strategies[i];
+ if(strategy.autoActivate) {
+ strategy.activate();
+ }
+ }
+ }
+ },
+
+ /**
+ * Method: removeMap
+ * The layer has been removed from the map.
+ *
+ * Parameters:
+ * map - {<OpenLayers.Map>}
+ */
+ removeMap: function(map) {
+ if(this.strategies) {
+ var strategy, i, len;
+ for(i=0, len=this.strategies.length; i<len; i++) {
+ strategy = this.strategies[i];
+ if(strategy.autoActivate) {
+ strategy.deactivate();
+ }
+ }
+ }
+ },
+
+ /**
+ * Method: onMapResize
+ * Notify the renderer of the change in size.
+ *
+ */
+ onMapResize: function() {
+ OpenLayers.Layer.prototype.onMapResize.apply(this, arguments);
+ this.renderer.setSize(this.map.getSize());
+ },
+
+ /**
+ * Method: moveTo
+ * Reset the vector layer's div so that it once again is lined up with
+ * the map. Notify the renderer of the change of extent, and in the
+ * case of a change of zoom level (resolution), have the
+ * renderer redraw features.
+ *
+ * If the layer has not yet been drawn, cycle through the layer's
+ * features and draw each one.
+ *
+ * Parameters:
+ * bounds - {<OpenLayers.Bounds>}
+ * zoomChanged - {Boolean}
+ * dragging - {Boolean}
+ */
+ moveTo: function(bounds, zoomChanged, dragging) {
+ OpenLayers.Layer.prototype.moveTo.apply(this, arguments);
+
+ var coordSysUnchanged = true;
+
+ if (!dragging) {
+ this.renderer.root.style.visibility = "hidden";
+
+ this.div.style.left = -parseInt(this.map.layerContainerDiv.style.left) + "px";
+ this.div.style.top = -parseInt(this.map.layerContainerDiv.style.top) + "px";
+ var extent = this.map.getExtent();
+ coordSysUnchanged = this.renderer.setExtent(extent, zoomChanged);
+
+ this.renderer.root.style.visibility = "visible";
+
+ // Force a reflow on gecko based browsers to prevent jump/flicker.
+ // This seems to happen on only certain configurations; it was originally
+ // noticed in FF 2.0 and Linux.
+ if (navigator.userAgent.toLowerCase().indexOf("gecko") != -1) {
+ this.div.scrollLeft = this.div.scrollLeft;
+ }
+
+ if(!zoomChanged && coordSysUnchanged) {
+ var unrenderedFeatures = {};
+ for(var i in this.unrenderedFeatures) {
+ var feature = this.unrenderedFeatures[i];
+ if(!this.drawFeature(feature)) {
+ unrenderedFeatures[i] = feature;
+ }
+ }
+ this.unrenderedFeatures = unrenderedFeatures;
+ }
+ }
+
+ if (!this.drawn || zoomChanged || !coordSysUnchanged) {
+ this.unrenderedFeatures = {};
+ 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;
+ }
+ feature = this.features[i];
+ if (!this.drawFeature(feature)) {
+ this.unrenderedFeatures[feature.id] = feature;
+ };
+ }
+ }
+ },
+
+ /**
+ * APIMethod: addFeatures
+ * Add Features to the layer.
+ *
+ * Parameters:
+ * features - {Array(<OpenLayers.Feature.Vector>)}
+ * options - {Object}
+ */
+ addFeatures: function(features, options) {
+ if (!(features instanceof Array)) {
+ features = [features];
+ }
+
+ var notify = !options || !options.silent;
+
+ for (var i=0, len=features.length; i<len; i++) {
+ if (i != (features.length - 1)) {
+ this.renderer.locked = true;
+ } else {
+ this.renderer.locked = false;
+ }
+ var feature = features[i];
+
+ if (this.geometryType &&
+ !(feature.geometry instanceof this.geometryType)) {
+ var throwStr = OpenLayers.i18n('componentShouldBe',
+ {'geomType':this.geometryType.prototype.CLASS_NAME});
+ throw throwStr;
+ }
+
+ this.features.push(feature);
+
+ //give feature reference to its layer
+ feature.layer = this;
+
+ if (!feature.style && this.style) {
+ feature.style = OpenLayers.Util.extend({}, this.style);
+ }
+
+ if (notify) {
+ this.events.triggerEvent("beforefeatureadded", {
+ feature: feature
+ });
+ this.preFeatureInsert(feature);
+ }
+
+ if (this.drawn) {
+ if(!this.drawFeature(feature)) {
+ this.unrenderedFeatures[feature.id] = feature;
+ }
+ }
+
+ if (notify) {
+ this.events.triggerEvent("featureadded", {
+ feature: feature
+ });
+ this.onFeatureInsert(feature);
+ }
+ }
+
+ if(notify) {
+ this.events.triggerEvent("featuresadded", {features: features});
+ }
+ },
+
+
+ /**
+ * APIMethod: removeFeatures
+ *
+ * Parameters:
+ * features - {Array(<OpenLayers.Feature.Vector>)}
+ * options - {Object}
+ */
+ removeFeatures: function(features, options) {
+ if (!(features instanceof Array)) {
+ features = [features];
+ }
+ if (features.length <= 0) {
+ return;
+ }
+
+ var notify = !options || !options.silent;
+
+ for (var i = features.length - 1; i >= 0; i--) {
+ // We remain locked so long as we're not at 0
+ // and the 'next' feature has a geometry. We do the geometry check
+ // because if all the features after the current one are 'null', we
+ // won't call eraseGeometry, so we break the 'renderer functions
+ // will always be called with locked=false *last*' rule. The end result
+ // is a possible gratiutious unlocking to save a loop through the rest
+ // of the list checking the remaining features every time. So long as
+ // null geoms are rare, this is probably okay.
+ if (i != 0 && features[i-1].geometry) {
+ this.renderer.locked = true;
+ } else {
+ this.renderer.locked = false;
+ }
+
+ var feature = features[i];
+ delete this.unrenderedFeatures[feature.id];
+
+ if (notify) {
+ this.events.triggerEvent("beforefeatureremoved", {
+ feature: feature
+ });
+ }
+
+ this.features = OpenLayers.Util.removeItem(this.features, feature);
+ // feature has no layer at this point
+ feature.layer = null;
+
+ if (feature.geometry) {
+ this.renderer.eraseGeometry(feature.geometry);
+ }
+
+ //in the case that this feature is one of the selected features,
+ // remove it from that array as well.
+ if (OpenLayers.Util.indexOf(this.selectedFeatures, feature) != -1){
+ OpenLayers.Util.removeItem(this.selectedFeatures, feature);
+ }
+
+ if (notify) {
+ this.events.triggerEvent("featureremoved", {
+ feature: feature
+ });
+ }
+ }
+
+ if (notify) {
+ this.events.triggerEvent("featuresremoved", {features: features});
+ }
+ },
+
+ /**
+ * APIMethod: destroyFeatures
+ * Erase and destroy features on the layer.
+ *
+ * Parameters:
+ * features - {Array(<OpenLayers.Feature.Vector>)} An optional array of
+ * features to destroy. If not supplied, all features on the layer
+ * will be destroyed.
+ * options - {Object}
+ */
+ destroyFeatures: function(features, options) {
+ var all = (features == undefined); // evaluates to true if
+ // features is null
+ if(all) {
+ features = this.features;
+ }
+ this.removeFeatures(features, options);
+ for (var i = 0; i < features.length; i++) {
+ features[i].destroy();
+ }
+ },
+
+ /**
+ * APIMethod: drawFeature
+ * Draw (or redraw) a feature on the layer. If the optional style argument
+ * is included, this style will be used. If no style is included, the
+ * feature's style will be used. If the feature doesn't have a style,
+ * the layer's style will be used.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>}
+ * style - {Object} Symbolizer hash or {String} renderIntent
+ *
+ * Returns:
+ * {Boolean} true if the renderer was able to draw the feature, false
+ * otherwise
+ */
+ drawFeature: function(feature, style) {
+ if (typeof style != "object") {
+ var renderIntent = typeof style == "string" ?
+ style : feature.renderIntent;
+ style = feature.style || this.style;
+ if (!style) {
+ style = this.styleMap.createSymbolizer(feature, renderIntent);
+ }
+ }
+
+ return this.renderer.drawFeature(feature, style);
+ },
+
+ /**
+ * Method: eraseFeatures
+ * Erase features from the layer.
+ *
+ * Parameters:
+ * features - {Array(<OpenLayers.Feature.Vector>)}
+ */
+ eraseFeatures: function(features) {
+ this.renderer.eraseFeatures(features);
+ },
+
+ /**
+ * Method: getFeatureFromEvent
+ * Given an event, return a feature if the event occurred over one.
+ * Otherwise, return null.
+ *
+ * Parameters:
+ * evt - {Event}
+ *
+ * Returns:
+ * {<OpenLayers.Feature.Vector>} A feature if one was under the event.
+ */
+ getFeatureFromEvent: function(evt) {
+ if (!this.renderer) {
+ OpenLayers.Console.error(OpenLayers.i18n("getFeatureError"));
+ return null;
+ }
+ var featureId = this.renderer.getFeatureIdFromEvent(evt);
+ return this.getFeatureById(featureId);
+ },
+
+ /**
+ * APIMethod: getFeatureById
+ * Given a feature id, return the feature if it exists in the features array
+ *
+ * Parameters:
+ * featureId - {String}
+ *
+ * Returns:
+ * {<OpenLayers.Feature.Vector>} A feature corresponding to the given
+ * featureId
+ */
+ getFeatureById: function(featureId) {
+ //TBD - would it be more efficient to use a hash for this.features?
+ var feature = null;
+ for(var i=0, len=this.features.length; i<len; ++i) {
+ if(this.features[i].id == featureId) {
+ feature = this.features[i];
+ break;
+ }
+ }
+ return feature;
+ },
+
+ /**
+ * Unselect the selected features
+ * i.e. clears the featureSelection array
+ * change the style back
+ clearSelection: function() {
+
+ var vectorLayer = this.map.vectorLayer;
+ for (var i = 0; i < this.map.featureSelection.length; i++) {
+ var featureSelection = this.map.featureSelection[i];
+ vectorLayer.drawFeature(featureSelection, vectorLayer.style);
+ }
+ this.map.featureSelection = [];
+ },
+ */
+
+
+ /**
+ * APIMethod: onFeatureInsert
+ * method called after a feature is inserted.
+ * Does nothing by default. Override this if you
+ * need to do something on feature updates.
+ *
+ * Paarameters:
+ * feature - {<OpenLayers.Feature.Vector>}
+ */
+ onFeatureInsert: function(feature) {
+ },
+
+ /**
+ * APIMethod: preFeatureInsert
+ * method called before a feature is inserted.
+ * Does nothing by default. Override this if you
+ * need to do something when features are first added to the
+ * layer, but before they are drawn, such as adjust the style.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>}
+ */
+ preFeatureInsert: function(feature) {
+ },
+
+ /**
+ * APIMethod: getDataExtent
+ * Calculates the max extent which includes all of the features.
+ *
+ * Returns:
+ * {<OpenLayers.Bounds>}
+ */
+ 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++){
+ maxExtent.extend(this.features[i].geometry.getBounds());
+ }
+ }
+
+ return maxExtent;
+ },
+
+ CLASS_NAME: "OpenLayers.Layer.Vector"
+});
+/* ======================================================================
+ OpenLayers/Layer/WMS/Untiled.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/Layer/WMS.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.WMS.Untiled
+ * *Deprecated*. To be removed in 3.0. Instead use OpenLayers.Layer.WMS and
+ * pass the option 'singleTile' as true.
+ *
+ * Inherits from:
+ * - <OpenLayers.Layer.WMS>
+ */
+OpenLayers.Layer.WMS.Untiled = OpenLayers.Class(OpenLayers.Layer.WMS, {
+
+ /**
+ * APIProperty: singleTile
+ * {singleTile} Always true for untiled.
+ */
+ singleTile: true,
+
+ /**
+ * Constructor: OpenLayers.Layer.WMS.Untiled
+ *
+ * Parameters:
+ * name - {String}
+ * url - {String}
+ * params - {Object}
+ * options - {Object}
+ */
+ initialize: function(name, url, params, options) {
+ OpenLayers.Layer.WMS.prototype.initialize.apply(this, arguments);
+
+ var msg = "The OpenLayers.Layer.WMS.Untiled class is deprecated and " +
+ "will be removed in 3.0. Instead, you should use the " +
+ "normal OpenLayers.Layer.WMS class, passing it the option " +
+ "'singleTile' as true.";
+ OpenLayers.Console.warn(msg);
+ },
+
+ /**
+ * Method: clone
+ * Create a clone of this layer
+ *
+ * Returns:
+ * {<OpenLayers.Layer.WMS.Untiled>} An exact clone of this layer
+ */
+ clone: function (obj) {
+
+ if (obj == null) {
+ obj = new OpenLayers.Layer.WMS.Untiled(this.name,
+ this.url,
+ this.params,
+ this.options);
+ }
+
+ //get all additions from superclasses
+ obj = OpenLayers.Layer.WMS.prototype.clone.apply(this, [obj]);
+
+ // copy/set any non-init, non-simple values here
+
+ return obj;
+ },
+
+ CLASS_NAME: "OpenLayers.Layer.WMS.Untiled"
+});
+/* ======================================================================
+ OpenLayers/Format/Filter.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/Format/XML.js
+ * @requires OpenLayers/Filter/FeatureId.js
+ * @requires OpenLayers/Filter/Logical.js
+ * @requires OpenLayers/Filter/Comparison.js
+ */
+
+/**
+ * Class: OpenLayers.Format.Filter
+ * Read/Wite ogc:Filter. Create a new instance with the <OpenLayers.Format.Filter>
+ * constructor.
+ *
+ * Inherits from:
+ * - <OpenLayers.Format.XML>
+ */
+OpenLayers.Format.Filter = OpenLayers.Class(OpenLayers.Format.XML, {
+
+ /**
+ * APIProperty: defaultVersion
+ * {String} Version number to assume if none found. Default is "1.0.0".
+ */
+ defaultVersion: "1.0.0",
+
+ /**
+ * APIProperty: version
+ * {String} Specify a version string if one is known.
+ */
+ version: null,
+
+ /**
+ * Property: parser
+ * {Object} Instance of the versioned parser. Cached for multiple read and
+ * write calls of the same version.
+ */
+ parser: null,
+
+ /**
+ * Constructor: OpenLayers.Format.Filter
+ * Create a new parser for Filter.
+ *
+ * Parameters:
+ * options - {Object} An optional object whose properties will be set on
+ * this instance.
+ */
+ initialize: function(options) {
+ OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
+ },
+
+ /**
+ * APIMethod: write
+ * Write an ogc:Filter given a filter object.
+ *
+ * Parameters:
+ * filter - {<OpenLayers.Filter>} An filter.
+ * options - {Object} Optional configuration object.
+ *
+ * Returns:
+ * {Elment} An ogc:Filter element node.
+ */
+ write: function(filter, options) {
+ var version = (options && options.version) ||
+ this.version || this.defaultVersion;
+ if(!this.parser || this.parser.VERSION != version) {
+ var format = OpenLayers.Format.Filter[
+ "v" + version.replace(/\./g, "_")
+ ];
+ if(!format) {
+ throw "Can't find a Filter parser for version " +
+ version;
+ }
+ this.parser = new format(this.options);
+ }
+ return this.parser.write(filter);
+ //return OpenLayers.Format.XML.prototype.write.apply(this, [root]);
+ },
+
+ /**
+ * APIMethod: read
+ * Read and Filter doc and return an object representing the Filter.
+ *
+ * Parameters:
+ * data - {String | DOMElement} Data to read.
+ *
+ * Returns:
+ * {<OpenLayers.Filter>} A filter object.
+ */
+ read: function(data) {
+ if(typeof data == "string") {
+ data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
+ }
+ var root = data.nodeType == 9 ? data.documentElement : data;
+ var version = this.version;
+ if(!version) {
+ version = this.defaultVersion;
+ }
+ if(!this.parser || this.parser.VERSION != version) {
+ var format = OpenLayers.Format.Filter[
+ "v" + version.replace(/\./g, "_")
+ ];
+ if(!format) {
+ throw "Can't find a Filter parser for version " +
+ version;
+ }
+ this.parser = new format(this.options);
+ }
+ var filter = this.parser.read(data);
+ return filter;
+ },
+
+ CLASS_NAME: "OpenLayers.Format.Filter"
+});
+/* ======================================================================
+ OpenLayers/Format/SLD.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/Format/XML.js
+ * @requires OpenLayers/Style.js
+ * @requires OpenLayers/Rule.js
+ * @requires OpenLayers/Filter/FeatureId.js
+ * @requires OpenLayers/Filter/Logical.js
+ * @requires OpenLayers/Filter/Comparison.js
+ */
+
+/**
+ * Class: OpenLayers.Format.SLD
+ * Read/Wite SLD. Create a new instance with the <OpenLayers.Format.SLD>
+ * constructor.
+ *
+ * Inherits from:
+ * - <OpenLayers.Format.XML>
+ */
+OpenLayers.Format.SLD = OpenLayers.Class(OpenLayers.Format.XML, {
+
+ /**
+ * APIProperty: defaultVersion
+ * {String} Version number to assume if none found. Default is "1.0.0".
+ */
+ defaultVersion: "1.0.0",
+
+ /**
+ * APIProperty: version
+ * {String} Specify a version string if one is known.
+ */
+ version: null,
+
+ /**
+ * Property: parser
+ * {Object} Instance of the versioned parser. Cached for multiple read and
+ * write calls of the same version.
+ */
+ parser: null,
+
+ /**
+ * Constructor: OpenLayers.Format.SLD
+ * Create a new parser for SLD.
+ *
+ * Parameters:
+ * options - {Object} An optional object whose properties will be set on
+ * this instance.
+ */
+ initialize: function(options) {
+ OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
+ },
+
+ /**
+ * APIMethod: write
+ * Write a SLD document given a list of styles.
+ *
+ * Parameters:
+ * sld - {Object} An object representing the SLD.
+ * options - {Object} Optional configuration object.
+ *
+ * Returns:
+ * {String} An SLD document string.
+ */
+ write: function(sld, options) {
+ var version = (options && options.version) ||
+ this.version || this.defaultVersion;
+ if(!this.parser || this.parser.VERSION != version) {
+ var format = OpenLayers.Format.SLD[
+ "v" + version.replace(/\./g, "_")
+ ];
+ if(!format) {
+ throw "Can't find a SLD parser for version " +
+ version;
+ }
+ this.parser = new format(this.options);
+ }
+ var root = this.parser.write(sld);
+ return OpenLayers.Format.XML.prototype.write.apply(this, [root]);
+ },
+
+ /**
+ * APIMethod: read
+ * Read and SLD doc and return an object representing the SLD.
+ *
+ * Parameters:
+ * data - {String | DOMElement} Data to read.
+ *
+ * Returns:
+ * {Object} An object representing the SLD.
+ */
+ read: function(data) {
+ if(typeof data == "string") {
+ data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
+ }
+ var root = data.documentElement;
+ var version = this.version;
+ if(!version) {
+ version = root.getAttribute("version");
+ if(!version) {
+ version = this.defaultVersion;
+ }
+ }
+ if(!this.parser || this.parser.VERSION != version) {
+ var format = OpenLayers.Format.SLD[
+ "v" + version.replace(/\./g, "_")
+ ];
+ if(!format) {
+ throw "Can't find a SLD parser for version " +
+ version;
+ }
+ this.parser = new format(this.options);
+ }
+ var sld = this.parser.read(data);
+ return sld;
+ },
+
+ CLASS_NAME: "OpenLayers.Format.SLD"
+});
+/* ======================================================================
+ OpenLayers/Format/Text.js
+ ====================================================================== */
+
+/* Copyright (c) 2006-2008 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/Feature/Vector.js
+ * @requires OpenLayers/Geometry/Point.js
+ */
+
+/**
+ * Class: OpenLayers.Format.Text
+ * Read Text format. Create a new instance with the <OpenLayers.Format.Text>
+ * constructor. This reads text which is formatted like CSV text, using
+ * tabs as the seperator by default. It provides parsing of data originally
+ * used in the MapViewerService, described on the wiki. This Format is used
+ * by the <OpenLayers.Layer.Text> class.
+ *
+ * Inherits from:
+ * - <OpenLayers.Format>
+ */
+OpenLayers.Format.Text = OpenLayers.Class(OpenLayers.Format, {
+
+ /**
+ * Constructor: OpenLayers.Format.Text
+ * Create a new parser for TSV Text.
+ *
+ * Parameters:
+ * options - {Object} An optional object whose properties will be set on
+ * this instance.
+ */
+ initialize: function(options) {
+ OpenLayers.Format.prototype.initialize.apply(this, [options]);
+ },
+
+ /**
+ * APIMethod: read
+ * Return a list of features from a Tab Seperated Values text string.
+ *
+ * Parameters:
+ * data - {String}
+ *
+ * Returns:
+ * An Array of <OpenLayers.Feature.Vector>s
+ */
+ read: function(text) {
+ var lines = text.split('\n');
+ var columns;
+ var features = [];
+ // length - 1 to allow for trailing new line
+ for (var lcv = 0; lcv < (lines.length - 1); lcv++) {
+ var currLine = lines[lcv].replace(/^\s*/,'').replace(/\s*$/,'');
+
+ if (currLine.charAt(0) != '#') { /* not a comment */
+
+ if (!columns) {
+ //First line is columns
+ columns = currLine.split('\t');
+ } else {
+ var vals = currLine.split('\t');
+ var geometry = new OpenLayers.Geometry.Point(0,0);
+ var attributes = {};
+ var style = {};
+ var icon, iconSize, iconOffset, overflow;
+ var set = false;
+ for (var valIndex = 0; valIndex < vals.length; valIndex++) {
+ if (vals[valIndex]) {
+ if (columns[valIndex] == 'point') {
+ var coords = vals[valIndex].split(',');
+ geometry.y = parseFloat(coords[0]);
+ geometry.x = parseFloat(coords[1]);
+ set = true;
+ } else if (columns[valIndex] == 'lat') {
+ geometry.y = parseFloat(vals[valIndex]);
+ set = true;
+ } else if (columns[valIndex] == 'lon') {
+ geometry.x = parseFloat(vals[valIndex]);
+ set = true;
+ } else if (columns[valIndex] == 'title')
+ attributes['title'] = vals[valIndex];
+ else if (columns[valIndex] == 'image' ||
+ columns[valIndex] == 'icon')
+ style['externalGraphic'] = vals[valIndex];
+ else if (columns[valIndex] == 'iconSize') {
+ var size = vals[valIndex].split(',');
+ style['graphicWidth'] = parseFloat(size[0]);
+ style['graphicHeight'] = parseFloat(size[1]);
+ } else if (columns[valIndex] == 'iconOffset') {
+ var offset = vals[valIndex].split(',');
+ style['graphicXOffset'] = parseFloat(offset[0]);
+ style['graphicYOffset'] = parseFloat(offset[1]);
+ } else if (columns[valIndex] == 'description') {
+ attributes['description'] = vals[valIndex];
+ } else if (columns[valIndex] == 'overflow') {
+ attributes['overflow'] = vals[valIndex];
+ }
+ }
+ }
+ if (set) {
+ if (this.internalProjection && this.externalProjection) {
+ geometry.transform(this.externalProjection,
+ this.internalProjection);
+ }
+ var feature = new OpenLayers.Feature.Vector(geometry, attributes, style);
+ features.push(feature);
+ }
+ }
+ }
+ }
+ return features;
+ },
+
+ CLASS_NAME: "OpenLayers.Format.Text"
+});
+/* ======================================================================
+ OpenLayers/Geometry/MultiLineString.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.MultiLineString
+ * A MultiLineString is a geometry with multiple <OpenLayers.Geometry.LineString>
+ * components.
+ *
+ * Inherits from:
+ * - <OpenLayers.Geometry.Collection>
+ * - <OpenLayers.Geometry>
+ */
+OpenLayers.Geometry.MultiLineString = 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.LineString"],
+
+ /**
+ * Constructor: OpenLayers.Geometry.MultiLineString
+ * Constructor for a MultiLineString Geometry.
+ *
+ * Parameters:
+ * components - {Array(<OpenLayers.Geometry.LineString>)}
+ *
+ */
+ initialize: function(components) {
+ OpenLayers.Geometry.Collection.prototype.initialize.apply(this,
+ arguments);
+ },
+
+ CLASS_NAME: "OpenLayers.Geometry.MultiLineString"
+});
+/* ======================================================================
+ OpenLayers/Geometry/MultiPoint.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.MultiPoint
+ * MultiPoint is a collection of Points. Create a new instance with the
+ * <OpenLayers.Geometry.MultiPoint> constructor.
+ *
+ * Inherits from:
+ * - <OpenLayers.Geometry.Collection>
+ * - <OpenLayers.Geometry>
+ */
+OpenLayers.Geometry.MultiPoint = 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.Point"],
+
+ /**
+ * Constructor: OpenLayers.Geometry.MultiPoint
+ * Create a new MultiPoint Geometry
+ *
+ * Parameters:
+ * components - {Array(<OpenLayers.Geometry.Point>)}
+ *
+ * Returns:
+ * {<OpenLayers.Geometry.MultiPoint>}
+ */
+ initialize: function(components) {
+ OpenLayers.Geometry.Collection.prototype.initialize.apply(this,
+ arguments);
+ },
+
+ /**
+ * APIMethod: addPoint
+ * Wrapper for <OpenLayers.Geometry.Collection.addComponent>
+ *
+ * Parameters:
+ * point - {<OpenLayers.Geometry.Point>} Point to be added
+ * index - {Integer} Optional index
+ */
+ addPoint: function(point, index) {
+ this.addComponent(point, index);
+ },
+
+ /**
+ * APIMethod: removePoint
+ * Wrapper for <OpenLayers.Geometry.Collection.removeComponent>
+ *
+ * Parameters:
+ * point - {<OpenLayers.Geometry.Point>} Point to be removed
+ */
+ removePoint: function(point){
+ this.removeComponent(point);
+ },
+
+ CLASS_NAME: "OpenLayers.Geometry.MultiPoint"
+});
+/* ======================================================================
+ OpenLayers/Geometry/MultiPolygon.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.MultiPolygon
+ * MultiPolygon is a geometry with multiple <OpenLayers.Geometry.Polygon>
+ * components. Create a new instance with the <OpenLayers.Geometry.MultiPolygon>
+ * constructor.
+ *
+ * Inherits from:
+ * - <OpenLayers.Geometry.Collection>
+ */
+OpenLayers.Geometry.MultiPolygon = 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.Polygon"],
+
+ /**
+ * Constructor: OpenLayers.Geometry.MultiPolygon
+ * Create a new MultiPolygon geometry
+ *
+ * Parameters:
+ * components - {Array(<OpenLayers.Geometry.Polygon>)} An array of polygons
+ * used to generate the MultiPolygon
+ *
+ */
+ initialize: function(components) {
+ OpenLayers.Geometry.Collection.prototype.initialize.apply(this,
+ arguments);
+ },
+
+ CLASS_NAME: "OpenLayers.Geometry.MultiPolygon"
+});
+/* ======================================================================
+ 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
+ ====================================================================== */
+
+/* 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
+ * @requires OpenLayers/Geometry/Point.js
+ */
+
+/**
+ * Class: OpenLayers.Handler.Point
+ * Handler to draw a point on the map. Point is displayed on mouse down,
+ * moves 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.Point> constructor.
+ *
+ * Inherits from:
+ * - <OpenLayers.Handler>
+ */
+OpenLayers.Handler.Point = OpenLayers.Class(OpenLayers.Handler, {
+
+ /**
+ * Property: point
+ * {<OpenLayers.Feature.Vector>} The currently drawn point
+ */
+ point: null,
+
+ /**
+ * Property: layer
+ * {<OpenLayers.Layer.Vector>} The temporary drawing layer
+ */
+ layer: null,
+
+ /**
+ * Property: drawing
+ * {Boolean} A point is being drawn
+ */
+ drawing: false,
+
+ /**
+ * Property: mouseDown
+ * {Boolean} The mouse is down
+ */
+ mouseDown: false,
+
+ /**
+ * Property: lastDown
+ * {<OpenLayers.Pixel>} Location of the last mouse down
+ */
+ lastDown: null,
+
+ /**
+ * Property: lastUp
+ * {<OpenLayers.Pixel>}
+ */
+ lastUp: null,
+
+ /**
+ * Constructor: OpenLayers.Handler.Point
+ * Create a new point handler.
+ *
+ * Parameters:
+ * control - {<OpenLayers.Control>} The control that owns this handler
+ * callbacks - {Object} An object with a 'done' property whose value is a
+ * function to be called when the point drawing is finished.
+ * The callback should expect to recieve a single argument,
+ * the point 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 optional object with properties to be set on the
+ * handler
+ */
+ initialize: function(control, callbacks, options) {
+ // TBD: deal with style
+ this.style = OpenLayers.Util.extend(OpenLayers.Feature.Vector.style['default'], {});
+
+ OpenLayers.Handler.prototype.initialize.apply(this, arguments);
+ },
+
+ /**
+ * APIMethod: activate
+ * turn on the handler
+ */
+ activate: function() {
+ if(!OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
+ return false;
+ }
+ // create temporary vector layer for rendering geometry sketch
+ // TBD: this could be moved to initialize/destroy - setting visibility here
+ 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);
+ return true;
+ },
+
+ /**
+ * Method: createFeature
+ * Add temporary features
+ */
+ createFeature: function() {
+ this.point = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point());
+ },
+
+ /**
+ * APIMethod: deactivate
+ * turn off the handler
+ */
+ deactivate: function() {
+ if(!OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
+ return false;
+ }
+ // call the cancel callback if mid-drawing
+ if(this.drawing) {
+ 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);
+ }
+ this.layer = null;
+ return true;
+ },
+
+ /**
+ * Method: destroyFeature
+ * Destroy the temporary geometries
+ */
+ destroyFeature: function() {
+ if(this.point) {
+ this.point.destroy();
+ }
+ this.point = null;
+ },
+
+ /**
+ * Method: finalize
+ * Finish the geometry and call the "done" callback.
+ */
+ finalize: function() {
+ this.layer.renderer.clear();
+ this.drawing = false;
+ this.mouseDown = false;
+ this.lastDown = null;
+ this.lastUp = null;
+ this.callback("done", [this.geometryClone()]);
+ this.destroyFeature();
+ },
+
+ /**
+ * APIMethod: cancel
+ * Finish the geometry and call the "cancel" callback.
+ */
+ cancel: function() {
+ this.layer.renderer.clear();
+ this.drawing = false;
+ this.mouseDown = false;
+ this.lastDown = null;
+ this.lastUp = null;
+ this.callback("cancel", [this.geometryClone()]);
+ this.destroyFeature();
+ },
+
+ /**
+ * Method: click
+ * Handle clicks. Clicks are stopped from propagating to other listeners
+ * on map.events or other dom elements.
+ *
+ * Parameters:
+ * evt - {Event} The browser event
+ *
+ * Returns:
+ * {Boolean} Allow event propagation
+ */
+ click: function(evt) {
+ OpenLayers.Event.stop(evt);
+ return false;
+ },
+
+ /**
+ * Method: dblclick
+ * Handle double-clicks. Double-clicks are stopped from propagating to other
+ * listeners on map.events or other dom elements.
+ *
+ * Parameters:
+ * evt - {Event} The browser event
+ *
+ * Returns:
+ * {Boolean} Allow event propagation
+ */
+ dblclick: function(evt) {
+ OpenLayers.Event.stop(evt);
+ return false;
+ },
+
+ /**
+ * Method: drawFeature
+ * Render features on the temporary layer.
+ */
+ drawFeature: function() {
+ this.layer.drawFeature(this.point, this.style);
+ },
+
+ /**
+ * Method: geometryClone
+ * Return a clone of the relevant geometry.
+ *
+ * Returns:
+ * {<OpenLayers.Geometry.Point>}
+ */
+ geometryClone: function() {
+ return this.point.geometry.clone();
+ },
+
+ /**
+ * Method: mousedown
+ * Handle mouse down. Adjust the geometry and redraw.
+ * Return determines whether to propagate the event on the map.
+ *
+ * Parameters:
+ * evt - {Event} The browser event
+ *
+ * Returns:
+ * {Boolean} Allow event propagation
+ */
+ mousedown: function(evt) {
+ // check keyboard modifiers
+ if(!this.checkModifiers(evt)) {
+ return true;
+ }
+ // ignore double-clicks
+ if(this.lastDown && this.lastDown.equals(evt.xy)) {
+ return true;
+ }
+ if(this.lastDown == null) {
+ this.createFeature();
+ }
+ this.lastDown = evt.xy;
+ this.drawing = true;
+ var lonlat = this.map.getLonLatFromPixel(evt.xy);
+ this.point.geometry.x = lonlat.lon;
+ this.point.geometry.y = lonlat.lat;
+ this.drawFeature();
+ return false;
+ },
+
+ /**
+ * Method: mousemove
+ * Handle mouse move. Adjust the geometry and redraw.
+ * Return determines whether to propagate the event on the map.
+ *
+ * Parameters:
+ * evt - {Event} The browser event
+ *
+ * Returns:
+ * {Boolean} Allow event propagation
+ */
+ mousemove: function (evt) {
+ if(this.drawing) {
+ var lonlat = this.map.getLonLatFromPixel(evt.xy);
+ this.point.geometry.x = lonlat.lon;
+ this.point.geometry.y = lonlat.lat;
+ this.point.geometry.clearBounds();
+ this.drawFeature();
+ }
+ return true;
+ },
+
+ /**
+ * Method: mouseup
+ * Handle mouse up. Send the latest point in the geometry to the control.
+ * Return determines whether to propagate the event on the map.
+ *
+ * Parameters:
+ * evt - {Event} The browser event
+ *
+ * Returns:
+ * {Boolean} Allow event propagation
+ */
+ mouseup: function (evt) {
+ if(this.drawing) {
+ this.finalize();
+ return false;
+ } else {
+ return true;
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Handler.Point"
+});
+/* ======================================================================
+ OpenLayers/Layer/GML.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/Layer/Vector.js
+ * @requires OpenLayers/Request/XMLHttpRequest.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.GML
+ * Create a vector layer by parsing a GML file. The GML file is
+ * passed in as a parameter.
+ *
+ * Inherits from:
+ * - <OpenLayers.Layer.Vector>
+ */
+OpenLayers.Layer.GML = OpenLayers.Class(OpenLayers.Layer.Vector, {
+
+ /**
+ * Property: loaded
+ * {Boolean} Flag for whether the GML data has been loaded yet.
+ */
+ loaded: false,
+
+ /**
+ * APIProperty: format
+ * {<OpenLayers.Format>} The format you want the data to be parsed with.
+ */
+ format: null,
+
+ /**
+ * APIProperty: formatOptions
+ * {Object} Hash of options which should be passed to the format when it is
+ * created. Must be passed in the constructor.
+ */
+ formatOptions: null,
+
+ /**
+ * Constructor: OpenLayers.Layer.GML
+ * Load and parse a single file on the web, according to the format
+ * provided via the 'format' option, defaulting to GML.
+ *
+ * Parameters:
+ * name - {String}
+ * url - {String} URL of a GML file.
+ * options - {Object} Hashtable of extra options to tag onto the layer.
+ */
+ initialize: function(name, url, options) {
+ var newArguments = [];
+ newArguments.push(name, options);
+ OpenLayers.Layer.Vector.prototype.initialize.apply(this, newArguments);
+ this.url = url;
+ },
+
+ /**
+ * APIMethod: setVisibility
+ * Set the visibility flag for the layer and hide/show&redraw accordingly.
+ * Fire event unless otherwise specified
+ * GML will be loaded if the layer is being made visible for the first
+ * time.
+ *
+ * Parameters:
+ * visible - {Boolean} Whether or not to display the layer
+ * (if in range)
+ * noEvent - {Boolean}
+ */
+ setVisibility: function(visibility, noEvent) {
+ OpenLayers.Layer.Vector.prototype.setVisibility.apply(this, arguments);
+ if(this.visibility && !this.loaded){
+ // Load the GML
+ this.loadGML();
+ }
+ },
+
+ /**
+ * Method: moveTo
+ * If layer is visible and GML has not been loaded, load GML, then load GML
+ * and call OpenLayers.Layer.Vector.moveTo() to redraw at the new location.
+ *
+ * Parameters:
+ * bounds - {Object}
+ * zoomChanged - {Object}
+ * minor - {Object}
+ */
+ moveTo:function(bounds, zoomChanged, minor) {
+ OpenLayers.Layer.Vector.prototype.moveTo.apply(this, arguments);
+ // Wait until initialisation is complete before loading GML
+ // otherwise we can get a race condition where the root HTML DOM is
+ // loaded after the GML is paited.
+ // See http://trac.openlayers.org/ticket/404
+ if(this.visibility && !this.loaded){
+ this.events.triggerEvent("loadstart");
+ this.loadGML();
+ }
+ },
+
+ /**
+ * Method: loadGML
+ */
+ loadGML: function() {
+ if (!this.loaded) {
+ OpenLayers.Request.GET({
+ url: this.url,
+ success: this.requestSuccess,
+ failure: this.requestFailure,
+ scope: this
+ });
+ this.loaded = true;
+ }
+ },
+
+ /**
+ * Method: setUrl
+ * Change the URL and reload the GML
+ *
+ * Parameters:
+ * url - {String} URL of a GML file.
+ */
+ setUrl:function(url) {
+ this.url = url;
+ this.destroyFeatures();
+ this.loaded = false;
+ this.events.triggerEvent("loadstart");
+ this.loadGML();
+ },
+
+ /**
+ * Method: requestSuccess
+ * Process GML after it has been loaded.
+ * Called by initialise() and loadUrl() after the GML has been loaded.
+ *
+ * Parameters:
+ * request - {String}
+ */
+ requestSuccess:function(request) {
+ var doc = request.responseXML;
+
+ if (!doc || !doc.documentElement) {
+ doc = request.responseText;
+ }
+
+ var options = {};
+
+ OpenLayers.Util.extend(options, this.formatOptions);
+ if (this.map && !this.projection.equals(this.map.getProjectionObject())) {
+ options.externalProjection = this.projection;
+ options.internalProjection = this.map.getProjectionObject();
+ }
+
+ var gml = this.format ? new this.format(options) : new OpenLayers.Format.GML(options);
+ this.addFeatures(gml.read(doc));
+ this.events.triggerEvent("loadend");
+ },
+
+ /**
+ * Method: requestFailure
+ * Process a failed loading of GML.
+ * Called by initialise() and loadUrl() if there was a problem loading GML.
+ *
+ * Parameters:
+ * request - {String}
+ */
+ requestFailure: function(request) {
+ OpenLayers.Console.userError(OpenLayers.i18n("errorLoadingGML", {'url':this.url}));
+ this.events.triggerEvent("loadend");
+ },
+
+ CLASS_NAME: "OpenLayers.Layer.GML"
+});
+/* ======================================================================
+ OpenLayers/Layer/PointTrack.js
+ ====================================================================== */
+
+/* Copyright (c) 2006-2007 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/Layer/Vector.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.PointTrack
+ * Vector layer to display ordered point features as a line, creating one
+ * LineString feature for each pair of two points.
+ *
+ * Inherits from:
+ * - <OpenLayers.Layer.Vector>
+ */
+OpenLayers.Layer.PointTrack = OpenLayers.Class(OpenLayers.Layer.Vector, {
+
+ /**
+ * APIProperty:
+ * dataFrom - {<OpenLayers.Layer.PointTrack.dataFrom>} optional. If the
+ * lines should get the data/attributes from one of the two
+ * points, creating it, which one should it be?
+ */
+ dataFrom: null,
+
+ /**
+ * Constructor: OpenLayers.PointTrack
+ * Constructor for a new OpenLayers.PointTrack instance.
+ *
+ * Parameters:
+ * name - {String} name of the layer
+ * options - {Object} Optional object with properties to tag onto the
+ * instance.
+ */
+ initialize: function(name, options) {
+ OpenLayers.Layer.Vector.prototype.initialize.apply(this, arguments);
+ },
+
+ /**
+ * APIMethod: addNodes
+ * Adds point features that will be used to create lines from, using point
+ * pairs. The first point of a pair will be the source node, the second
+ * will be the target node.
+ *
+ * Parameters:
+ * pointFeatures - {Array(<OpenLayers.Feature>)}
+ *
+ */
+ addNodes: function(pointFeatures) {
+ if (pointFeatures.length < 2) {
+ OpenLayers.Console.error(
+ "At least two point features have to be added to create" +
+ "a line from");
+ return;
+ }
+
+ var lines = new Array(pointFeatures.length-1);
+
+ var pointFeature, startPoint, endPoint;
+ for(var i=0, len=pointFeatures.length; i<len; i++) {
+ pointFeature = pointFeatures[i];
+ endPoint = pointFeature.geometry;
+
+ if (!endPoint) {
+ var lonlat = pointFeature.lonlat;
+ endPoint = new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat);
+ } else if(endPoint.CLASS_NAME != "OpenLayers.Geometry.Point") {
+ OpenLayers.Console.error(
+ "Only features with point geometries are supported.");
+ return;
+ }
+
+ if(i > 0) {
+ var attributes = (this.dataFrom != null) ?
+ (pointFeatures[i+this.dataFrom].data ||
+ pointFeatures[i+this.dataFrom].attributes) :
+ null;
+ var line = new OpenLayers.Geometry.LineString([startPoint,
+ endPoint]);
+
+ lines[i-1] = new OpenLayers.Feature.Vector(line, attributes);
+ }
+
+ startPoint = endPoint;
+ }
+
+ this.addFeatures(lines);
+ },
+
+ CLASS_NAME: "OpenLayers.Layer.PointTrack"
+});
+
+/**
+ * Constant: OpenLayers.Layer.PointTrack.dataFrom
+ * {Object} with the following keys
+ * - SOURCE_NODE: take data/attributes from the source node of the line
+ * - TARGET_NODE: take data/attributes from the target node of the line
+ */
+OpenLayers.Layer.PointTrack.dataFrom = {'SOURCE_NODE': -1, 'TARGET_NODE': 0};
+
+/* ======================================================================
+ OpenLayers/Layer/WFS.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/Tile/WFS.js
+ * @requires OpenLayers/Layer/Vector.js
+ * @requires OpenLayers/Layer/Markers.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.WFS
+ *
+ * Inherits from:
+ * - <OpenLayers.Layer.Vector>
+ * - <OpenLayers.Layer.Markers>
+ */
+OpenLayers.Layer.WFS = OpenLayers.Class(
+ OpenLayers.Layer.Vector, OpenLayers.Layer.Markers, {
+
+ /**
+ * APIProperty: isBaseLayer
+ * {Boolean} WFS layer is not a base layer by default.
+ */
+ isBaseLayer: false,
+
+ /**
+ * Property: tile
+ * {<OpenLayers.Tile.WFS>}
+ */
+ tile: null,
+
+ /**
+ * APIProperty: ratio
+ * {Float} the ratio of image/tile size to map size (this is the untiled
+ * buffer)
+ */
+ ratio: 2,
+
+ /**
+ * Property: DEFAULT_PARAMS
+ * {Object} Hashtable of default key/value parameters
+ */
+ DEFAULT_PARAMS: { service: "WFS",
+ version: "1.0.0",
+ request: "GetFeature"
+ },
+
+ /**
+ * APIProperty: featureClass
+ * {<OpenLayers.Feature>} If featureClass is defined, an old-style markers
+ * based WFS layer is created instead of a new-style vector layer. If
+ * sent, this should be a subclass of OpenLayers.Feature
+ */
+ featureClass: null,
+
+ /**
+ * APIProperty: format
+ * {<OpenLayers.Format>} The format you want the data to be parsed with.
+ * Must be passed in the constructor. Should be a class, not an instance.
+ * This option can only be used if no featureClass is passed / vectorMode
+ * is false: if a featureClass is passed, then this parameter is ignored.
+ */
+ format: null,
+
+ /**
+ * Property: formatObject
+ * {<OpenLayers.Format>} Internally created/managed format object, used by
+ * the Tile to parse data.
+ */
+ formatObject: null,
+
+ /**
+ * APIProperty: formatOptions
+ * {Object} Hash of options which should be passed to the format when it is
+ * created. Must be passed in the constructor.
+ */
+ formatOptions: null,
+
+ /**
+ * Property: vectorMode
+ * {Boolean} Should be calculated automatically. Determines whether the
+ * layer is in vector mode or marker mode.
+ */
+ vectorMode: true,
+
+ /**
+ * APIProperty: encodeBBOX
+ * {Boolean} Should the BBOX commas be encoded? The WMS spec says 'no',
+ * but some services want it that way. Default false.
+ */
+ encodeBBOX: false,
+
+ /**
+ * APIProperty: extractAttributes
+ * {Boolean} Should the WFS layer parse attributes from the retrieved
+ * GML? Defaults to false. If enabled, parsing is slower, but
+ * attributes are available in the attributes property of
+ * layer features.
+ */
+ extractAttributes: false,
+
+ /**
+ * Constructor: OpenLayers.Layer.WFS
+ *
+ * Parameters:
+ * name - {String}
+ * url - {String}
+ * params - {Object}
+ * options - {Object} Hashtable of extra options to tag onto the layer
+ */
+ initialize: function(name, url, params, options) {
+ if (options == undefined) { options = {}; }
+
+ if (options.featureClass ||
+ !OpenLayers.Layer.Vector ||
+ !OpenLayers.Feature.Vector) {
+ this.vectorMode = false;
+ }
+
+ // Turn off error reporting, browsers like Safari may work
+ // depending on the setup, and we don't want an unneccesary alert.
+ OpenLayers.Util.extend(options, {'reportError': false});
+ var newArguments = [];
+ newArguments.push(name, options);
+ OpenLayers.Layer.Vector.prototype.initialize.apply(this, newArguments);
+ if (!this.renderer || !this.vectorMode) {
+ this.vectorMode = false;
+ if (!options.featureClass) {
+ options.featureClass = OpenLayers.Feature.WFS;
+ }
+ OpenLayers.Layer.Markers.prototype.initialize.apply(this,
+ newArguments);
+ }
+
+ if (this.params && this.params.typename && !this.options.typename) {
+ this.options.typename = this.params.typename;
+ }
+
+ if (!this.options.geometry_column) {
+ this.options.geometry_column = "the_geom";
+ }
+
+ this.params = OpenLayers.Util.applyDefaults(
+ params,
+ OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS)
+ );
+ this.url = url;
+ },
+
+
+ /**
+ * APIMethod: destroy
+ */
+ destroy: function() {
+ if (this.vectorMode) {
+ OpenLayers.Layer.Vector.prototype.destroy.apply(this, arguments);
+ } else {
+ OpenLayers.Layer.Markers.prototype.destroy.apply(this, arguments);
+ }
+ if (this.tile) {
+ this.tile.destroy();
+ }
+ this.tile = null;
+
+ this.ratio = null;
+ this.featureClass = null;
+ this.format = null;
+
+ if (this.formatObject && this.formatObject.destroy) {
+ this.formatObject.destroy();
+ }
+ this.formatObject = null;
+
+ this.formatOptions = null;
+ this.vectorMode = null;
+ this.encodeBBOX = null;
+ this.extractAttributes = null;
+ },
+
+ /**
+ * Method: setMap
+ *
+ * Parameters:
+ * map - {<OpenLayers.Map>}
+ */
+ setMap: function(map) {
+ if (this.vectorMode) {
+ OpenLayers.Layer.Vector.prototype.setMap.apply(this, arguments);
+
+ var options = {
+ 'extractAttributes': this.extractAttributes
+ };
+
+ OpenLayers.Util.extend(options, this.formatOptions);
+ if (this.map && !this.projection.equals(this.map.getProjectionObject())) {
+ options.externalProjection = this.projection;
+ options.internalProjection = this.map.getProjectionObject();
+ }
+
+ this.formatObject = this.format ? new this.format(options) : new OpenLayers.Format.GML(options);
+ } else {
+ OpenLayers.Layer.Markers.prototype.setMap.apply(this, arguments);
+ }
+ },
+
+ /**
+ * Method: moveTo
+ *
+ * Parameters:
+ * bounds - {<OpenLayers.Bounds>}
+ * zoomChanged - {Boolean}
+ * dragging - {Boolean}
+ */
+ moveTo:function(bounds, zoomChanged, dragging) {
+ if (this.vectorMode) {
+ OpenLayers.Layer.Vector.prototype.moveTo.apply(this, arguments);
+ } else {
+ OpenLayers.Layer.Markers.prototype.moveTo.apply(this, arguments);
+ }
+
+ // don't load wfs features while dragging, wait for drag end
+ if (dragging) {
+ // TBD try to hide the vector layer while dragging
+ // this.setVisibility(false);
+ // this will probably help for panning performances
+ return false;
+ }
+
+ if ( zoomChanged ) {
+ if (this.vectorMode) {
+ this.renderer.clear();
+ }
+ }
+
+ //DEPRECATED - REMOVE IN 3.0
+ // don't load data if current zoom level doesn't match
+ if (this.options.minZoomLevel) {
+ OpenLayers.Console.warn(OpenLayers.i18n('minZoomLevelError'));
+
+ if (this.map.getZoom() < this.options.minZoomLevel) {
+ return null;
+ }
+ }
+
+ if (bounds == null) {
+ bounds = this.map.getExtent();
+ }
+
+ var firstRendering = (this.tile == null);
+
+ //does the new bounds to which we need to move fall outside of the
+ // current tile's bounds?
+ var outOfBounds = (!firstRendering &&
+ !this.tile.bounds.containsBounds(bounds));
+
+ if (zoomChanged || firstRendering || (!dragging && outOfBounds)) {
+ //determine new tile bounds
+ var center = bounds.getCenterLonLat();
+ var tileWidth = bounds.getWidth() * this.ratio;
+ var tileHeight = bounds.getHeight() * this.ratio;
+ var tileBounds =
+ new OpenLayers.Bounds(center.lon - (tileWidth / 2),
+ center.lat - (tileHeight / 2),
+ center.lon + (tileWidth / 2),
+ center.lat + (tileHeight / 2));
+
+ //determine new tile size
+ var tileSize = this.map.getSize();
+ tileSize.w = tileSize.w * this.ratio;
+ tileSize.h = tileSize.h * this.ratio;
+
+ //determine new position (upper left corner of new bounds)
+ var ul = new OpenLayers.LonLat(tileBounds.left, tileBounds.top);
+ var pos = this.map.getLayerPxFromLonLat(ul);
+
+ //formulate request url string
+ var url = this.getFullRequestString();
+
+ var params = {BBOX: this.encodeBBOX ? tileBounds.toBBOX()
+ : tileBounds.toArray()};
+
+ if (this.map && !this.projection.equals(this.map.getProjectionObject())) {
+ var projectedBounds = tileBounds.clone();
+ projectedBounds.transform(this.map.getProjectionObject(),
+ this.projection);
+ params.BBOX = this.encodeBBOX ? projectedBounds.toBBOX()
+ : projectedBounds.toArray();
+ }
+
+ url += "&" + OpenLayers.Util.getParameterString(params);
+
+ if (!this.tile) {
+ this.tile = new OpenLayers.Tile.WFS(this, pos, tileBounds,
+ url, tileSize);
+ this.addTileMonitoringHooks(this.tile);
+ this.tile.draw();
+ } else {
+ if (this.vectorMode) {
+ this.destroyFeatures();
+ this.renderer.clear();
+ } else {
+ this.clearMarkers();
+ }
+ this.removeTileMonitoringHooks(this.tile);
+ this.tile.destroy();
+
+ this.tile = null;
+ this.tile = new OpenLayers.Tile.WFS(this, pos, tileBounds,
+ url, tileSize);
+ this.addTileMonitoringHooks(this.tile);
+ this.tile.draw();
+ }
+ }
+ },
+
+ /**
+ * Method: addTileMonitoringHooks
+ * This function takes a tile as input and adds the appropriate hooks to
+ * the tile so that the layer can keep track of the loading tile
+ * (making sure to check that the tile is always the layer's current
+ * tile before taking any action).
+ *
+ * Parameters:
+ * tile - {<OpenLayers.Tile>}
+ */
+ addTileMonitoringHooks: function(tile) {
+ tile.onLoadStart = function() {
+ //if this is the the layer's current tile, then trigger
+ // a 'loadstart'
+ if (this == this.layer.tile) {
+ this.layer.events.triggerEvent("loadstart");
+ }
+ };
+ tile.events.register("loadstart", tile, tile.onLoadStart);
+
+ tile.onLoadEnd = function() {
+ //if this is the the layer's current tile, then trigger
+ // a 'tileloaded' and 'loadend'
+ if (this == this.layer.tile) {
+ this.layer.events.triggerEvent("tileloaded");
+ this.layer.events.triggerEvent("loadend");
+ }
+ };
+ tile.events.register("loadend", tile, tile.onLoadEnd);
+ tile.events.register("unload", tile, tile.onLoadEnd);
+ },
+
+ /**
+ * Method: removeTileMonitoringHooks
+ * This function takes a tile as input and removes the tile hooks
+ * that were added in addTileMonitoringHooks()
+ *
+ * Parameters:
+ * tile - {<OpenLayers.Tile>}
+ */
+ removeTileMonitoringHooks: function(tile) {
+ tile.unload();
+ tile.events.un({
+ "loadstart": tile.onLoadStart,
+ "loadend": tile.onLoadEnd,
+ "unload": tile.onLoadEnd,
+ scope: tile
+ });
+ },
+
+ /**
+ * Method: onMapResize
+ * Call the onMapResize method of the appropriate parent class.
+ */
+ onMapResize: function() {
+ if(this.vectorMode) {
+ OpenLayers.Layer.Vector.prototype.onMapResize.apply(this,
+ arguments);
+ } else {
+ OpenLayers.Layer.Markers.prototype.onMapResize.apply(this,
+ arguments);
+ }
+ },
+
+ /**
+ * APIMethod: mergeNewParams
+ * Modify parameters for the layer and redraw.
+ *
+ * Parameters:
+ * newParams - {Object}
+ */
+ mergeNewParams:function(newParams) {
+ var upperParams = OpenLayers.Util.upperCaseObject(newParams);
+ var newArguments = [upperParams];
+ return OpenLayers.Layer.HTTPRequest.prototype.mergeNewParams.apply(this,
+ newArguments);
+ },
+
+ /**
+ * APIMethod: clone
+ *
+ * Parameters:
+ * obj - {Object}
+ *
+ * Returns:
+ * {<OpenLayers.Layer.WFS>} An exact clone of this OpenLayers.Layer.WFS
+ */
+ clone: function (obj) {
+
+ if (obj == null) {
+ obj = new OpenLayers.Layer.WFS(this.name,
+ this.url,
+ this.params,
+ this.options);
+ }
+
+ //get all additions from superclasses
+ if (this.vectorMode) {
+ obj = OpenLayers.Layer.Vector.prototype.clone.apply(this, [obj]);
+ } else {
+ obj = OpenLayers.Layer.Markers.prototype.clone.apply(this, [obj]);
+ }
+
+ // copy/set any non-init, non-simple values here
+
+ return obj;
+ },
+
+ /**
+ * APIMethod: getFullRequestString
+ * combine the layer's url with its params and these newParams.
+ *
+ * Add the SRS parameter from 'projection' -- this is probably
+ * more eloquently done via a setProjection() method, but this
+ * works for now and always.
+ *
+ * Parameters:
+ * newParams - {Object}
+ * altUrl - {String} Use this as the url instead of the layer's url
+ */
+ getFullRequestString:function(newParams, altUrl) {
+ var projectionCode = this.projection.getCode() || this.map.getProjection();
+ this.params.SRS = (projectionCode == "none") ? null : projectionCode;
+
+ return OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(
+ this, arguments);
+ },
+
+ /**
+ * APIMethod: commit
+ * Write out the data to a WFS server.
+ */
+ commit: function() {
+ if (!this.writer) {
+ var options = {};
+ if (this.map && !this.projection.equals(this.map.getProjectionObject())) {
+ options.externalProjection = this.projection;
+ options.internalProjection = this.map.getProjectionObject();
+ }
+
+ this.writer = new OpenLayers.Format.WFS(options,this);
+ }
+
+ var data = this.writer.write(this.features);
+
+ OpenLayers.Request.POST({
+ url: this.url,
+ data: data,
+ success: this.commitSuccess,
+ failure: this.commitFailure,
+ scope: this
+ });
+ },
+
+ /**
+ * Method: commitSuccess
+ * Called when the Ajax request returns a response
+ *
+ * Parameters:
+ * response - {XmlNode} from server
+ */
+ commitSuccess: function(request) {
+ var response = request.responseText;
+ if (response.indexOf('SUCCESS') != -1) {
+ this.commitReport(OpenLayers.i18n("commitSuccess", {'response':response}));
+
+ for(var i = 0; i < this.features.length; i++) {
+ this.features[i].state = null;
+ }
+ // TBD redraw the layer or reset the state of features
+ // foreach features: set state to null
+ } else if (response.indexOf('FAILED') != -1 ||
+ response.indexOf('Exception') != -1) {
+ this.commitReport(OpenLayers.i18n("commitFailed", {'response':response}));
+ }
+ },
+
+ /**
+ * Method: commitFailure
+ * Called when the Ajax request fails
+ *
+ * Parameters:
+ * response - {XmlNode} from server
+ */
+ commitFailure: function(request) {},
+
+ /**
+ * APIMethod: commitReport
+ * Called with a 'success' message if the commit succeeded, otherwise
+ * a failure message, and the full request text as a second parameter.
+ * Override this function to provide custom transaction reporting.
+ *
+ * string - {String} reporting string
+ * response - {String} full XML response
+ */
+ commitReport: function(string, response) {
+ OpenLayers.Console.userError(string);
+ },
+
+
+ /**
+ * APIMethod: refresh
+ * Refreshes all the features of the layer
+ */
+ refresh: function() {
+ if (this.tile) {
+ if (this.vectorMode) {
+ this.renderer.clear();
+ this.features.length = 0;
+ } else {
+ this.clearMarkers();
+ this.markers.length = 0;
+ }
+ this.tile.draw();
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Layer.WFS"
+});
+/* ======================================================================
+ OpenLayers/Format/Filter/v1.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/Format/Filter.js
+ */
+
+/**
+ * Class: OpenLayers.Format.Filter.v1
+ * Superclass for Filter version 1 parsers.
+ *
+ * Inherits from:
+ * - <OpenLayers.Format.XML>
+ */
+OpenLayers.Format.Filter.v1 = OpenLayers.Class(OpenLayers.Format.XML, {
+
+ /**
+ * Property: namespaces
+ * {Object} Mapping of namespace aliases to namespace URIs.
+ */
+ namespaces: {
+ ogc: "http://www.opengis.net/ogc",
+ xlink: "http://www.w3.org/1999/xlink",
+ xsi: "http://www.w3.org/2001/XMLSchema-instance"
+ },
+
+ /**
+ * Property: defaultPrefix
+ */
+ defaultPrefix: "ogc",
+
+ /**
+ * Property: schemaLocation
+ * {String} Schema location for a particular minor version.
+ */
+ schemaLocation: null,
+
+ /**
+ * Constructor: OpenLayers.Format.Filter.v1
+ * Instances of this class are not created directly. Use the
+ * <OpenLayers.Format.Filter> constructor instead.
+ *
+ * Parameters:
+ * options - {Object} An optional object whose properties will be set on
+ * this instance.
+ */
+ initialize: function(options) {
+ OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
+ },
+
+ /**
+ * Method: read
+ *
+ * Parameters:
+ * data - {DOMElement} A Filter document element.
+ *
+ * Returns:
+ * {<OpenLayers.Filter>} A filter object.
+ */
+ read: function(data) {
+ var obj = {}
+ var filter = this.readers.ogc["Filter"].apply(this, [data, obj]);
+ return obj.filter;
+ },
+
+ /**
+ * Property: readers
+ * Contains public functions, grouped by namespace prefix, that will
+ * be applied when a namespaced node is found matching the function
+ * name. The function will be applied in the scope of this parser
+ * with two arguments: the node being read and a context object passed
+ * from the parent.
+ */
+ readers: {
+ "ogc": {
+ "Filter": function(node, parent) {
+ // Filters correspond to subclasses of OpenLayers.Filter.
+ // Since they contain information we don't persist, we
+ // create a temporary object and then pass on the filter
+ // (ogc:Filter) to the parent obj.
+ var obj = {
+ fids: [],
+ filters: []
+ };
+ this.readChildNodes(node, obj);
+ if(obj.fids.length > 0) {
+ parent.filter = new OpenLayers.Filter.FeatureId({
+ fids: obj.fids
+ });
+ } else if(obj.filters.length > 0) {
+ parent.filter = obj.filters[0];
+ }
+ },
+ "FeatureId": function(node, obj) {
+ var fid = node.getAttribute("fid");
+ if(fid) {
+ obj.fids.push(fid);
+ }
+ },
+ "And": function(node, obj) {
+ var filter = new OpenLayers.Filter.Logical({
+ type: OpenLayers.Filter.Logical.AND
+ });
+ this.readChildNodes(node, filter);
+ obj.filters.push(filter);
+ },
+ "Or": function(node, obj) {
+ var filter = new OpenLayers.Filter.Logical({
+ type: OpenLayers.Filter.Logical.OR
+ });
+ this.readChildNodes(node, filter);
+ obj.filters.push(filter);
+ },
+ "Not": function(node, obj) {
+ var filter = new OpenLayers.Filter.Logical({
+ type: OpenLayers.Filter.Logical.NOT
+ });
+ this.readChildNodes(node, filter);
+ obj.filters.push(filter);
+ },
+ "PropertyIsEqualTo": function(node, obj) {
+ var filter = new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.EQUAL_TO
+ });
+ this.readChildNodes(node, filter);
+ obj.filters.push(filter);
+ },
+ "PropertyIsNotEqualTo": function(node, obj) {
+ var filter = new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.NOT_EQUAL_TO
+ });
+ this.readChildNodes(node, filter);
+ obj.filters.push(filter);
+ },
+ "PropertyIsLessThan": function(node, obj) {
+ var filter = new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.LESS_THAN
+ });
+ this.readChildNodes(node, filter);
+ obj.filters.push(filter);
+ },
+ "PropertyIsGreaterThan": function(node, obj) {
+ var filter = new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.GREATER_THAN
+ });
+ this.readChildNodes(node, filter);
+ obj.filters.push(filter);
+ },
+ "PropertyIsLessThanOrEqualTo": function(node, obj) {
+ var filter = new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO
+ });
+ this.readChildNodes(node, filter);
+ obj.filters.push(filter);
+ },
+ "PropertyIsGreaterThanOrEqualTo": function(node, obj) {
+ var filter = new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO
+ });
+ this.readChildNodes(node, filter);
+ obj.filters.push(filter);
+ },
+ "PropertyIsBetween": function(node, obj) {
+ var filter = new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.BETWEEN
+ });
+ this.readChildNodes(node, filter);
+ obj.filters.push(filter);
+ },
+ "PropertyIsLike": function(node, obj) {
+ var filter = new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.LIKE
+ });
+ this.readChildNodes(node, filter);
+ var wildCard = node.getAttribute("wildCard");
+ var singleChar = node.getAttribute("singleChar");
+ var esc = node.getAttribute("escape");
+ filter.value2regex(wildCard, singleChar, esc);
+ obj.filters.push(filter);
+ },
+ "Literal": function(node, obj) {
+ obj.value = this.getChildValue(node);
+ },
+ "PropertyName": function(node, filter) {
+ filter.property = this.getChildValue(node);
+ },
+ "LowerBoundary": function(node, filter) {
+ filter.lowerBoundary = this.readOgcExpression(node);
+ },
+ "UpperBoundary": function(node, filter) {
+ filter.upperBoundary = this.readOgcExpression(node);
+ }
+
+ }
+ },
+
+ /**
+ * Method: readOgcExpression
+ * Limited support for OGC expressions.
+ *
+ * Parameters:
+ * node - {DOMElement} A DOM element that contains an ogc:expression.
+ *
+ * Returns:
+ * {String} A value to be used in a symbolizer.
+ */
+ readOgcExpression: function(node) {
+ var obj = {};
+ this.readChildNodes(node, obj);
+ var value = obj.value;
+ if(!value) {
+ value = this.getChildValue(node);
+ }
+ return value;
+ },
+
+ /**
+ * Method: write
+ *
+ * Parameters:
+ * filter - {<OpenLayers.Filter>} A filter object.
+ *
+ * Returns:
+ * {DOMElement} An ogc:Filter element.
+ */
+ write: function(filter) {
+ return this.writers.ogc["Filter"].apply(this, [filter]);
+ },
+
+ /**
+ * Property: writers
+ * As a compliment to the readers property, this structure contains public
+ * writing functions grouped by namespace alias and named like the
+ * node names they produce.
+ */
+ writers: {
+ "ogc": {
+ "Filter": function(filter) {
+ var node = this.createElementNSPlus("ogc:Filter");
+ var sub = filter.CLASS_NAME.split(".").pop();
+ if(sub == "FeatureId") {
+ for(var i=0; i<filter.fids.length; ++i) {
+ this.writeNode(node, "FeatureId", filter.fids[i]);
+ }
+ } else {
+ this.writeNode(node, this.getFilterType(filter), filter);
+ }
+ return node;
+ },
+ "FeatureId": function(fid) {
+ return this.createElementNSPlus("ogc:FeatureId", {
+ attributes: {fid: fid}
+ });
+ },
+ "And": function(filter) {
+ var node = this.createElementNSPlus("ogc:And");
+ var childFilter;
+ for(var i=0; i<filter.filters.length; ++i) {
+ childFilter = filter.filters[i];
+ this.writeNode(
+ node, this.getFilterType(childFilter), childFilter
+ );
+ }
+ return node;
+ },
+ "Or": function(filter) {
+ var node = this.createElementNSPlus("ogc:Or");
+ var childFilter;
+ for(var i=0; i<filter.filters.length; ++i) {
+ childFilter = filter.filters[i];
+ this.writeNode(
+ node, this.getFilterType(childFilter), childFilter
+ );
+ }
+ return node;
+ },
+ "Not": function(filter) {
+ var node = this.createElementNSPlus("ogc:Not");
+ var childFilter = filter.filters[0];
+ this.writeNode(
+ node, this.getFilterType(childFilter), childFilter
+ );
+ return node;
+ },
+ "PropertyIsEqualTo": function(filter) {
+ var node = this.createElementNSPlus("ogc:PropertyIsEqualTo");
+ // no ogc:expression handling for now
+ this.writeNode(node, "PropertyName", filter);
+ this.writeNode(node, "Literal", filter.value);
+ return node;
+ },
+ "PropertyIsNotEqualTo": function(filter) {
+ var node = this.createElementNSPlus("ogc:PropertyIsNotEqualTo");
+ // no ogc:expression handling for now
+ this.writeNode(node, "PropertyName", filter);
+ this.writeNode(node, "Literal", filter.value);
+ return node;
+ },
+ "PropertyIsLessThan": function(filter) {
+ var node = this.createElementNSPlus("ogc:PropertyIsLessThan");
+ // no ogc:expression handling for now
+ this.writeNode(node, "PropertyName", filter);
+ this.writeNode(node, "Literal", filter.value);
+ return node;
+ },
+ "PropertyIsGreaterThan": function(filter) {
+ var node = this.createElementNSPlus("ogc:PropertyIsGreaterThan");
+ // no ogc:expression handling for now
+ this.writeNode(node, "PropertyName", filter);
+ this.writeNode(node, "Literal", filter.value);
+ return node;
+ },
+ "PropertyIsLessThanOrEqualTo": function(filter) {
+ var node = this.createElementNSPlus("ogc:PropertyIsLessThanOrEqualTo");
+ // no ogc:expression handling for now
+ this.writeNode(node, "PropertyName", filter);
+ this.writeNode(node, "Literal", filter.value);
+ return node;
+ },
+ "PropertyIsGreaterThanOrEqualTo": function(filter) {
+ var node = this.createElementNSPlus("ogc:PropertyIsGreaterThanOrEqualTo");
+ // no ogc:expression handling for now
+ this.writeNode(node, "PropertyName", filter);
+ this.writeNode(node, "Literal", filter.value);
+ return node;
+ },
+ "PropertyIsBetween": function(filter) {
+ var node = this.createElementNSPlus("ogc:PropertyIsBetween");
+ // no ogc:expression handling for now
+ this.writeNode(node, "PropertyName", filter);
+ this.writeNode(node, "LowerBoundary", filter);
+ this.writeNode(node, "UpperBoundary", filter);
+ return node;
+ },
+ "PropertyIsLike": function(filter) {
+ var node = this.createElementNSPlus("ogc:PropertyIsLike", {
+ attributes: {
+ wildCard: "*", singleChar: ".", escape: "!"
+ }
+ });
+ // no ogc:expression handling for now
+ this.writeNode(node, "PropertyName", filter);
+ // convert regex string to ogc string
+ this.writeNode(node, "Literal", filter.regex2value());
+ return node;
+ },
+ "PropertyName": function(filter) {
+ // no ogc:expression handling for now
+ return this.createElementNSPlus("ogc:PropertyName", {
+ value: filter.property
+ });
+ },
+ "Literal": function(value) {
+ // no ogc:expression handling for now
+ return this.createElementNSPlus("ogc:Literal", {
+ value: value
+ });
+ },
+ "LowerBoundary": function(filter) {
+ // no ogc:expression handling for now
+ var node = this.createElementNSPlus("ogc:LowerBoundary");
+ this.writeNode(node, "Literal", filter.lowerBoundary);
+ return node;
+ },
+ "UpperBoundary": function(filter) {
+ // no ogc:expression handling for now
+ var node = this.createElementNSPlus("ogc:UpperBoundary");
+ this.writeNode(node, "Literal", filter.upperBoundary);
+ return node;
+ },
+ "BBOX": function(filter) {
+ var node = this.createElementNSPlus("ogc:BBOX");
+ this.writeNode(node, "PropertyName", filter);
+ var gml = new OpenLayers.Format.GML();
+ node.appendChild(gml.buildGeometryNode(filter.value));
+ return node;
+ },
+ "DWITHIN": function(filter) {
+ var node = this.createElementNSPlus("ogc:DWithin");
+ this.writeNode(node, "PropertyName", filter);
+ var gml = new OpenLayers.Format.GML();
+ node.appendChild(gml.buildGeometryNode(filter.value));
+ this.writeNode(node, "Distance", filter);
+ return node;
+ },
+ "INTERSECTS": function(filter) {
+ var node = this.createElementNSPlus("ogc:Intersects");
+ this.writeNode(node, "PropertyName", filter);
+ var gml = new OpenLayers.Format.GML();
+ node.appendChild(gml.buildGeometryNode(filter.value));
+ return node;
+ },
+ "Distance": function(filter) {
+ return this.createElementNSPlus("ogc:Distance",
+ {attributes: {units: filter.distanceUnits},
+ value: filter.distance});
+ }
+ }
+ },
+
+ /**
+ * Method: getFilterType
+ */
+ getFilterType: function(filter) {
+ var filterType = this.filterMap[filter.type];
+ if(!filterType) {
+ throw "Filter writing not supported for rule type: " + filter.type;
+ }
+ return filterType;
+ },
+
+ /**
+ * Property: filterMap
+ * {Object} Contains a member for each filter type. Values are node names
+ * for corresponding OGC Filter child elements.
+ */
+ filterMap: {
+ "&&": "And",
+ "||": "Or",
+ "!": "Not",
+ "==": "PropertyIsEqualTo",
+ "!=": "PropertyIsNotEqualTo",
+ "<": "PropertyIsLessThan",
+ ">": "PropertyIsGreaterThan",
+ "<=": "PropertyIsLessThanOrEqualTo",
+ ">=": "PropertyIsGreaterThanOrEqualTo",
+ "..": "PropertyIsBetween",
+ "~": "PropertyIsLike",
+ "BBOX": "BBOX",
+ "DWITHIN": "DWITHIN",
+ "INTERSECTS": "INTERSECTS"
+ },
+
+
+ /**
+ * Methods below this point are of general use for versioned XML parsers.
+ * These are candidates for an abstract class.
+ */
+
+ /**
+ * Method: getNamespacePrefix
+ * Get the namespace prefix for a given uri from the <namespaces> object.
+ *
+ * Returns:
+ * {String} A namespace prefix or null if none found.
+ */
+ getNamespacePrefix: function(uri) {
+ var prefix = null;
+ if(uri == null) {
+ prefix = this.namespaces[this.defaultPrefix];
+ } else {
+ var gotPrefix = false;
+ for(prefix in this.namespaces) {
+ if(this.namespaces[prefix] == uri) {
+ gotPrefix = true;
+ break;
+ }
+ }
+ if(!gotPrefix) {
+ prefix = null;
+ }
+ }
+ return prefix;
+ },
+
+
+ /**
+ * Method: readChildNodes
+ */
+ readChildNodes: function(node, obj) {
+ var children = node.childNodes;
+ var child, group, reader, prefix, local;
+ for(var i=0; i<children.length; ++i) {
+ child = children[i];
+ if(child.nodeType == 1) {
+ prefix = this.getNamespacePrefix(child.namespaceURI);
+ local = child.nodeName.split(":").pop();
+ group = this.readers[prefix];
+ if(group) {
+ reader = group[local];
+ if(reader) {
+ reader.apply(this, [child, obj]);
+ }
+ }
+ }
+ }
+ },
+
+ /**
+ * Method: writeNode
+ * Shorthand for applying one of the named writers and appending the
+ * results to a node. If a qualified name is not provided for the
+ * second argument (and a local name is used instead), the namespace
+ * of the parent node will be assumed.
+ *
+ * Parameters:
+ * parent - {DOMElement} Result will be appended to this node.
+ * name - {String} The name of a node to generate. If a qualified name
+ * (e.g. "pre:Name") is used, the namespace prefix is assumed to be
+ * in the <writers> group. If a local name is used (e.g. "Name") then
+ * the namespace of the parent is assumed.
+ * obj - {Object} Structure containing data for the writer.
+ *
+ * Returns:
+ * {DOMElement} The child node.
+ */
+ writeNode: function(parent, name, obj) {
+ var prefix, local;
+ var split = name.indexOf(":");
+ if(split > 0) {
+ prefix = name.substring(0, split);
+ local = name.substring(split + 1);
+ } else {
+ prefix = this.getNamespacePrefix(parent.namespaceURI);
+ local = name;
+ }
+ var child = this.writers[prefix][local].apply(this, [obj]);
+ parent.appendChild(child);
+ return child;
+ },
+
+ /**
+ * Method: createElementNSPlus
+ * Shorthand for creating namespaced elements with optional attributes and
+ * child text nodes.
+ *
+ * Parameters:
+ * name - {String} The qualified node name.
+ * options - {Object} Optional object for node configuration.
+ *
+ * Returns:
+ * {Element} An element node.
+ */
+ createElementNSPlus: function(name, options) {
+ options = options || {};
+ var loc = name.indexOf(":");
+ // order of prefix preference
+ // 1. in the uri option
+ // 2. in the prefix option
+ // 3. in the qualified name
+ // 4. from the defaultPrefix
+ var uri = options.uri || this.namespaces[options.prefix];
+ if(!uri) {
+ loc = name.indexOf(":");
+ uri = this.namespaces[name.substring(0, loc)];
+ }
+ if(!uri) {
+ uri = this.namespaces[this.defaultPrefix];
+ }
+ var node = this.createElementNS(uri, name);
+ if(options.attributes) {
+ this.setAttributes(node, options.attributes);
+ }
+ if(options.value) {
+ node.appendChild(this.createTextNode(options.value));
+ }
+ return node;
+ },
+
+ /**
+ * Method: setAttributes
+ * Set multiple attributes given key value pairs from an object.
+ *
+ * Parameters:
+ * node - {Element} An element node.
+ * obj - {Object || Array} An object whose properties represent attribute
+ * names and values represent attribute values. If an attribute name
+ * is a qualified name ("prefix:local"), the prefix will be looked up
+ * in the parsers {namespaces} object. If the prefix is found,
+ * setAttributeNS will be used instead of setAttribute.
+ */
+ setAttributes: function(node, obj) {
+ var value, loc, alias, uri;
+ for(var name in obj) {
+ value = obj[name].toString();
+ // check for qualified attribute name ("prefix:local")
+ uri = this.namespaces[name.substring(0, name.indexOf(":"))] || null;
+ this.setAttributeNS(node, uri, name, value);
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Format.Filter.v1"
+
+});
+/* ======================================================================
+ OpenLayers/Format/SLD/v1.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/Rule.js
+ * @requires OpenLayers/Filter.js
+ * @requires OpenLayers/Filter/FeatureId.js
+ * @requires OpenLayers/Filter/Logical.js
+ * @requires OpenLayers/Filter/Comparison.js
+ * @requires OpenLayers/Format/SLD.js
+ */
+
+/**
+ * Class: OpenLayers.Format.SLD.v1
+ * Superclass for SLD version 1 parsers.
+ *
+ * Inherits from:
+ * - <OpenLayers.Format.XML>
+ */
+OpenLayers.Format.SLD.v1 = OpenLayers.Class(OpenLayers.Format.XML, {
+
+ /**
+ * Property: namespaces
+ * {Object} Mapping of namespace aliases to namespace URIs.
+ */
+ namespaces: {
+ sld: "http://www.opengis.net/sld",
+ ogc: "http://www.opengis.net/ogc",
+ xlink: "http://www.w3.org/1999/xlink",
+ xsi: "http://www.w3.org/2001/XMLSchema-instance"
+ },
+
+ /**
+ * Property: defaultPrefix
+ */
+ defaultPrefix: "sld",
+
+ /**
+ * Property: schemaLocation
+ * {String} Schema location for a particular minor version.
+ */
+ schemaLocation: null,
+
+ /**
+ * APIProperty: defaultSymbolizer.
+ * {Object} A symbolizer with the SLD defaults.
+ */
+ defaultSymbolizer: {
+ fillColor: "#808080",
+ fillOpacity: 1,
+ strokeColor: "#000000",
+ strokeOpacity: 1,
+ strokeWidth: 1,
+ strokeDashstyle: "solid",
+ pointRadius: 3,
+ graphicName: "square"
+ },
+
+ /**
+ * Constructor: OpenLayers.Format.SLD.v1
+ * Instances of this class are not created directly. Use the
+ * <OpenLayers.Format.SLD> constructor instead.
+ *
+ * Parameters:
+ * options - {Object} An optional object whose properties will be set on
+ * this instance.
+ */
+ initialize: function(options) {
+ // extend with ogc:Filter readers and writers
+ this.readers["ogc"] = OpenLayers.Format.Filter.v1.prototype.readers["ogc"];
+ this.writers["ogc"] = OpenLayers.Format.Filter.v1.prototype.writers["ogc"];
+ // extend with custom filter methods that may get changed
+ this.readOgcExpression = OpenLayers.Format.Filter.v1.prototype.readOgcExpression;
+ this.getFilterType = OpenLayers.Format.Filter.v1.prototype.getFilterType;
+ this.filterMap = OpenLayers.Format.Filter.v1.prototype.filterMap;
+ OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
+ },
+
+ /**
+ * Method: read
+ *
+ * Parameters:
+ * data - {DOMElement} An SLD document element.
+ *
+ * Returns:
+ * {Object} An object representing the SLD.
+ */
+ read: function(data) {
+ var sld = {
+ namedLayers: {}
+ };
+ this.readChildNodes(data, sld);
+ return sld;
+ },
+
+ /**
+ * Property: readers
+ * Contains public functions, grouped by namespace prefix, that will
+ * be applied when a namespaced node is found matching the function
+ * name. The function will be applied in the scope of this parser
+ * with two arguments: the node being read and a context object passed
+ * from the parent.
+ */
+ readers: {
+ "sld": {
+ "StyledLayerDescriptor": function(node, sld) {
+ sld.version = node.getAttribute("version");
+ this.readChildNodes(node, sld);
+ },
+ "Name": function(node, obj) {
+ obj.name = this.getChildValue(node);
+ },
+ "Title": function(node, obj) {
+ obj.title = this.getChildValue(node);
+ },
+ "Abstract": function(node, obj) {
+ obj.description = this.getChildValue(node);
+ },
+ "NamedLayer": function(node, sld) {
+ var layer = {
+ userStyles: [],
+ namedStyles: []
+ };
+ this.readChildNodes(node, layer);
+ // give each of the user styles this layer name
+ for(var i=0, len=layer.userStyles.length; i<len; ++i) {
+ layer.userStyles[i].layerName = layer.name;
+ }
+ sld.namedLayers[layer.name] = layer;
+ },
+ "NamedStyle": function(node, layer) {
+ layer.namedStyles.push(
+ this.getChildName(node.firstChild)
+ );
+ },
+ "UserStyle": function(node, layer) {
+ var style = new OpenLayers.Style(this.defaultSymbolizer);
+ this.readChildNodes(node, style);
+ layer.userStyles.push(style);
+ },
+ "IsDefault": function(node, style) {
+ if(this.getChildValue(node) == "1") {
+ style.isDefault = true;
+ }
+ },
+ "FeatureTypeStyle": function(node, style) {
+ // OpenLayers doesn't have a place for FeatureTypeStyle
+ // Name, Title, Abstract, FeatureTypeName, or
+ // SemanticTypeIdentifier so, we make a temporary object
+ // and later just use the Rule(s).
+ var obj = {
+ rules: []
+ };
+ this.readChildNodes(node, obj);
+ style.rules = obj.rules;
+ },
+ "Rule": function(node, obj) {
+ var rule = new OpenLayers.Rule();
+ this.readChildNodes(node, rule);
+ obj.rules.push(rule);
+ },
+ "ElseFilter": function(node, rule) {
+ rule.elseFilter = true;
+ },
+ "MinScaleDenominator": function(node, rule) {
+ rule.minScaleDenominator = this.getChildValue(node);
+ },
+ "MaxScaleDenominator": function(node, rule) {
+ rule.maxScaleDenominator = this.getChildValue(node);
+ },
+ "LineSymbolizer": function(node, rule) {
+ // OpenLayers doens't do painter's order, instead we extend
+ var symbolizer = rule.symbolizer["Line"] || {};
+ this.readChildNodes(node, symbolizer);
+ // in case it didn't exist before
+ rule.symbolizer["Line"] = symbolizer;
+ },
+ "PolygonSymbolizer": function(node, rule) {
+ // OpenLayers doens't do painter's order, instead we extend
+ var symbolizer = rule.symbolizer["Polygon"] || {};
+ this.readChildNodes(node, symbolizer);
+ // in case it didn't exist before
+ rule.symbolizer["Polygon"] = symbolizer;
+ },
+ "PointSymbolizer": function(node, rule) {
+ // OpenLayers doens't do painter's order, instead we extend
+ var symbolizer = rule.symbolizer["Point"] || {};
+ this.readChildNodes(node, symbolizer);
+ // in case it didn't exist before
+ rule.symbolizer["Point"] = symbolizer;
+ },
+ "Stroke": function(node, symbolizer) {
+ this.readChildNodes(node, symbolizer);
+ },
+ "Fill": function(node, symbolizer) {
+ this.readChildNodes(node, symbolizer);
+ },
+ "CssParameter": function(node, symbolizer) {
+ var cssProperty = node.getAttribute("name");
+ var symProperty = this.cssMap[cssProperty];
+ if(symProperty) {
+ // Limited support for parsing of OGC expressions
+ var value = this.readOgcExpression(node);
+ // always string, could be an empty string
+ if(value) {
+ symbolizer[symProperty] = value;
+ }
+ }
+ },
+ "Graphic": function(node, symbolizer) {
+ var graphic = {};
+ // painter's order not respected here, clobber previous with next
+ this.readChildNodes(node, graphic);
+ // directly properties with names that match symbolizer properties
+ var properties = [
+ "strokeColor", "strokeWidth", "strokeOpacity",
+ "strokeLinecap", "fillColor", "fillOpacity",
+ "graphicName", "rotation", "graphicFormat"
+ ];
+ var prop, value;
+ for(var i=0, len=properties.length; i<len; ++i) {
+ prop = properties[i];
+ value = graphic[prop];
+ if(value != undefined) {
+ symbolizer[prop] = value;
+ }
+ }
+ // set other generic properties with specific graphic property names
+ if(graphic.opacity != undefined) {
+ symbolizer.graphicOpacity = graphic.opacity;
+ }
+ if(graphic.size != undefined) {
+ symbolizer.pointRadius = graphic.size / 2;
+ }
+ if(graphic.href != undefined) {
+ symbolizer.externalGraphic = graphic.href;
+ }
+ if(graphic.rotation != undefined) {
+ symbolizer.rotation = graphic.rotation;
+ }
+ },
+ "ExternalGraphic": function(node, graphic) {
+ this.readChildNodes(node, graphic);
+ },
+ "Mark": function(node, graphic) {
+ this.readChildNodes(node, graphic);
+ },
+ "WellKnownName": function(node, graphic) {
+ graphic.graphicName = this.getChildValue(node);
+ },
+ "Opacity": function(node, obj) {
+ // No support for parsing of OGC expressions
+ var opacity = this.getChildValue(node);
+ // always string, could be empty string
+ if(opacity) {
+ obj.opacity = opacity;
+ }
+ },
+ "Size": function(node, obj) {
+ // No support for parsing of OGC expressions
+ var size = this.getChildValue(node);
+ // always string, could be empty string
+ if(size) {
+ obj.size = size;
+ }
+ },
+ "Rotation": function(node, obj) {
+ // No support for parsing of OGC expressions
+ var rotation = this.getChildValue(node);
+ // always string, could be empty string
+ if(rotation) {
+ obj.rotation = rotation;
+ }
+ },
+ "OnlineResource": function(node, obj) {
+ obj.href = this.getAttributeNS(
+ node, this.namespaces.xlink, "href"
+ );
+ },
+ "Format": function(node, graphic) {
+ graphic.graphicFormat = this.getChildValue(node);
+ }
+ }
+ },
+
+ /**
+ * Property: cssMap
+ * {Object} Object mapping supported css property names to OpenLayers
+ * symbolizer property names.
+ */
+ cssMap: {
+ "stroke": "strokeColor",
+ "stroke-opacity": "strokeOpacity",
+ "stroke-width": "strokeWidth",
+ "stroke-linecap": "strokeLinecap",
+ "stroke-dasharray": "strokeDashstyle",
+ "fill": "fillColor",
+ "fill-opacity": "fillOpacity",
+ "font-family": "fontFamily",
+ "font-size": "fontSize"
+ },
+
+ /**
+ * Method: getCssProperty
+ * Given a symbolizer property, get the corresponding CSS property
+ * from the <cssMap>.
+ *
+ * Parameters:
+ * sym - {String} A symbolizer property name.
+ *
+ * Returns:
+ * {String} A CSS property name or null if none found.
+ */
+ getCssProperty: function(sym) {
+ var css = null;
+ for(var prop in this.cssMap) {
+ if(this.cssMap[prop] == sym) {
+ css = prop;
+ break;
+ }
+ }
+ return css;
+ },
+
+ /**
+ * Method: getGraphicFormat
+ * Given a href for an external graphic, try to determine the mime-type.
+ * This method doesn't try too hard, and will fall back to
+ * <defautlGraphicFormat> if one of the known <graphicFormats> is not
+ * the file extension of the provided href.
+ *
+ * Parameters:
+ * href - {String}
+ *
+ * Returns:
+ * {String} The graphic format.
+ */
+ getGraphicFormat: function(href) {
+ var format, regex;
+ for(var key in this.graphicFormats) {
+ if(this.graphicFormats[key].test(href)) {
+ format = key;
+ break;
+ }
+ }
+ return format || this.defautlGraphicFormat;
+ },
+
+ /**
+ * Property: defaultGraphicFormat
+ * {String} If none other can be determined from <getGraphicFormat>, this
+ * default will be returned.
+ */
+ defaultGraphicFormat: "image/png",
+
+ /**
+ * Property: graphicFormats
+ * {Object} Mapping of image mime-types to regular extensions matching
+ * well-known file extensions.
+ */
+ graphicFormats: {
+ "image/jpeg": /\.jpe?g$/i,
+ "image/gif": /\.gif$/i,
+ "image/png": /\.png$/i
+ },
+
+ /**
+ * Method: write
+ *
+ * Parameters:
+ * sld - {Object} An object representing the SLD.
+ *
+ * Returns:
+ * {DOMElement} The root of an SLD document.
+ */
+ write: function(sld) {
+ return this.writers.sld.StyledLayerDescriptor.apply(this, [sld]);
+ },
+
+ /**
+ * Property: writers
+ * As a compliment to the readers property, this structure contains public
+ * writing functions grouped by namespace alias and named like the
+ * node names they produce.
+ */
+ writers: {
+ "sld": {
+ "StyledLayerDescriptor": function(sld) {
+ var root = this.createElementNSPlus(
+ "StyledLayerDescriptor",
+ {attributes: {
+ "version": this.VERSION,
+ "xsi:schemaLocation": this.schemaLocation
+ }}
+ );
+ // add in optional name
+ if(sld.name) {
+ this.writeNode(root, "Name", sld.name);
+ }
+ // add in optional title
+ if(sld.title) {
+ this.writeNode(root, "Title", sld.title);
+ }
+ // add in optional description
+ if(sld.description) {
+ this.writeNode(root, "Abstract", sld.description);
+ }
+ // add in named layers
+ for(var name in sld.namedLayers) {
+ this.writeNode(root, "NamedLayer", sld.namedLayers[name]);
+ }
+ return root;
+ },
+ "Name": function(name) {
+ return this.createElementNSPlus("Name", {value: name});
+ },
+ "Title": function(title) {
+ return this.createElementNSPlus("Title", {value: title});
+ },
+ "Abstract": function(description) {
+ return this.createElementNSPlus(
+ "Abstract", {value: description}
+ );
+ },
+ "NamedLayer": function(layer) {
+ var node = this.createElementNSPlus("NamedLayer");
+
+ // add in required name
+ this.writeNode(node, "Name", layer.name);
+
+ // optional sld:LayerFeatureConstraints here
+
+ // add in named styles
+ if(layer.namedStyles) {
+ for(var i=0, len=layer.namedStyles.length; i<len; ++i) {
+ this.writeNode(
+ node, "NamedStyle", layer.namedStyles[i]
+ );
+ }
+ }
+
+ // add in user styles
+ if(layer.userStyles) {
+ for(var i=0, len=layer.userStyles.length; i<len; ++i) {
+ this.writeNode(
+ node, "UserStyle", layer.userStyles[i]
+ );
+ }
+ }
+
+ return node;
+ },
+ "NamedStyle": function(name) {
+ var node = this.createElementNSPlus("NamedStyle");
+ this.writeNode(node, "Name", name);
+ return node;
+ },
+ "UserStyle": function(style) {
+ var node = this.createElementNSPlus("UserStyle");
+
+ // add in optional name
+ if(style.name) {
+ this.writeNode(node, "Name", style.name);
+ }
+ // add in optional title
+ if(style.title) {
+ this.writeNode(node, "Title", style.title);
+ }
+ // add in optional description
+ if(style.description) {
+ this.writeNode(node, "Abstract", style.description);
+ }
+
+ // add isdefault
+ if(style.isDefault) {
+ this.writeNode(node, "IsDefault", style.isDefault);
+ }
+
+ // add FeatureTypeStyles
+ this.writeNode(node, "FeatureTypeStyle", style);
+
+ return node;
+ },
+ "IsDefault": function(bool) {
+ return this.createElementNSPlus(
+ "IsDefault", {value: (bool) ? "1" : "0"}
+ );
+ },
+ "FeatureTypeStyle": function(style) {
+ var node = this.createElementNSPlus("FeatureTypeStyle");
+
+ // OpenLayers currently stores no Name, Title, Abstract,
+ // FeatureTypeName, or SemanticTypeIdentifier information
+ // related to FeatureTypeStyle
+
+ // add in rules
+ for(var i=0, len=style.rules.length; i<len; ++i) {
+ this.writeNode(node, "Rule", style.rules[i]);
+ }
+
+ return node;
+ },
+ "Rule": function(rule) {
+ var node = this.createElementNSPlus("Rule");
+
+ // add in optional name
+ if(rule.name) {
+ this.writeNode(node, "Name", rule.name);
+ }
+ // add in optional title
+ if(rule.title) {
+ this.writeNode(node, "Title", rule.title);
+ }
+ // add in optional description
+ if(rule.description) {
+ this.writeNode(node, "Abstract", rule.description);
+ }
+
+ // add in LegendGraphic here
+
+ // add in optional filters
+ if(rule.elseFilter) {
+ this.writeNode(node, "ElseFilter");
+ } else if(rule.filter) {
+ this.writeNode(node, "ogc:Filter", rule.filter);
+ }
+
+ // add in scale limits
+ if(rule.minScaleDenominator != undefined) {
+ this.writeNode(
+ node, "MinScaleDenominator", rule.minScaleDenominator
+ );
+ }
+ if(rule.maxScaleDenominator != undefined) {
+ this.writeNode(
+ node, "MaxScaleDenominator", rule.maxScaleDenominator
+ );
+ }
+
+ // add in symbolizers (relies on geometry type keys)
+ var types = OpenLayers.Style.SYMBOLIZER_PREFIXES;
+ var type, symbolizer;
+ for(var i=0, len=types.length; i<len; ++i) {
+ type = types[i];
+ symbolizer = rule.symbolizer[type];
+ if(symbolizer) {
+ this.writeNode(
+ node, type + "Symbolizer", symbolizer
+ );
+ }
+ }
+ return node;
+
+ },
+ "ElseFilter": function() {
+ return this.createElementNSPlus("ElseFilter");
+ },
+ "MinScaleDenominator": function(scale) {
+ return this.createElementNSPlus(
+ "MinScaleDenominator", {value: scale}
+ );
+ },
+ "MaxScaleDenominator": function(scale) {
+ return this.createElementNSPlus(
+ "MaxScaleDenominator", {value: scale}
+ );
+ },
+ "LineSymbolizer": function(symbolizer) {
+ var node = this.createElementNSPlus("LineSymbolizer");
+ this.writeNode(node, "Stroke", symbolizer);
+ return node;
+ },
+ "Stroke": function(symbolizer) {
+ var node = this.createElementNSPlus("Stroke");
+
+ // GraphicFill here
+ // GraphicStroke here
+
+ // add in CssParameters
+ if(symbolizer.strokeColor != undefined) {
+ this.writeNode(
+ node, "CssParameter",
+ {symbolizer: symbolizer, key: "strokeColor"}
+ );
+ }
+ if(symbolizer.strokeOpacity != undefined) {
+ this.writeNode(
+ node, "CssParameter",
+ {symbolizer: symbolizer, key: "strokeOpacity"}
+ );
+ }
+ if(symbolizer.strokeWidth != undefined) {
+ this.writeNode(
+ node, "CssParameter",
+ {symbolizer: symbolizer, key: "strokeWidth"}
+ );
+ }
+ return node;
+ },
+ "CssParameter": function(obj) {
+ // not handling ogc:expressions for now
+ return this.createElementNSPlus("CssParameter", {
+ attributes: {name: this.getCssProperty(obj.key)},
+ value: obj.symbolizer[obj.key]
+ });
+ },
+ "TextSymbolizer": function(symbolizer) {
+ var node = this.createElementNSPlus("TextSymbolizer");
+ // add in optional Label
+ if(symbolizer.label != null) {
+ this.writeNode(node, "Label", symbolizer.label);
+ }
+ // add in optional Font
+ if(symbolizer.fontFamily != null ||
+ symbolizer.fontSize != null) {
+ this.writeNode(node, "Font", symbolizer);
+ }
+ // add in optional Fill
+ if(symbolizer.fillColor != null ||
+ symbolizer.fillOpacity != null) {
+ this.writeNode(node, "Fill", symbolizer);
+ }
+ return node;
+ },
+ "Font": function(symbolizer) {
+ var node = this.createElementNSPlus("Font");
+ // add in CssParameters
+ if(symbolizer.fontFamily) {
+ this.writeNode(
+ node, "CssParameter",
+ {symbolizer: symbolizer, key: "fontFamily"}
+ );
+ }
+ if(symbolizer.fontSize) {
+ this.writeNode(
+ node, "CssParameter",
+ {symbolizer: symbolizer, key: "fontSize"}
+ );
+ }
+ return node;
+ },
+ "Label": function(label) {
+ // only the simplest of ogc:expression handled
+ // {label: "some text and a ${propertyName}"}
+ var node = this.createElementNSPlus("Label");
+ var tokens = label.split("${");
+ node.appendChild(this.createTextNode(tokens[0]));
+ var item, last;
+ for(var i=1, len=tokens.length; i<len; i++) {
+ item = tokens[i];
+ last = item.indexOf("}");
+ if(last > 0) {
+ this.writeNode(
+ node, "ogc:PropertyName",
+ {property: item.substring(0, last)}
+ );
+ node.appendChild(
+ this.createTextNode(item.substring(++last))
+ );
+ } else {
+ // no ending }, so this is a literal ${
+ node.appendChild(
+ this.createTextNode("${" + item)
+ );
+ }
+ }
+ return node;
+ },
+ "PolygonSymbolizer": function(symbolizer) {
+ var node = this.createElementNSPlus("PolygonSymbolizer");
+ this.writeNode(node, "Fill", symbolizer);
+ this.writeNode(node, "Stroke", symbolizer);
+ return node;
+ },
+ "Fill": function(symbolizer) {
+ var node = this.createElementNSPlus("Fill");
+
+ // GraphicFill here
+
+ // add in CssParameters
+ if(symbolizer.fillColor) {
+ this.writeNode(
+ node, "CssParameter",
+ {symbolizer: symbolizer, key: "fillColor"}
+ );
+ }
+ if(symbolizer.fillOpacity) {
+ this.writeNode(
+ node, "CssParameter",
+ {symbolizer: symbolizer, key: "fillOpacity"}
+ );
+ }
+ return node;
+ },
+ "PointSymbolizer": function(symbolizer) {
+ var node = this.createElementNSPlus("PointSymbolizer");
+ this.writeNode(node, "Graphic", symbolizer);
+ return node;
+ },
+ "Graphic": function(symbolizer) {
+ var node = this.createElementNSPlus("Graphic");
+ if(symbolizer.externalGraphic != undefined) {
+ this.writeNode(node, "ExternalGraphic", symbolizer);
+ } else if(symbolizer.graphicName) {
+ this.writeNode(node, "Mark", symbolizer);
+ }
+
+ if(symbolizer.graphicOpacity != undefined) {
+ this.writeNode(node, "Opacity", symbolizer.graphicOpacity);
+ }
+ if(symbolizer.pointRadius != undefined) {
+ this.writeNode(node, "Size", symbolizer.pointRadius * 2);
+ }
+ if(symbolizer.rotation != undefined) {
+ this.writeNode(node, "Rotation", symbolizer.rotation);
+ }
+ return node;
+ },
+ "ExternalGraphic": function(symbolizer) {
+ var node = this.createElementNSPlus("ExternalGraphic");
+ this.writeNode(
+ node, "OnlineResource", symbolizer.externalGraphic
+ );
+ var format = symbolizer.graphicFormat ||
+ this.getGraphicFormat(symbolizer.externalGraphic);
+ this.writeNode(node, "Format", format);
+ return node;
+ },
+ "Mark": function(symbolizer) {
+ var node = this.createElementNSPlus("Mark");
+ this.writeNode(node, "WellKnownName", symbolizer.graphicName);
+ this.writeNode(node, "Fill", symbolizer);
+ this.writeNode(node, "Stroke", symbolizer);
+ return node;
+ },
+ "WellKnownName": function(name) {
+ return this.createElementNSPlus("WellKnownName", {
+ value: name
+ });
+ },
+ "Opacity": function(value) {
+ return this.createElementNSPlus("Opacity", {
+ value: value
+ });
+ },
+ "Size": function(value) {
+ return this.createElementNSPlus("Size", {
+ value: value
+ });
+ },
+ "Rotation": function(value) {
+ return this.createElementNSPlus("Rotation", {
+ value: value
+ });
+ },
+ "OnlineResource": function(href) {
+ return this.createElementNSPlus("OnlineResource", {
+ attributes: {
+ "xlink:type": "simple",
+ "xlink:href": href
+ }
+ });
+ },
+ "Format": function(format) {
+ return this.createElementNSPlus("Format", {
+ value: format
+ });
+ }
+ }
+ },
+
+ /**
+ * Methods below this point are of general use for versioned XML parsers.
+ * These are candidates for an abstract class.
+ */
+
+ /**
+ * Method: getNamespacePrefix
+ * Get the namespace prefix for a given uri from the <namespaces> object.
+ *
+ * Returns:
+ * {String} A namespace prefix or null if none found.
+ */
+ getNamespacePrefix: function(uri) {
+ var prefix = null;
+ if(uri == null) {
+ prefix = this.namespaces[this.defaultPrefix];
+ } else {
+ var gotPrefix = false;
+ for(prefix in this.namespaces) {
+ if(this.namespaces[prefix] == uri) {
+ gotPrefix = true;
+ break;
+ }
+ }
+ if(!gotPrefix) {
+ prefix = null;
+ }
+ }
+ return prefix;
+ },
+
+
+ /**
+ * Method: readChildNodes
+ */
+ readChildNodes: function(node, obj) {
+ var children = node.childNodes;
+ var child, group, reader, prefix, local;
+ for(var i=0, len=children.length; i<len; ++i) {
+ child = children[i];
+ if(child.nodeType == 1) {
+ prefix = this.getNamespacePrefix(child.namespaceURI);
+ local = child.nodeName.split(":").pop();
+ group = this.readers[prefix];
+ if(group) {
+ reader = group[local];
+ if(reader) {
+ reader.apply(this, [child, obj]);
+ }
+ }
+ }
+ }
+ },
+
+ /**
+ * Method: writeNode
+ * Shorthand for applying one of the named writers and appending the
+ * results to a node. If a qualified name is not provided for the
+ * second argument (and a local name is used instead), the namespace
+ * of the parent node will be assumed.
+ *
+ * Parameters:
+ * parent - {DOMElement} Result will be appended to this node.
+ * name - {String} The name of a node to generate. If a qualified name
+ * (e.g. "pre:Name") is used, the namespace prefix is assumed to be
+ * in the <writers> group. If a local name is used (e.g. "Name") then
+ * the namespace of the parent is assumed.
+ * obj - {Object} Structure containing data for the writer.
+ *
+ * Returns:
+ * {DOMElement} The child node.
+ */
+ writeNode: function(parent, name, obj) {
+ var prefix, local;
+ var split = name.indexOf(":");
+ if(split > 0) {
+ prefix = name.substring(0, split);
+ local = name.substring(split + 1);
+ } else {
+ prefix = this.getNamespacePrefix(parent.namespaceURI);
+ local = name;
+ }
+ var child = this.writers[prefix][local].apply(this, [obj]);
+ parent.appendChild(child);
+ return child;
+ },
+
+ /**
+ * Method: createElementNSPlus
+ * Shorthand for creating namespaced elements with optional attributes and
+ * child text nodes.
+ *
+ * Parameters:
+ * name - {String} The qualified node name.
+ * options - {Object} Optional object for node configuration.
+ *
+ * Returns:
+ * {Element} An element node.
+ */
+ createElementNSPlus: function(name, options) {
+ options = options || {};
+ var loc = name.indexOf(":");
+ // order of prefix preference
+ // 1. in the uri option
+ // 2. in the prefix option
+ // 3. in the qualified name
+ // 4. from the defaultPrefix
+ var uri = options.uri || this.namespaces[options.prefix];
+ if(!uri) {
+ loc = name.indexOf(":");
+ uri = this.namespaces[name.substring(0, loc)];
+ }
+ if(!uri) {
+ uri = this.namespaces[this.defaultPrefix];
+ }
+ var node = this.createElementNS(uri, name);
+ if(options.attributes) {
+ this.setAttributes(node, options.attributes);
+ }
+ if(options.value) {
+ node.appendChild(this.createTextNode(options.value));
+ }
+ return node;
+ },
+
+ /**
+ * Method: setAttributes
+ * Set multiple attributes given key value pairs from an object.
+ *
+ * Parameters:
+ * node - {Element} An element node.
+ * obj - {Object || Array} An object whose properties represent attribute
+ * names and values represent attribute values. If an attribute name
+ * is a qualified name ("prefix:local"), the prefix will be looked up
+ * in the parsers {namespaces} object. If the prefix is found,
+ * setAttributeNS will be used instead of setAttribute.
+ */
+ setAttributes: function(node, obj) {
+ var value, loc, alias, uri;
+ for(var name in obj) {
+ value = obj[name].toString();
+ // check for qualified attribute name ("prefix:local")
+ uri = this.namespaces[name.substring(0, name.indexOf(":"))] || null;
+ this.setAttributeNS(node, uri, name, value);
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Format.SLD.v1"
+
+});
+/* ======================================================================
+ OpenLayers/Geometry/Curve.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/MultiPoint.js
+ */
+
+/**
+ * Class: OpenLayers.Geometry.Curve
+ * A Curve is a MultiPoint, whose points are assumed to be connected. To
+ * this end, we provide a "getLength()" function, which iterates through
+ * the points, summing the distances between them.
+ *
+ * Inherits:
+ * - <OpenLayers.Geometry.MultiPoint>
+ */
+OpenLayers.Geometry.Curve = OpenLayers.Class(OpenLayers.Geometry.MultiPoint, {
+
+ /**
+ * 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.Curve
+ *
+ * Parameters:
+ * point - {Array(<OpenLayers.Geometry.Point>)}
+ */
+ initialize: function(points) {
+ OpenLayers.Geometry.MultiPoint.prototype.initialize.apply(this,
+ arguments);
+ },
+
+ /**
+ * APIMethod: getLength
+ *
+ * Returns:
+ * {Float} The length of the curve
+ */
+ getLength: function() {
+ var length = 0.0;
+ if ( this.components && (this.components.length > 1)) {
+ for(var i=1, len=this.components.length; i<len; i++) {
+ length += this.components[i-1].distanceTo(this.components[i]);
+ }
+ }
+ return length;
+ },
+
+ CLASS_NAME: "OpenLayers.Geometry.Curve"
+});
+/* ======================================================================
+ OpenLayers/Format/Filter/v1_0_0.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/Format/Filter/v1.js
+ */
+
+/**
+ * Class: OpenLayers.Format.Filter.v1_0_0
+ * Write ogc:Filter version 1.0.0.
+ *
+ * Inherits from:
+ * - <OpenLayers.Format.Filter.v1>
+ */
+OpenLayers.Format.Filter.v1_0_0 = OpenLayers.Class(
+ OpenLayers.Format.Filter.v1, {
+
+ /**
+ * Constant: VERSION
+ * {String} 1.0.0
+ */
+ VERSION: "1.0.0",
+
+ /**
+ * Property: schemaLocation
+ * {String} http://www.opengis.net/ogc/filter/1.0.0/filter.xsd
+ */
+ schemaLocation: "http://www.opengis.net/ogc/filter/1.0.0/filter.xsd",
+
+ /**
+ * Constructor: OpenLayers.Format.Filter.v1_0_0
+ * Instances of this class are not created directly. Use the
+ * <OpenLayers.Format.Filter> constructor instead.
+ *
+ * Parameters:
+ * options - {Object} An optional object whose properties will be set on
+ * this instance.
+ */
+ initialize: function(options) {
+ OpenLayers.Format.Filter.v1.prototype.initialize.apply(
+ this, [options]
+ );
+ },
+
+ CLASS_NAME: "OpenLayers.Format.Filter.v1_0_0"
+
+});
+/* ======================================================================
+ OpenLayers/Format/SLD/v1_0_0.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/Format/SLD/v1.js
+ */
+
+/**
+ * Class: OpenLayers.Format.SLD.v1_0_0
+ * Write SLD version 1.0.0.
+ *
+ * Inherits from:
+ * - <OpenLayers.Format.SLD.v1>
+ */
+OpenLayers.Format.SLD.v1_0_0 = OpenLayers.Class(
+ OpenLayers.Format.SLD.v1, {
+
+ /**
+ * Constant: VERSION
+ * {String} 1.0.0
+ */
+ VERSION: "1.0.0",
+
+ /**
+ * Property: schemaLocation
+ * {String} http://www.opengis.net/sld
+ * http://schemas.opengis.net/sld/1.0.0/StyledLayerDescriptor.xsd
+ */
+ schemaLocation: "http://www.opengis.net/sld http://schemas.opengis.net/sld/1.0.0/StyledLayerDescriptor.xsd",
+
+ /**
+ * Constructor: OpenLayers.Format.SLD.v1_0_0
+ * Instances of this class are not created directly. Use the
+ * <OpenLayers.Format.SLD> constructor instead.
+ *
+ * Parameters:
+ * options - {Object} An optional object whose properties will be set on
+ * this instance.
+ */
+ initialize: function(options) {
+ OpenLayers.Format.SLD.v1.prototype.initialize.apply(
+ this, [options]
+ );
+ },
+
+ CLASS_NAME: "OpenLayers.Format.SLD.v1_0_0"
+
+});
+/* ======================================================================
+ OpenLayers/Geometry/LineString.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/Curve.js
+ */
+
+/**
+ * Class: OpenLayers.Geometry.LineString
+ * A LineString is a Curve which, once two points have been added to it, can
+ * never be less than two points long.
+ *
+ * Inherits from:
+ * - <OpenLayers.Geometry.Curve>
+ */
+OpenLayers.Geometry.LineString = OpenLayers.Class(OpenLayers.Geometry.Curve, {
+
+ /**
+ * Constructor: OpenLayers.Geometry.LineString
+ * Create a new LineString geometry
+ *
+ * Parameters:
+ * points - {Array(<OpenLayers.Geometry.Point>)} An array of points used to
+ * generate the linestring
+ *
+ */
+ initialize: function(points) {
+ OpenLayers.Geometry.Curve.prototype.initialize.apply(this, arguments);
+ },
+
+ /**
+ * APIMethod: removeComponent
+ * Only allows removal of a point if there are three or more points in
+ * the linestring. (otherwise the result would be just a single point)
+ *
+ * Parameters:
+ * point - {<OpenLayers.Geometry.Point>} The point to be removed
+ */
+ removeComponent: function(point) {
+ if ( this.components && (this.components.length > 2)) {
+ OpenLayers.Geometry.Collection.prototype.removeComponent.apply(this,
+ arguments);
+ }
+ },
+
+ /**
+ * APIMethod: intersects
+ * Test for instersection between two geometries. This is a cheapo
+ * implementation of the Bently-Ottmann algorigithm. It doesn't
+ * really keep track of a sweep line data structure. It is closer
+ * to the brute force method, except that segments are sorted and
+ * potential intersections are only calculated when bounding boxes
+ * intersect.
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry>}
+ *
+ * Returns:
+ * {Boolean} The input geometry intersects this geometry.
+ */
+ intersects: function(geometry) {
+ var intersect = false;
+ var type = geometry.CLASS_NAME;
+ if(type == "OpenLayers.Geometry.LineString" ||
+ type == "OpenLayers.Geometry.LinearRing" ||
+ type == "OpenLayers.Geometry.Point") {
+ var segs1 = this.getSortedSegments();
+ var segs2;
+ if(type == "OpenLayers.Geometry.Point") {
+ segs2 = [{
+ x1: geometry.x, y1: geometry.y,
+ x2: geometry.x, y2: geometry.y
+ }];
+ } else {
+ segs2 = geometry.getSortedSegments();
+ }
+ var seg1, seg1x1, seg1x2, seg1y1, seg1y2,
+ seg2, seg2y1, seg2y2;
+ // sweep right
+ outer: for(var i=0, len=segs1.length; i<len; ++i) {
+ seg1 = segs1[i];
+ seg1x1 = seg1.x1;
+ seg1x2 = seg1.x2;
+ seg1y1 = seg1.y1;
+ seg1y2 = seg1.y2;
+ inner: for(var j=0, jlen=segs2.length; j<jlen; ++j) {
+ seg2 = segs2[j];
+ if(seg2.x1 > seg1x2) {
+ // seg1 still left of seg2
+ break;
+ }
+ if(seg2.x2 < seg1x1) {
+ // seg2 still left of seg1
+ continue;
+ }
+ seg2y1 = seg2.y1;
+ seg2y2 = seg2.y2;
+ if(Math.min(seg2y1, seg2y2) > Math.max(seg1y1, seg1y2)) {
+ // seg2 above seg1
+ continue;
+ }
+ if(Math.max(seg2y1, seg2y2) < Math.min(seg1y1, seg1y2)) {
+ // seg2 below seg1
+ continue;
+ }
+ if(OpenLayers.Geometry.segmentsIntersect(seg1, seg2)) {
+ intersect = true;
+ break outer;
+ }
+ }
+ }
+ } else {
+ intersect = geometry.intersects(this);
+ }
+ return intersect;
+ },
+
+ /**
+ * Method: getSortedSegments
+ *
+ * Returns:
+ * {Array} An array of segment objects. Segment objects have properties
+ * x1, y1, x2, and y2. The start point is represented by x1 and y1.
+ * The end point is represented by x2 and y2. Start and end are
+ * ordered so that x1 < x2.
+ */
+ getSortedSegments: function() {
+ var numSeg = this.components.length - 1;
+ var segments = new Array(numSeg);
+ for(var i=0; i<numSeg; ++i) {
+ point1 = this.components[i];
+ point2 = this.components[i + 1];
+ if(point1.x < point2.x) {
+ segments[i] = {
+ x1: point1.x,
+ y1: point1.y,
+ x2: point2.x,
+ y2: point2.y
+ };
+ } else {
+ segments[i] = {
+ x1: point2.x,
+ y1: point2.y,
+ x2: point1.x,
+ y2: point1.y
+ };
+ }
+ }
+ // more efficient to define this somewhere static
+ function byX1(seg1, seg2) {
+ return seg1.x1 - seg2.x1;
+ }
+ return segments.sort(byX1);
+ },
+
+ CLASS_NAME: "OpenLayers.Geometry.LineString"
+});
+/* ======================================================================
+ OpenLayers/Format/GML.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/Format/XML.js
+ * @requires OpenLayers/Feature/Vector.js
+ * @requires OpenLayers/Geometry/Point.js
+ * @requires OpenLayers/Geometry/MultiPoint.js
+ * @requires OpenLayers/Geometry/LineString.js
+ * @requires OpenLayers/Geometry/MultiLineString.js
+ * @requires OpenLayers/Geometry/Polygon.js
+ * @requires OpenLayers/Geometry/MultiPolygon.js
+ */
+
+/**
+ * Class: OpenLayers.Format.GML
+ * Read/Wite GML. Create a new instance with the <OpenLayers.Format.GML>
+ * constructor. Supports the GML simple features profile.
+ *
+ * Inherits from:
+ * - <OpenLayers.Format>
+ */
+OpenLayers.Format.GML = OpenLayers.Class(OpenLayers.Format.XML, {
+
+ /*
+ * APIProperty: featureNS
+ * {String} Namespace used for feature attributes. Default is
+ * "http://mapserver.gis.umn.edu/mapserver".
+ */
+ featureNS: "http://mapserver.gis.umn.edu/mapserver",
+
+ /**
+ * APIProperty: featurePrefix
+ * {String} Namespace alias (or prefix) for feature nodes. Default is
+ * "feature".
+ */
+ featurePrefix: "feature",
+
+ /*
+ * APIProperty: featureName
+ * {String} Element name for features. Default is "featureMember".
+ */
+ featureName: "featureMember",
+
+ /*
+ * APIProperty: layerName
+ * {String} Name of data layer. Default is "features".
+ */
+ layerName: "features",
+
+ /**
+ * APIProperty: geometryName
+ * {String} Name of geometry element. Defaults to "geometry".
+ */
+ geometryName: "geometry",
+
+ /**
+ * APIProperty: collectionName
+ * {String} Name of featureCollection element.
+ */
+ collectionName: "FeatureCollection",
+
+ /**
+ * APIProperty: gmlns
+ * {String} GML Namespace.
+ */
+ gmlns: "http://www.opengis.net/gml",
+
+ /**
+ * APIProperty: extractAttributes
+ * {Boolean} Extract attributes from GML.
+ */
+ extractAttributes: true,
+
+ /**
+ * APIProperty: xy
+ * {Boolean} Order of the GML coordinate true:(x,y) or false:(y,x)
+ * Changing is not recommended, a new Format should be instantiated.
+ */
+ xy: true,
+
+ /**
+ * Constructor: OpenLayers.Format.GML
+ * Create a new parser for GML.
+ *
+ * Parameters:
+ * options - {Object} An optional object whose properties will be set on
+ * this instance.
+ */
+ initialize: function(options) {
+ // compile regular expressions once instead of every time they are used
+ this.regExes = {
+ trimSpace: (/^\s*|\s*$/g),
+ removeSpace: (/\s*/g),
+ splitSpace: (/\s+/),
+ trimComma: (/\s*,\s*/g)
+ };
+ OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
+ },
+
+ /**
+ * APIMethod: read
+ * Read data from a string, and return a list of features.
+ *
+ * Parameters:
+ * data - {String} or {DOMElement} data to read/parse.
+ *
+ * Returns:
+ * {Array(<OpenLayers.Feature.Vector>)} An array of features.
+ */
+ read: function(data) {
+ if(typeof data == "string") {
+ data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
+ }
+ var featureNodes = this.getElementsByTagNameNS(data.documentElement,
+ this.gmlns,
+ this.featureName);
+ var features = [];
+ for(var i=0; i<featureNodes.length; i++) {
+ var feature = this.parseFeature(featureNodes[i]);
+ if(feature) {
+ features.push(feature);
+ }
+ }
+ return features;
+ },
+
+ /**
+ * Method: parseFeature
+ * This function is the core of the GML parsing code in OpenLayers.
+ * It creates the geometries that are then attached to the returned
+ * feature, and calls parseAttributes() to get attribute data out.
+ *
+ * Parameters:
+ * node - {DOMElement} A GML feature node.
+ */
+ parseFeature: function(node) {
+ // only accept on geometry per feature - look for highest "order"
+ var order = ["MultiPolygon", "Polygon",
+ "MultiLineString", "LineString",
+ "MultiPoint", "Point", "Envelope"];
+ var type, nodeList, geometry, parser;
+ for(var i=0; i<order.length; ++i) {
+ type = order[i];
+ nodeList = this.getElementsByTagNameNS(node, this.gmlns, type);
+ if(nodeList.length > 0) {
+ // only deal with first geometry of this type
+ var parser = this.parseGeometry[type.toLowerCase()];
+ if(parser) {
+ geometry = parser.apply(this, [nodeList[0]]);
+ if (this.internalProjection && this.externalProjection) {
+ geometry.transform(this.externalProjection,
+ this.internalProjection);
+ }
+ } else {
+ OpenLayers.Console.error(OpenLayers.i18n(
+ "unsupportedGeometryType", {'geomType':type}));
+ }
+ // stop looking for different geometry types
+ break;
+ }
+ }
+
+ // construct feature (optionally with attributes)
+ var attributes;
+ if(this.extractAttributes) {
+ attributes = this.parseAttributes(node);
+ }
+ var feature = new OpenLayers.Feature.Vector(geometry, attributes);
+
+ feature.gml = {
+ featureType: node.firstChild.nodeName.split(":")[1],
+ featureNS: node.firstChild.namespaceURI,
+ featureNSPrefix: node.firstChild.prefix
+ };
+
+ // assign fid - this can come from a "fid" or "id" attribute
+ var childNode = node.firstChild;
+ var fid;
+ while(childNode) {
+ if(childNode.nodeType == 1) {
+ fid = childNode.getAttribute("fid") ||
+ childNode.getAttribute("id");
+ if(fid) {
+ break;
+ }
+ }
+ childNode = childNode.nextSibling;
+ }
+ feature.fid = fid;
+ return feature;
+ },
+
+ /**
+ * Property: parseGeometry
+ * Properties of this object are the functions that parse geometries based
+ * on their type.
+ */
+ parseGeometry: {
+
+ /**
+ * Method: parseGeometry.point
+ * Given a GML node representing a point geometry, create an OpenLayers
+ * point geometry.
+ *
+ * Parameters:
+ * node - {DOMElement} A GML node.
+ *
+ * Returns:
+ * {<OpenLayers.Geometry.Point>} A point geometry.
+ */
+ point: function(node) {
+ /**
+ * Three coordinate variations to consider:
+ * 1) <gml:pos>x y z</gml:pos>
+ * 2) <gml:coordinates>x, y, z</gml:coordinates>
+ * 3) <gml:coord><gml:X>x</gml:X><gml:Y>y</gml:Y></gml:coord>
+ */
+ var nodeList, coordString;
+ var coords = [];
+
+ // look for <gml:pos>
+ var nodeList = this.getElementsByTagNameNS(node, this.gmlns, "pos");
+ if(nodeList.length > 0) {
+ coordString = nodeList[0].firstChild.nodeValue;
+ coordString = coordString.replace(this.regExes.trimSpace, "");
+ coords = coordString.split(this.regExes.splitSpace);
+ }
+
+ // look for <gml:coordinates>
+ if(coords.length == 0) {
+ nodeList = this.getElementsByTagNameNS(node, this.gmlns,
+ "coordinates");
+ if(nodeList.length > 0) {
+ coordString = nodeList[0].firstChild.nodeValue;
+ coordString = coordString.replace(this.regExes.removeSpace,
+ "");
+ coords = coordString.split(",");
+ }
+ }
+
+ // look for <gml:coord>
+ if(coords.length == 0) {
+ nodeList = this.getElementsByTagNameNS(node, this.gmlns,
+ "coord");
+ if(nodeList.length > 0) {
+ var xList = this.getElementsByTagNameNS(nodeList[0],
+ this.gmlns, "X");
+ var yList = this.getElementsByTagNameNS(nodeList[0],
+ this.gmlns, "Y");
+ if(xList.length > 0 && yList.length > 0) {
+ coords = [xList[0].firstChild.nodeValue,
+ yList[0].firstChild.nodeValue];
+ }
+ }
+ }
+
+ // preserve third dimension
+ if(coords.length == 2) {
+ coords[2] = null;
+ }
+
+ if (this.xy) {
+ return new OpenLayers.Geometry.Point(coords[0], coords[1],
+ coords[2]);
+ }
+ else{
+ return new OpenLayers.Geometry.Point(coords[1], coords[0],
+ coords[2]);
+ }
+ },
+
+ /**
+ * Method: parseGeometry.multipoint
+ * Given a GML node representing a multipoint geometry, create an
+ * OpenLayers multipoint geometry.
+ *
+ * Parameters:
+ * node - {DOMElement} A GML node.
+ *
+ * Returns:
+ * {<OpenLayers.Geometry.MultiPoint>} A multipoint geometry.
+ */
+ multipoint: function(node) {
+ var nodeList = this.getElementsByTagNameNS(node, this.gmlns,
+ "Point");
+ var components = [];
+ if(nodeList.length > 0) {
+ var point;
+ for(var i=0; i<nodeList.length; ++i) {
+ point = this.parseGeometry.point.apply(this, [nodeList[i]]);
+ if(point) {
+ components.push(point);
+ }
+ }
+ }
+ return new OpenLayers.Geometry.MultiPoint(components);
+ },
+
+ /**
+ * Method: parseGeometry.linestring
+ * Given a GML node representing a linestring geometry, create an
+ * OpenLayers linestring geometry.
+ *
+ * Parameters:
+ * node - {DOMElement} A GML node.
+ *
+ * Returns:
+ * {<OpenLayers.Geometry.LineString>} A linestring geometry.
+ */
+ linestring: function(node, ring) {
+ /**
+ * Two coordinate variations to consider:
+ * 1) <gml:posList dimension="d">x0 y0 z0 x1 y1 z1</gml:posList>
+ * 2) <gml:coordinates>x0, y0, z0 x1, y1, z1</gml:coordinates>
+ */
+ var nodeList, coordString;
+ var coords = [];
+ var points = [];
+
+ // look for <gml:posList>
+ nodeList = this.getElementsByTagNameNS(node, this.gmlns, "posList");
+ if(nodeList.length > 0) {
+ coordString = this.concatChildValues(nodeList[0]);
+ coordString = coordString.replace(this.regExes.trimSpace, "");
+ coords = coordString.split(this.regExes.splitSpace);
+ var dim = parseInt(nodeList[0].getAttribute("dimension"));
+ var j, x, y, z;
+ for(var i=0; i<coords.length/dim; ++i) {
+ j = i * dim;
+ x = coords[j];
+ y = coords[j+1];
+ z = (dim == 2) ? null : coords[j+2];
+ if (this.xy) {
+ points.push(new OpenLayers.Geometry.Point(x, y, z));
+ } else {
+ points.push(new OpenLayers.Geometry.Point(y, x, z));
+ }
+ }
+ }
+
+ // look for <gml:coordinates>
+ if(coords.length == 0) {
+ nodeList = this.getElementsByTagNameNS(node, this.gmlns,
+ "coordinates");
+ if(nodeList.length > 0) {
+ coordString = this.concatChildValues(nodeList[0]);
+ coordString = coordString.replace(this.regExes.trimSpace,
+ "");
+ coordString = coordString.replace(this.regExes.trimComma,
+ ",");
+ var pointList = coordString.split(this.regExes.splitSpace);
+ for(var i=0; i<pointList.length; ++i) {
+ coords = pointList[i].split(",");
+ if(coords.length == 2) {
+ coords[2] = null;
+ }
+ if (this.xy) {
+ points.push(new OpenLayers.Geometry.Point(coords[0],
+ coords[1],
+ coords[2]));
+ } else {
+ points.push(new OpenLayers.Geometry.Point(coords[1],
+ coords[0],
+ coords[2]));
+ }
+ }
+ }
+ }
+
+ var line = null;
+ if(points.length != 0) {
+ if(ring) {
+ line = new OpenLayers.Geometry.LinearRing(points);
+ } else {
+ line = new OpenLayers.Geometry.LineString(points);
+ }
+ }
+ return line;
+ },
+
+ /**
+ * Method: parseGeometry.multilinestring
+ * Given a GML node representing a multilinestring geometry, create an
+ * OpenLayers multilinestring geometry.
+ *
+ * Parameters:
+ * node - {DOMElement} A GML node.
+ *
+ * Returns:
+ * {<OpenLayers.Geometry.MultiLineString>} A multilinestring geometry.
+ */
+ multilinestring: function(node) {
+ var nodeList = this.getElementsByTagNameNS(node, this.gmlns,
+ "LineString");
+ var components = [];
+ if(nodeList.length > 0) {
+ var line;
+ for(var i=0; i<nodeList.length; ++i) {
+ line = this.parseGeometry.linestring.apply(this,
+ [nodeList[i]]);
+ if(line) {
+ components.push(line);
+ }
+ }
+ }
+ return new OpenLayers.Geometry.MultiLineString(components);
+ },
+
+ /**
+ * Method: parseGeometry.polygon
+ * Given a GML node representing a polygon geometry, create an
+ * OpenLayers polygon geometry.
+ *
+ * Parameters:
+ * node - {DOMElement} A GML node.
+ *
+ * Returns:
+ * {<OpenLayers.Geometry.Polygon>} A polygon geometry.
+ */
+ polygon: function(node) {
+ var nodeList = this.getElementsByTagNameNS(node, this.gmlns,
+ "LinearRing");
+ var components = [];
+ if(nodeList.length > 0) {
+ // this assumes exterior ring first, inner rings after
+ var ring;
+ for(var i=0; i<nodeList.length; ++i) {
+ ring = this.parseGeometry.linestring.apply(this,
+ [nodeList[i], true]);
+ if(ring) {
+ components.push(ring);
+ }
+ }
+ }
+ return new OpenLayers.Geometry.Polygon(components);
+ },
+
+ /**
+ * Method: parseGeometry.multipolygon
+ * Given a GML node representing a multipolygon geometry, create an
+ * OpenLayers multipolygon geometry.
+ *
+ * Parameters:
+ * node - {DOMElement} A GML node.
+ *
+ * Returns:
+ * {<OpenLayers.Geometry.MultiPolygon>} A multipolygon geometry.
+ */
+ multipolygon: function(node) {
+ var nodeList = this.getElementsByTagNameNS(node, this.gmlns,
+ "Polygon");
+ var components = [];
+ if(nodeList.length > 0) {
+ var polygon;
+ for(var i=0; i<nodeList.length; ++i) {
+ polygon = this.parseGeometry.polygon.apply(this,
+ [nodeList[i]]);
+ if(polygon) {
+ components.push(polygon);
+ }
+ }
+ }
+ return new OpenLayers.Geometry.MultiPolygon(components);
+ },
+
+ envelope: function(node) {
+ var components = [];
+ var coordString;
+ var envelope;
+
+ var lpoint = this.getElementsByTagNameNS(node, this.gmlns, "lowerCorner");
+ if (lpoint.length > 0) {
+ var coords = [];
+
+ if(lpoint.length > 0) {
+ coordString = lpoint[0].firstChild.nodeValue;
+ coordString = coordString.replace(this.regExes.trimSpace, "");
+ coords = coordString.split(this.regExes.splitSpace);
+ }
+
+ if(coords.length == 2) {
+ coords[2] = null;
+ }
+ if (this.xy) {
+ var lowerPoint = new OpenLayers.Geometry.Point(coords[0], coords[1],coords[2]);
+ } else {
+ var lowerPoint = new OpenLayers.Geometry.Point(coords[1], coords[0],coords[2]);
+ }
+ }
+
+ var upoint = this.getElementsByTagNameNS(node, this.gmlns, "upperCorner");
+ if (upoint.length > 0) {
+ var coords = [];
+
+ if(upoint.length > 0) {
+ coordString = upoint[0].firstChild.nodeValue;
+ coordString = coordString.replace(this.regExes.trimSpace, "");
+ coords = coordString.split(this.regExes.splitSpace);
+ }
+
+ if(coords.length == 2) {
+ coords[2] = null;
+ }
+ if (this.xy) {
+ var upperPoint = new OpenLayers.Geometry.Point(coords[0], coords[1],coords[2]);
+ } else {
+ var upperPoint = new OpenLayers.Geometry.Point(coords[1], coords[0],coords[2]);
+ }
+ }
+
+ if (lowerPoint && upperPoint) {
+ components.push(new OpenLayers.Geometry.Point(lowerPoint.x, lowerPoint.y));
+ components.push(new OpenLayers.Geometry.Point(upperPoint.x, lowerPoint.y));
+ components.push(new OpenLayers.Geometry.Point(upperPoint.x, upperPoint.y));
+ components.push(new OpenLayers.Geometry.Point(lowerPoint.x, upperPoint.y));
+ components.push(new OpenLayers.Geometry.Point(lowerPoint.x, lowerPoint.y));
+
+ var ring = new OpenLayers.Geometry.LinearRing(components);
+ envelope = new OpenLayers.Geometry.Polygon([ring]);
+ }
+ return envelope;
+ }
+ },
+
+ /**
+ * Method: parseAttributes
+ *
+ * Parameters:
+ * node - {<DOMElement>}
+ *
+ * Returns:
+ * {Object} An attributes object.
+ */
+ parseAttributes: function(node) {
+ var attributes = {};
+ // assume attributes are children of the first type 1 child
+ var childNode = node.firstChild;
+ var children, i, child, grandchildren, grandchild, name, value;
+ while(childNode) {
+ if(childNode.nodeType == 1) {
+ // attributes are type 1 children with one type 3 child
+ children = childNode.childNodes;
+ for(i=0; i<children.length; ++i) {
+ child = children[i];
+ if(child.nodeType == 1) {
+ grandchildren = child.childNodes;
+ if(grandchildren.length == 1) {
+ grandchild = grandchildren[0];
+ if(grandchild.nodeType == 3 ||
+ grandchild.nodeType == 4) {
+ name = (child.prefix) ?
+ child.nodeName.split(":")[1] :
+ child.nodeName;
+ value = grandchild.nodeValue.replace(
+ this.regExes.trimSpace, "");
+ attributes[name] = value;
+ }
+ }
+ }
+ }
+ break;
+ }
+ childNode = childNode.nextSibling;
+ }
+ return attributes;
+ },
+
+ /**
+ * APIMethod: write
+ * Generate a GML document string given a list of features.
+ *
+ * Parameters:
+ * features - {Array(<OpenLayers.Feature.Vector>)} List of features to
+ * serialize into a string.
+ *
+ * Returns:
+ * {String} A string representing the GML document.
+ */
+ write: function(features) {
+ if(!(features instanceof Array)) {
+ features = [features];
+ }
+ var gml = this.createElementNS("http://www.opengis.net/wfs",
+ "wfs:" + this.collectionName);
+ for(var i=0; i<features.length; i++) {
+ gml.appendChild(this.createFeatureXML(features[i]));
+ }
+ return OpenLayers.Format.XML.prototype.write.apply(this, [gml]);
+ },
+
+ /**
+ * Method: createFeatureXML
+ * Accept an OpenLayers.Feature.Vector, and build a GML node for it.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>} The feature to be built as GML.
+ *
+ * Returns:
+ * {DOMElement} A node reprensting the feature in GML.
+ */
+ createFeatureXML: function(feature) {
+ var geometry = feature.geometry;
+ var geometryNode = this.buildGeometryNode(geometry);
+ var geomContainer = this.createElementNS(this.featureNS,
+ this.featurePrefix + ":" +
+ this.geometryName);
+ geomContainer.appendChild(geometryNode);
+ var featureNode = this.createElementNS(this.gmlns,
+ "gml:" + this.featureName);
+ var featureContainer = this.createElementNS(this.featureNS,
+ this.featurePrefix + ":" +
+ this.layerName);
+ var fid = feature.fid || feature.id;
+ featureContainer.setAttribute("fid", fid);
+ featureContainer.appendChild(geomContainer);
+ for(var attr in feature.attributes) {
+ var attrText = this.createTextNode(feature.attributes[attr]);
+ var nodename = attr.substring(attr.lastIndexOf(":") + 1);
+ var attrContainer = this.createElementNS(this.featureNS,
+ this.featurePrefix + ":" +
+ nodename);
+ attrContainer.appendChild(attrText);
+ featureContainer.appendChild(attrContainer);
+ }
+ featureNode.appendChild(featureContainer);
+ return featureNode;
+ },
+
+ /**
+ * APIMethod: buildGeometryNode
+ */
+ buildGeometryNode: function(geometry) {
+ if (this.externalProjection && this.internalProjection) {
+ geometry = geometry.clone();
+ geometry.transform(this.internalProjection,
+ this.externalProjection);
+ }
+ var className = geometry.CLASS_NAME;
+ var type = className.substring(className.lastIndexOf(".") + 1);
+ var builder = this.buildGeometry[type.toLowerCase()];
+ return builder.apply(this, [geometry]);
+ },
+
+ /**
+ * Property: buildGeometry
+ * Object containing methods to do the actual geometry node building
+ * based on geometry type.
+ */
+ buildGeometry: {
+ // TBD retrieve the srs from layer
+ // srsName is non-standard, so not including it until it's right.
+ // gml.setAttribute("srsName",
+ // "http://www.opengis.net/gml/srs/epsg.xml#4326");
+
+ /**
+ * Method: buildGeometry.point
+ * Given an OpenLayers point geometry, create a GML point.
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry.Point>} A point geometry.
+ *
+ * Returns:
+ * {DOMElement} A GML point node.
+ */
+ point: function(geometry) {
+ var gml = this.createElementNS(this.gmlns, "gml:Point");
+ gml.appendChild(this.buildCoordinatesNode(geometry));
+ return gml;
+ },
+
+ /**
+ * Method: buildGeometry.multipoint
+ * Given an OpenLayers multipoint geometry, create a GML multipoint.
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry.MultiPoint>} A multipoint geometry.
+ *
+ * Returns:
+ * {DOMElement} A GML multipoint node.
+ */
+ multipoint: function(geometry) {
+ var gml = this.createElementNS(this.gmlns, "gml:MultiPoint");
+ var points = geometry.components;
+ var pointMember, pointGeom;
+ for(var i=0; i<points.length; i++) {
+ pointMember = this.createElementNS(this.gmlns,
+ "gml:pointMember");
+ pointGeom = this.buildGeometry.point.apply(this,
+ [points[i]]);
+ pointMember.appendChild(pointGeom);
+ gml.appendChild(pointMember);
+ }
+ return gml;
+ },
+
+ /**
+ * Method: buildGeometry.linestring
+ * Given an OpenLayers linestring geometry, create a GML linestring.
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry.LineString>} A linestring geometry.
+ *
+ * Returns:
+ * {DOMElement} A GML linestring node.
+ */
+ linestring: function(geometry) {
+ var gml = this.createElementNS(this.gmlns, "gml:LineString");
+ gml.appendChild(this.buildCoordinatesNode(geometry));
+ return gml;
+ },
+
+ /**
+ * Method: buildGeometry.multilinestring
+ * Given an OpenLayers multilinestring geometry, create a GML
+ * multilinestring.
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry.MultiLineString>} A multilinestring
+ * geometry.
+ *
+ * Returns:
+ * {DOMElement} A GML multilinestring node.
+ */
+ multilinestring: function(geometry) {
+ var gml = this.createElementNS(this.gmlns, "gml:MultiLineString");
+ var lines = geometry.components;
+ var lineMember, lineGeom;
+ for(var i=0; i<lines.length; ++i) {
+ lineMember = this.createElementNS(this.gmlns,
+ "gml:lineStringMember");
+ lineGeom = this.buildGeometry.linestring.apply(this,
+ [lines[i]]);
+ lineMember.appendChild(lineGeom);
+ gml.appendChild(lineMember);
+ }
+ return gml;
+ },
+
+ /**
+ * Method: buildGeometry.linearring
+ * Given an OpenLayers linearring geometry, create a GML linearring.
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry.LinearRing>} A linearring geometry.
+ *
+ * Returns:
+ * {DOMElement} A GML linearring node.
+ */
+ linearring: function(geometry) {
+ var gml = this.createElementNS(this.gmlns, "gml:LinearRing");
+ gml.appendChild(this.buildCoordinatesNode(geometry));
+ return gml;
+ },
+
+ /**
+ * Method: buildGeometry.polygon
+ * Given an OpenLayers polygon geometry, create a GML polygon.
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry.Polygon>} A polygon geometry.
+ *
+ * Returns:
+ * {DOMElement} A GML polygon node.
+ */
+ polygon: function(geometry) {
+ var gml = this.createElementNS(this.gmlns, "gml:Polygon");
+ var rings = geometry.components;
+ var ringMember, ringGeom, type;
+ for(var i=0; i<rings.length; ++i) {
+ type = (i==0) ? "outerBoundaryIs" : "innerBoundaryIs";
+ ringMember = this.createElementNS(this.gmlns,
+ "gml:" + type);
+ ringGeom = this.buildGeometry.linearring.apply(this,
+ [rings[i]]);
+ ringMember.appendChild(ringGeom);
+ gml.appendChild(ringMember);
+ }
+ return gml;
+ },
+
+ /**
+ * Method: buildGeometry.multipolygon
+ * Given an OpenLayers multipolygon geometry, create a GML multipolygon.
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry.MultiPolygon>} A multipolygon
+ * geometry.
+ *
+ * Returns:
+ * {DOMElement} A GML multipolygon node.
+ */
+ multipolygon: function(geometry) {
+ var gml = this.createElementNS(this.gmlns, "gml:MultiPolygon");
+ var polys = geometry.components;
+ var polyMember, polyGeom;
+ for(var i=0; i<polys.length; ++i) {
+ polyMember = this.createElementNS(this.gmlns,
+ "gml:polygonMember");
+ polyGeom = this.buildGeometry.polygon.apply(this,
+ [polys[i]]);
+ polyMember.appendChild(polyGeom);
+ gml.appendChild(polyMember);
+ }
+ return gml;
+ }
+ },
+
+ /**
+ * Method: buildCoordinates
+ * builds the coordinates XmlNode
+ * (code)
+ * <gml:coordinates decimal="." cs="," ts=" ">...</gml:coordinates>
+ * (end)
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry>}
+ *
+ * Returns:
+ * {XmlNode} created xmlNode
+ */
+ buildCoordinatesNode: function(geometry) {
+ var coordinatesNode = this.createElementNS(this.gmlns,
+ "gml:coordinates");
+ coordinatesNode.setAttribute("decimal", ".");
+ coordinatesNode.setAttribute("cs", ",");
+ coordinatesNode.setAttribute("ts", " ");
+
+ var points = (geometry.components) ? geometry.components : [geometry];
+ var parts = [];
+ for(var i=0; i<points.length; i++) {
+ parts.push(points[i].x + "," + points[i].y);
+ }
+
+ var txtNode = this.createTextNode(parts.join(" "));
+ coordinatesNode.appendChild(txtNode);
+
+ return coordinatesNode;
+ },
+
+ CLASS_NAME: "OpenLayers.Format.GML"
+});
+/* ======================================================================
+ OpenLayers/Format/GPX.js
+ ====================================================================== */
+
+/* Copyright (c) 2006-2007 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/Format/XML.js
+ * @requires OpenLayers/Feature/Vector.js
+ * @requires OpenLayers/Geometry/Point.js
+ * @requires OpenLayers/Geometry/LineString.js
+ *
+ * Class: OpenLayers.Format.GPX
+ * Read/write GPX parser. Create a new instance with the
+ * <OpenLayers.Format.GPX> constructor.
+ *
+ * Inherits from:
+ * - <OpenLayers.Format.XML>
+ */
+OpenLayers.Format.GPX = OpenLayers.Class(OpenLayers.Format.XML, {
+ /**
+ * APIProperty: extractWaypoints
+ * {Boolean} Extract waypoints from GPX. (default: true)
+ */
+ extractWaypoints: true,
+
+ /**
+ * APIProperty: extractTracks
+ * {Boolean} Extract tracks from GPX. (default: true)
+ */
+ extractTracks: true,
+
+ /**
+ * APIProperty: extractRoutes
+ * {Boolean} Extract routes from GPX. (default: true)
+ */
+ extractRoutes: true,
+
+ /**
+ * APIProperty: extractAttributes
+ * {Boolean} Extract feature attributes from GPX. (default: true)
+ * NOTE: Attributes as part of extensions to the GPX standard may not
+ * be extracted.
+ */
+ extractAttributes: true,
+
+ /**
+ * Constructor: OpenLayers.Format.GPX
+ * Create a new parser for GPX.
+ *
+ * Parameters:
+ * options - {Object} An optional object whose properties will be set on
+ * this instance.
+ */
+ initialize: function(options) {
+ OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
+ },
+
+ /**
+ * APIMethod: read
+ * Return a list of features from a GPX doc
+ *
+ * Parameters:
+ * doc - {Element}
+ *
+ * Returns:
+ * An Array of <OpenLayers.Feature.Vector>s
+ */
+ read: function(doc) {
+ if (typeof doc == "string") {
+ doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc]);
+ }
+ var features = [];
+
+ if(this.extractWaypoints) {
+ var waypoints = doc.getElementsByTagName("wpt");
+ for (var l = 0, len = waypoints.length; l < len; l++) {
+ var attrs = {};
+ if(this.extractAttributes) {
+ attrs = this.parseAttributes(waypoints[l]);
+ }
+ var wpt = new OpenLayers.Geometry.Point(waypoints[l].getAttribute("lon"), waypoints[l].getAttribute("lat"));
+ features.push(new OpenLayers.Feature.Vector(wpt, attrs));
+ }
+ }
+
+ if(this.extractTracks) {
+ var tracks = doc.getElementsByTagName("trk");
+ for (var i=0, len=tracks.length; i<len; i++) {
+ // Attributes are only in trk nodes, not trkseg nodes
+ var attrs = {}
+ if(this.extractAttributes) {
+ attrs = this.parseAttributes(tracks[i]);
+ }
+
+ var segs = this.getElementsByTagNameNS(tracks[i], tracks[i].namespaceURI, "trkseg");
+ for (var j = 0, seglen = segs.length; j < seglen; j++) {
+ // We don't yet support extraction of trkpt attributes
+ // All trksegs of a trk get that trk's attributes
+ var track = this.extractSegment(segs[j], "trkpt");
+ features.push(new OpenLayers.Feature.Vector(track, attrs));
+ }
+ }
+ }
+
+ if(this.extractRoutes) {
+ var routes = doc.getElementsByTagName("rte");
+ for (var k=0, klen=routes.length; k<klen; k++) {
+ var attrs = {}
+ if(this.extractAttributes) {
+ attrs = this.parseAttributes(routes[k]);
+ }
+ var route = this.extractSegment(routes[k], "rtept");
+ features.push(new OpenLayers.Feature.Vector(route, attrs));
+ }
+ }
+
+ if (this.internalProjection && this.externalProjection) {
+ for (var g = 0, featLength = features.length; g < featLength; g++) {
+ features[g].geometry.transform(this.externalProjection,
+ this.internalProjection);
+ }
+ }
+
+ return features;
+ },
+
+ /**
+ * Method: extractSegment
+ *
+ * Parameters:
+ * segment - {<DOMElement>} a trkseg or rte node to parse
+ * segmentType - {String} nodeName of waypoints that form the line
+ *
+ * Returns:
+ * {<OpenLayers.Geometry.LineString>} A linestring geometry
+ */
+ extractSegment: function(segment, segmentType) {
+ var points = this.getElementsByTagNameNS(segment, segment.namespaceURI, segmentType);
+ var point_features = [];
+ for (var i = 0, len = points.length; i < len; i++) {
+ point_features.push(new OpenLayers.Geometry.Point(points[i].getAttribute("lon"), points[i].getAttribute("lat")));
+ }
+ return new OpenLayers.Geometry.LineString(point_features);
+ },
+
+ /**
+ * Method: parseAttributes
+ *
+ * Parameters:
+ * node - {<DOMElement>}
+ *
+ * Returns:
+ * {Object} An attributes object.
+ */
+ parseAttributes: function(node) {
+ // node is either a wpt, trk or rte
+ // attributes are children of the form <attr>value</attr>
+ var attributes = {};
+ var attrNode = node.firstChild;
+ while(attrNode) {
+ if(attrNode.nodeType == 1) {
+ var value = attrNode.firstChild;
+ if(value.nodeType == 3 || value.nodeType == 4) {
+ name = (attrNode.prefix) ?
+ attrNode.nodeName.split(":")[1] :
+ attrNode.nodeName;
+ attributes[name] = value.nodeValue;
+ }
+ }
+ attrNode = attrNode.nextSibling;
+ }
+ return attributes;
+ },
+
+ CLASS_NAME: "OpenLayers.Format.GPX"
+});
+/* ======================================================================
+ OpenLayers/Format/GeoJSON.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/Format/JSON.js
+ * @requires OpenLayers/Feature/Vector.js
+ * @requires OpenLayers/Geometry/Point.js
+ * @requires OpenLayers/Geometry/MultiPoint.js
+ * @requires OpenLayers/Geometry/LineString.js
+ * @requires OpenLayers/Geometry/MultiLineString.js
+ * @requires OpenLayers/Geometry/Polygon.js
+ * @requires OpenLayers/Geometry/MultiPolygon.js
+ */
+
+/**
+ * Class: OpenLayers.Format.GeoJSON
+ * Read and write GeoJSON. Create a new parser with the
+ * <OpenLayers.Format.GeoJSON> constructor.
+ *
+ * Inherits from:
+ * - <OpenLayers.Format.JSON>
+ */
+OpenLayers.Format.GeoJSON = OpenLayers.Class(OpenLayers.Format.JSON, {
+
+ /**
+ * Constructor: OpenLayers.Format.GeoJSON
+ * Create a new parser for GeoJSON.
+ *
+ * Parameters:
+ * options - {Object} An optional object whose properties will be set on
+ * this instance.
+ */
+ initialize: function(options) {
+ OpenLayers.Format.JSON.prototype.initialize.apply(this, [options]);
+ },
+
+ /**
+ * APIMethod: read
+ * Deserialize a GeoJSON string.
+ *
+ * Parameters:
+ * json - {String} A GeoJSON string
+ * type - {String} Optional string that determines the structure of
+ * the output. Supported values are "Geometry", "Feature", and
+ * "FeatureCollection". If absent or null, a default of
+ * "FeatureCollection" is assumed.
+ * filter - {Function} A function which will be called for every key and
+ * value at every level of the final result. Each value will be
+ * replaced by the result of the filter function. This can be used to
+ * reform generic objects into instances of classes, or to transform
+ * date strings into Date objects.
+ *
+ * Returns:
+ * {Object} The return depends on the value of the type argument. If type
+ * is "FeatureCollection" (the default), the return will be an array
+ * of <OpenLayers.Feature.Vector>. If type is "Geometry", the input json
+ * must represent a single geometry, and the return will be an
+ * <OpenLayers.Geometry>. If type is "Feature", the input json must
+ * represent a single feature, and the return will be an
+ * <OpenLayers.Feature.Vector>.
+ */
+ read: function(json, type, filter) {
+ type = (type) ? type : "FeatureCollection";
+ var results = null;
+ var obj = null;
+ if (typeof json == "string") {
+ obj = OpenLayers.Format.JSON.prototype.read.apply(this,
+ [json, filter]);
+ } else {
+ obj = json;
+ }
+ if(!obj) {
+ OpenLayers.Console.error("Bad JSON: " + json);
+ } else if(typeof(obj.type) != "string") {
+ OpenLayers.Console.error("Bad GeoJSON - no type: " + json);
+ } else if(this.isValidType(obj, type)) {
+ switch(type) {
+ case "Geometry":
+ try {
+ results = this.parseGeometry(obj);
+ } catch(err) {
+ OpenLayers.Console.error(err);
+ }
+ break;
+ case "Feature":
+ try {
+ results = this.parseFeature(obj);
+ results.type = "Feature";
+ } catch(err) {
+ OpenLayers.Console.error(err);
+ }
+ break;
+ case "FeatureCollection":
+ // for type FeatureCollection, we allow input to be any type
+ results = [];
+ switch(obj.type) {
+ case "Feature":
+ try {
+ results.push(this.parseFeature(obj));
+ } catch(err) {
+ results = null;
+ OpenLayers.Console.error(err);
+ }
+ break;
+ case "FeatureCollection":
+ for(var i=0, len=obj.features.length; i<len; ++i) {
+ try {
+ results.push(this.parseFeature(obj.features[i]));
+ } catch(err) {
+ results = null;
+ OpenLayers.Console.error(err);
+ }
+ }
+ break;
+ default:
+ try {
+ var geom = this.parseGeometry(obj);
+ results.push(new OpenLayers.Feature.Vector(geom));
+ } catch(err) {
+ results = null;
+ OpenLayers.Console.error(err);
+ }
+ }
+ break;
+ }
+ }
+ return results;
+ },
+
+ /**
+ * Method: isValidType
+ * Check if a GeoJSON object is a valid representative of the given type.
+ *
+ * Returns:
+ * {Boolean} The object is valid GeoJSON object of the given type.
+ */
+ isValidType: function(obj, type) {
+ var valid = false;
+ switch(type) {
+ case "Geometry":
+ if(OpenLayers.Util.indexOf(
+ ["Point", "MultiPoint", "LineString", "MultiLineString",
+ "Polygon", "MultiPolygon", "Box", "GeometryCollection"],
+ obj.type) == -1) {
+ // unsupported geometry type
+ OpenLayers.Console.error("Unsupported geometry type: " +
+ obj.type);
+ } else {
+ valid = true;
+ }
+ break;
+ case "FeatureCollection":
+ // allow for any type to be converted to a feature collection
+ valid = true;
+ break;
+ default:
+ // for Feature types must match
+ if(obj.type == type) {
+ valid = true;
+ } else {
+ OpenLayers.Console.error("Cannot convert types from " +
+ obj.type + " to " + type);
+ }
+ }
+ return valid;
+ },
+
+ /**
+ * Method: parseFeature
+ * Convert a feature object from GeoJSON into an
+ * <OpenLayers.Feature.Vector>.
+ *
+ * Parameters:
+ * obj - {Object} An object created from a GeoJSON object
+ *
+ * Returns:
+ * {<OpenLayers.Feature.Vector>} A feature.
+ */
+ parseFeature: function(obj) {
+ var feature, geometry, attributes;
+ attributes = (obj.properties) ? obj.properties : {};
+ try {
+ geometry = this.parseGeometry(obj.geometry);
+ } catch(err) {
+ // deal with bad geometries
+ throw err;
+ }
+ feature = new OpenLayers.Feature.Vector(geometry, attributes);
+ if(obj.id) {
+ feature.fid = obj.id;
+ }
+ return feature;
+ },
+
+ /**
+ * Method: parseGeometry
+ * Convert a geometry object from GeoJSON into an <OpenLayers.Geometry>.
+ *
+ * Parameters:
+ * obj - {Object} An object created from a GeoJSON object
+ *
+ * Returns:
+ * {<OpenLayers.Geometry>} A geometry.
+ */
+ parseGeometry: function(obj) {
+ if (obj == null) {
+ return null;
+ }
+ var geometry;
+ if(obj.type == "GeometryCollection") {
+ if(!(obj.geometries instanceof Array)) {
+ throw "GeometryCollection must have geometries array: " + obj;
+ }
+ var numGeom = obj.geometries.length;
+ var components = new Array(numGeom);
+ for(var i=0; i<numGeom; ++i) {
+ components[i] = this.parseGeometry.apply(
+ this, [obj.geometries[i]]
+ );
+ }
+ geometry = new OpenLayers.Geometry.Collection(components);
+ } else {
+ if(!(obj.coordinates instanceof Array)) {
+ throw "Geometry must have coordinates array: " + obj;
+ }
+ if(!this.parseCoords[obj.type.toLowerCase()]) {
+ throw "Unsupported geometry type: " + obj.type;
+ }
+ try {
+ geometry = this.parseCoords[obj.type.toLowerCase()].apply(
+ this, [obj.coordinates]
+ );
+ } catch(err) {
+ // deal with bad coordinates
+ throw err;
+ }
+ }
+ if (this.internalProjection && this.externalProjection) {
+ geometry.transform(this.externalProjection,
+ this.internalProjection);
+ }
+ return geometry;
+ },
+
+ /**
+ * Property: parseCoords
+ * Object with properties corresponding to the GeoJSON geometry types.
+ * Property values are functions that do the actual parsing.
+ */
+ parseCoords: {
+ /**
+ * Method: parseCoords.point
+ * Convert a coordinate array from GeoJSON into an
+ * <OpenLayers.Geometry>.
+ *
+ * Parameters:
+ * array - {Object} The coordinates array from the GeoJSON fragment.
+ *
+ * Returns:
+ * {<OpenLayers.Geometry>} A geometry.
+ */
+ "point": function(array) {
+ if(array.length != 2) {
+ throw "Only 2D points are supported: " + array;
+ }
+ return new OpenLayers.Geometry.Point(array[0], array[1]);
+ },
+
+ /**
+ * Method: parseCoords.multipoint
+ * Convert a coordinate array from GeoJSON into an
+ * <OpenLayers.Geometry>.
+ *
+ * Parameters:
+ * array {Object} The coordinates array from the GeoJSON fragment.
+ *
+ * Returns:
+ * {<OpenLayers.Geometry>} A geometry.
+ */
+ "multipoint": function(array) {
+ var points = [];
+ var p = null;
+ for(var i=0, len=array.length; i<len; ++i) {
+ try {
+ p = this.parseCoords["point"].apply(this, [array[i]]);
+ } catch(err) {
+ throw err;
+ }
+ points.push(p);
+ }
+ return new OpenLayers.Geometry.MultiPoint(points);
+ },
+
+ /**
+ * Method: parseCoords.linestring
+ * Convert a coordinate array from GeoJSON into an
+ * <OpenLayers.Geometry>.
+ *
+ * Parameters:
+ * array - {Object} The coordinates array from the GeoJSON fragment.
+ *
+ * Returns:
+ * {<OpenLayers.Geometry>} A geometry.
+ */
+ "linestring": function(array) {
+ var points = [];
+ var p = null;
+ for(var i=0, len=array.length; i<len; ++i) {
+ try {
+ p = this.parseCoords["point"].apply(this, [array[i]]);
+ } catch(err) {
+ throw err;
+ }
+ points.push(p);
+ }
+ return new OpenLayers.Geometry.LineString(points);
+ },
+
+ /**
+ * Method: parseCoords.multilinestring
+ * Convert a coordinate array from GeoJSON into an
+ * <OpenLayers.Geometry>.
+ *
+ * Parameters:
+ * array - {Object} The coordinates array from the GeoJSON fragment.
+ *
+ * Returns:
+ * {<OpenLayers.Geometry>} A geometry.
+ */
+ "multilinestring": function(array) {
+ var lines = [];
+ var l = null;
+ for(var i=0, len=array.length; i<len; ++i) {
+ try {
+ l = this.parseCoords["linestring"].apply(this, [array[i]]);
+ } catch(err) {
+ throw err;
+ }
+ lines.push(l);
+ }
+ return new OpenLayers.Geometry.MultiLineString(lines);
+ },
+
+ /**
+ * Method: parseCoords.polygon
+ * Convert a coordinate array from GeoJSON into an
+ * <OpenLayers.Geometry>.
+ *
+ * Returns:
+ * {<OpenLayers.Geometry>} A geometry.
+ */
+ "polygon": function(array) {
+ var rings = [];
+ var r, l;
+ for(var i=0, len=array.length; i<len; ++i) {
+ try {
+ l = this.parseCoords["linestring"].apply(this, [array[i]]);
+ } catch(err) {
+ throw err;
+ }
+ r = new OpenLayers.Geometry.LinearRing(l.components);
+ rings.push(r);
+ }
+ return new OpenLayers.Geometry.Polygon(rings);
+ },
+
+ /**
+ * Method: parseCoords.multipolygon
+ * Convert a coordinate array from GeoJSON into an
+ * <OpenLayers.Geometry>.
+ *
+ * Parameters:
+ * array - {Object} The coordinates array from the GeoJSON fragment.
+ *
+ * Returns:
+ * {<OpenLayers.Geometry>} A geometry.
+ */
+ "multipolygon": function(array) {
+ var polys = [];
+ var p = null;
+ for(var i=0, len=array.length; i<len; ++i) {
+ try {
+ p = this.parseCoords["polygon"].apply(this, [array[i]]);
+ } catch(err) {
+ throw err;
+ }
+ polys.push(p);
+ }
+ return new OpenLayers.Geometry.MultiPolygon(polys);
+ },
+
+ /**
+ * Method: parseCoords.box
+ * Convert a coordinate array from GeoJSON into an
+ * <OpenLayers.Geometry>.
+ *
+ * Parameters:
+ * array - {Object} The coordinates array from the GeoJSON fragment.
+ *
+ * Returns:
+ * {<OpenLayers.Geometry>} A geometry.
+ */
+ "box": function(array) {
+ if(array.length != 2) {
+ throw "GeoJSON box coordinates must have 2 elements";
+ }
+ return new OpenLayers.Geometry.Polygon([
+ new OpenLayers.Geometry.LinearRing([
+ new OpenLayers.Geometry.Point(array[0][0], array[0][1]),
+ new OpenLayers.Geometry.Point(array[1][0], array[0][1]),
+ new OpenLayers.Geometry.Point(array[1][0], array[1][1]),
+ new OpenLayers.Geometry.Point(array[0][0], array[1][1]),
+ new OpenLayers.Geometry.Point(array[0][0], array[0][1])
+ ])
+ ]);
+ }
+
+ },
+
+ /**
+ * APIMethod: write
+ * Serialize a feature, geometry, array of features into a GeoJSON string.
+ *
+ * Parameters:
+ * obj - {Object} An <OpenLayers.Feature.Vector>, <OpenLayers.Geometry>,
+ * or an array of features.
+ * pretty - {Boolean} Structure the output with newlines and indentation.
+ * Default is false.
+ *
+ * Returns:
+ * {String} The GeoJSON string representation of the input geometry,
+ * features, or array of features.
+ */
+ write: function(obj, pretty) {
+ var geojson = {
+ "type": null
+ };
+ if(obj instanceof Array) {
+ geojson.type = "FeatureCollection";
+ var numFeatures = obj.length;
+ geojson.features = new Array(numFeatures);
+ for(var i=0; i<numFeatures; ++i) {
+ var element = obj[i];
+ if(!element instanceof OpenLayers.Feature.Vector) {
+ var msg = "FeatureCollection only supports collections " +
+ "of features: " + element;
+ throw msg;
+ }
+ geojson.features[i] = this.extract.feature.apply(
+ this, [element]
+ );
+ }
+ } else if (obj.CLASS_NAME.indexOf("OpenLayers.Geometry") == 0) {
+ geojson = this.extract.geometry.apply(this, [obj]);
+ } else if (obj instanceof OpenLayers.Feature.Vector) {
+ geojson = this.extract.feature.apply(this, [obj]);
+ if(obj.layer && obj.layer.projection) {
+ geojson.crs = this.createCRSObject(obj);
+ }
+ }
+ return OpenLayers.Format.JSON.prototype.write.apply(this,
+ [geojson, pretty]);
+ },
+
+ /**
+ * Method: createCRSObject
+ * Create the CRS object for an object.
+ *
+ * Parameters:
+ * object - {<OpenLayers.Feature.Vector>}
+ *
+ * Returns:
+ * {Object} An object which can be assigned to the crs property
+ * of a GeoJSON object.
+ */
+ createCRSObject: function(object) {
+ var proj = object.layer.projection.toString();
+ var crs = {};
+ if (proj.match(/epsg:/i)) {
+ var code = parseInt(proj.substring(proj.indexOf(":") + 1));
+ if (code == 4326) {
+ crs = {
+ "type": "OGC",
+ "properties": {
+ "urn": "urn:ogc:def:crs:OGC:1.3:CRS84"
+ }
+ };
+ } else {
+ crs = {
+ "type": "EPSG",
+ "properties": {
+ "code": code
+ }
+ };
+ }
+ }
+ return crs;
+ },
+
+ /**
+ * Property: extract
+ * Object with properties corresponding to the GeoJSON types.
+ * Property values are functions that do the actual value extraction.
+ */
+ extract: {
+ /**
+ * Method: extract.feature
+ * Return a partial GeoJSON object representing a single feature.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>}
+ *
+ * Returns:
+ * {Object} An object representing the point.
+ */
+ 'feature': function(feature) {
+ var geom = this.extract.geometry.apply(this, [feature.geometry]);
+ return {
+ "type": "Feature",
+ "id": feature.fid == null ? feature.id : feature.fid,
+ "properties": feature.attributes,
+ "geometry": geom
+ };
+ },
+
+ /**
+ * Method: extract.geometry
+ * Return a GeoJSON object representing a single geometry.
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry>}
+ *
+ * Returns:
+ * {Object} An object representing the geometry.
+ */
+ 'geometry': function(geometry) {
+ if (geometry == null) {
+ return null;
+ }
+ if (this.internalProjection && this.externalProjection) {
+ geometry = geometry.clone();
+ geometry.transform(this.internalProjection,
+ this.externalProjection);
+ }
+ var geometryType = geometry.CLASS_NAME.split('.')[2];
+ var data = this.extract[geometryType.toLowerCase()].apply(this, [geometry]);
+ var json;
+ if(geometryType == "Collection") {
+ json = {
+ "type": "GeometryCollection",
+ "geometries": data
+ };
+ } else {
+ json = {
+ "type": geometryType,
+ "coordinates": data
+ };
+ }
+
+ return json;
+ },
+
+ /**
+ * Method: extract.point
+ * Return an array of coordinates from a point.
+ *
+ * Parameters:
+ * point - {<OpenLayers.Geometry.Point>}
+ *
+ * Returns:
+ * {Array} An array of coordinates representing the point.
+ */
+ 'point': function(point) {
+ return [point.x, point.y];
+ },
+
+ /**
+ * Method: extract.multipoint
+ * Return an array of point coordinates from a multipoint.
+ *
+ * Parameters:
+ * multipoint - {<OpenLayers.Geometry.MultiPoint>}
+ *
+ * Returns:
+ * {Array} An array of point coordinate arrays representing
+ * the multipoint.
+ */
+ 'multipoint': function(multipoint) {
+ var array = [];
+ for(var i=0, len=multipoint.components.length; i<len; ++i) {
+ array.push(this.extract.point.apply(this, [multipoint.components[i]]));
+ }
+ return array;
+ },
+
+ /**
+ * Method: extract.linestring
+ * Return an array of coordinate arrays from a linestring.
+ *
+ * Parameters:
+ * linestring - {<OpenLayers.Geometry.LineString>}
+ *
+ * Returns:
+ * {Array} An array of coordinate arrays representing
+ * the linestring.
+ */
+ 'linestring': function(linestring) {
+ var array = [];
+ for(var i=0, len=linestring.components.length; i<len; ++i) {
+ array.push(this.extract.point.apply(this, [linestring.components[i]]));
+ }
+ return array;
+ },
+
+ /**
+ * Method: extract.multilinestring
+ * Return an array of linestring arrays from a linestring.
+ *
+ * Parameters:
+ * linestring - {<OpenLayers.Geometry.MultiLineString>}
+ *
+ * Returns:
+ * {Array} An array of linestring arrays representing
+ * the multilinestring.
+ */
+ 'multilinestring': function(multilinestring) {
+ var array = [];
+ for(var i=0, len=multilinestring.components.length; i<len; ++i) {
+ array.push(this.extract.linestring.apply(this, [multilinestring.components[i]]));
+ }
+ return array;
+ },
+
+ /**
+ * Method: extract.polygon
+ * Return an array of linear ring arrays from a polygon.
+ *
+ * Parameters:
+ * polygon - {<OpenLayers.Geometry.Polygon>}
+ *
+ * Returns:
+ * {Array} An array of linear ring arrays representing the polygon.
+ */
+ 'polygon': function(polygon) {
+ var array = [];
+ for(var i=0, len=polygon.components.length; i<len; ++i) {
+ array.push(this.extract.linestring.apply(this, [polygon.components[i]]));
+ }
+ return array;
+ },
+
+ /**
+ * Method: extract.multipolygon
+ * Return an array of polygon arrays from a multipolygon.
+ *
+ * Parameters:
+ * multipolygon - {<OpenLayers.Geometry.MultiPolygon>}
+ *
+ * Returns:
+ * {Array} An array of polygon arrays representing
+ * the multipolygon
+ */
+ 'multipolygon': function(multipolygon) {
+ var array = [];
+ for(var i=0, len=multipolygon.components.length; i<len; ++i) {
+ array.push(this.extract.polygon.apply(this, [multipolygon.components[i]]));
+ }
+ return array;
+ },
+
+ /**
+ * Method: extract.collection
+ * Return an array of geometries from a geometry collection.
+ *
+ * Parameters:
+ * collection - {<OpenLayers.Geometry.Collection>}
+ *
+ * Returns:
+ * {Array} An array of geometry objects representing the geometry
+ * collection.
+ */
+ 'collection': function(collection) {
+ var len = collection.components.length;
+ var array = new Array(len);
+ for(var i=0; i<len; ++i) {
+ array[i] = this.extract.geometry.apply(
+ this, [collection.components[i]]
+ );
+ }
+ return array;
+ }
+
+
+ },
+
+ CLASS_NAME: "OpenLayers.Format.GeoJSON"
+
+});
+/* ======================================================================
+ OpenLayers/Format/GeoRSS.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/Format/XML.js
+ * @requires OpenLayers/Feature/Vector.js
+ * @requires OpenLayers/Geometry/Point.js
+ * @requires OpenLayers/Geometry/LineString.js
+ * @requires OpenLayers/Geometry/Polygon.js
+ */
+
+/**
+ * Class: OpenLayers.Format.GeoRSS
+ * Read/write GeoRSS parser. Create a new instance with the
+ * <OpenLayers.Format.GeoRSS> constructor.
+ *
+ * Inherits from:
+ * - <OpenLayers.Format.XML>
+ */
+OpenLayers.Format.GeoRSS = OpenLayers.Class(OpenLayers.Format.XML, {
+
+ /**
+ * APIProperty: rssns
+ * {String} RSS namespace to use. Defaults to
+ * "http://backend.userland.com/rss2"
+ */
+ rssns: "http://backend.userland.com/rss2",
+
+ /**
+ * APIProperty: featurens
+ * {String} Feature Attributes namespace. Defaults to
+ * "http://mapserver.gis.umn.edu/mapserver"
+ */
+ featureNS: "http://mapserver.gis.umn.edu/mapserver",
+
+ /**
+ * APIProperty: georssns
+ * {String} GeoRSS namespace to use. Defaults to
+ * "http://www.georss.org/georss"
+ */
+ georssns: "http://www.georss.org/georss",
+
+ /**
+ * APIProperty: geons
+ * {String} W3C Geo namespace to use. Defaults to
+ * "http://www.w3.org/2003/01/geo/wgs84_pos#"
+ */
+ geons: "http://www.w3.org/2003/01/geo/wgs84_pos#",
+
+ /**
+ * APIProperty: featureTitle
+ * {String} Default title for features. Defaults to "Untitled"
+ */
+ featureTitle: "Untitled",
+
+ /**
+ * APIProperty: featureDescription
+ * {String} Default description for features. Defaults to "No Description"
+ */
+ featureDescription: "No Description",
+
+ /**
+ * Property: gmlParse
+ * {Object} GML Format object for parsing features
+ * Non-API and only created if necessary
+ */
+ gmlParser: null,
+
+ /**
+ * APIProperty: xy
+ * {Boolean} Order of the GML coordinate: true:(x,y) or false:(y,x)
+ * For GeoRSS the default is (y,x), therefore: false
+ */
+ xy: false,
+
+ /**
+ * Constructor: OpenLayers.Format.GeoRSS
+ * Create a new parser for GeoRSS.
+ *
+ * Parameters:
+ * options - {Object} An optional object whose properties will be set on
+ * this instance.
+ */
+ initialize: function(options) {
+ OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
+ },
+
+ /**
+ * Method: createGeometryFromItem
+ * Return a geometry from a GeoRSS Item.
+ *
+ * Parameters:
+ * item - {DOMElement} A GeoRSS item node.
+ *
+ * Returns:
+ * {<OpenLayers.Geometry>} A geometry representing the node.
+ */
+ createGeometryFromItem: function(item) {
+ var point = this.getElementsByTagNameNS(item, this.georssns, "point");
+ var lat = this.getElementsByTagNameNS(item, this.geons, 'lat');
+ var lon = this.getElementsByTagNameNS(item, this.geons, 'long');
+
+ var line = this.getElementsByTagNameNS(item,
+ this.georssns,
+ "line");
+ var polygon = this.getElementsByTagNameNS(item,
+ this.georssns,
+ "polygon");
+ var where = this.getElementsByTagNameNS(item,
+ this.georssns,
+ "where");
+ if (point.length > 0 || (lat.length > 0 && lon.length > 0)) {
+ var location;
+ if (point.length > 0) {
+ location = OpenLayers.String.trim(
+ point[0].firstChild.nodeValue).split(/\s+/);
+ if (location.length !=2) {
+ location = OpenLayers.String.trim(
+ point[0].firstChild.nodeValue).split(/\s*,\s*/);
+ }
+ } else {
+ location = [parseFloat(lat[0].firstChild.nodeValue),
+ parseFloat(lon[0].firstChild.nodeValue)];
+ }
+
+ var geometry = new OpenLayers.Geometry.Point(parseFloat(location[1]),
+ parseFloat(location[0]));
+
+ } else if (line.length > 0) {
+ var coords = OpenLayers.String.trim(this.concatChildValues(line[0])).split(/\s+/);
+ var components = [];
+ var point;
+ for (var i=0, len=coords.length; i<len; i+=2) {
+ point = new OpenLayers.Geometry.Point(parseFloat(coords[i+1]),
+ parseFloat(coords[i]));
+ components.push(point);
+ }
+ geometry = new OpenLayers.Geometry.LineString(components);
+ } else if (polygon.length > 0) {
+ var coords = OpenLayers.String.trim(this.concatChildValues(polygon[0])).split(/\s+/);
+ var components = [];
+ var point;
+ for (var i=0, len=coords.length; i<len; i+=2) {
+ point = new OpenLayers.Geometry.Point(parseFloat(coords[i+1]),
+ parseFloat(coords[i]));
+ components.push(point);
+ }
+ geometry = new OpenLayers.Geometry.Polygon([new OpenLayers.Geometry.LinearRing(components)]);
+ } else if (where.length > 0) {
+ if (!this.gmlParser) {
+ this.gmlParser = new OpenLayers.Format.GML({'xy': this.xy});
+ }
+ var feature = this.gmlParser.parseFeature(where[0]);
+ geometry = feature.geometry;
+ }
+
+ if (geometry && this.internalProjection && this.externalProjection) {
+ geometry.transform(this.externalProjection,
+ this.internalProjection);
+ }
+
+ return geometry;
+ },
+
+ /**
+ * Method: createFeatureFromItem
+ * Return a feature from a GeoRSS Item.
+ *
+ * Parameters:
+ * item - {DOMElement} A GeoRSS item node.
+ *
+ * Returns:
+ * {<OpenLayers.Feature.Vector>} A feature representing the item.
+ */
+ createFeatureFromItem: function(item) {
+ var geometry = this.createGeometryFromItem(item);
+
+ /* Provide defaults for title and description */
+ var title = this.getChildValue(item, "*", "title", this.featureTitle);
+
+ /* First try RSS descriptions, then Atom summaries */
+ var description = this.getChildValue(
+ item, "*", "description",
+ this.getChildValue(item, "*", "content", this.featureDescription)
+ );
+
+ /* If no link URL is found in the first child node, try the
+ href attribute */
+ var link = this.getChildValue(item, "*", "link");
+ if(!link) {
+ try {
+ link = this.getElementsByTagNameNS(item, "*", "link")[0].getAttribute("href");
+ } catch(e) {
+ link = null;
+ }
+ }
+
+ var id = this.getChildValue(item, "*", "id", null);
+
+ var data = {
+ "title": title,
+ "description": description,
+ "link": link
+ };
+ var feature = new OpenLayers.Feature.Vector(geometry, data);
+ feature.fid = id;
+ return feature;
+ },
+
+ /**
+ * Method: getChildValue
+ *
+ * Parameters:
+ * node - {DOMElement}
+ * nsuri - {String} Child node namespace uri ("*" for any).
+ * name - {String} Child node name.
+ * def - {String} Optional string default to return if no child found.
+ *
+ * Returns:
+ * {String} The value of the first child with the given tag name. Returns
+ * default value or empty string if none found.
+ */
+ getChildValue: function(node, nsuri, name, def) {
+ var value;
+ var eles = this.getElementsByTagNameNS(node, nsuri, name);
+ if(eles && eles[0] && eles[0].firstChild
+ && eles[0].firstChild.nodeValue) {
+ value = eles[0].firstChild.nodeValue;
+ } else {
+ value = (def == undefined) ? "" : def;
+ }
+ return value;
+ },
+
+ /**
+ * APIMethod: read
+ * Return a list of features from a GeoRSS doc
+
+ * Parameters:
+ * data - {Element}
+ *
+ * Returns:
+ * An Array of <OpenLayers.Feature.Vector>s
+ */
+ read: function(doc) {
+ if (typeof doc == "string") {
+ doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc]);
+ }
+
+ /* Try RSS items first, then Atom entries */
+ var itemlist = null;
+ itemlist = this.getElementsByTagNameNS(doc, '*', 'item');
+ if (itemlist.length == 0) {
+ itemlist = this.getElementsByTagNameNS(doc, '*', 'entry');
+ }
+
+ var numItems = itemlist.length;
+ var features = new Array(numItems);
+ for(var i=0; i<numItems; i++) {
+ features[i] = this.createFeatureFromItem(itemlist[i]);
+ }
+ return features;
+ },
+
+
+ /**
+ * APIMethod: write
+ * Accept Feature Collection, and return a string.
+ *
+ * Parameters:
+ * features - {Array(<OpenLayers.Feature.Vector>)} List of features to serialize into a string.
+ */
+ write: function(features) {
+ var georss;
+ if(features instanceof Array) {
+ georss = this.createElementNS(this.rssns, "rss");
+ for(var i=0, len=features.length; i<len; i++) {
+ georss.appendChild(this.createFeatureXML(features[i]));
+ }
+ } else {
+ georss = this.createFeatureXML(features);
+ }
+ return OpenLayers.Format.XML.prototype.write.apply(this, [georss]);
+ },
+
+ /**
+ * Method: createFeatureXML
+ * Accept an <OpenLayers.Feature.Vector>, and build a geometry for it.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>}
+ *
+ * Returns:
+ * {DOMElement}
+ */
+ createFeatureXML: function(feature) {
+ var geometryNode = this.buildGeometryNode(feature.geometry);
+ var featureNode = this.createElementNS(this.rssns, "item");
+ var titleNode = this.createElementNS(this.rssns, "title");
+ titleNode.appendChild(this.createTextNode(feature.attributes.title ? feature.attributes.title : ""));
+ var descNode = this.createElementNS(this.rssns, "description");
+ descNode.appendChild(this.createTextNode(feature.attributes.description ? feature.attributes.description : ""));
+ featureNode.appendChild(titleNode);
+ featureNode.appendChild(descNode);
+ if (feature.attributes.link) {
+ var linkNode = this.createElementNS(this.rssns, "link");
+ linkNode.appendChild(this.createTextNode(feature.attributes.link));
+ featureNode.appendChild(linkNode);
+ }
+ for(var attr in feature.attributes) {
+ if (attr == "link" || attr == "title" || attr == "description") { continue; }
+ var attrText = this.createTextNode(feature.attributes[attr]);
+ var nodename = attr;
+ if (attr.search(":") != -1) {
+ nodename = attr.split(":")[1];
+ }
+ var attrContainer = this.createElementNS(this.featureNS, "feature:"+nodename);
+ attrContainer.appendChild(attrText);
+ featureNode.appendChild(attrContainer);
+ }
+ featureNode.appendChild(geometryNode);
+ return featureNode;
+ },
+
+ /**
+ * Method: buildGeometryNode
+ * builds a GeoRSS node with a given geometry
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry>}
+ *
+ * Returns:
+ * {DOMElement} A gml node.
+ */
+ buildGeometryNode: function(geometry) {
+ if (this.internalProjection && this.externalProjection) {
+ geometry = geometry.clone();
+ geometry.transform(this.internalProjection,
+ this.externalProjection);
+ }
+ var node;
+ // match Polygon
+ if (geometry.CLASS_NAME == "OpenLayers.Geometry.Polygon") {
+ node = this.createElementNS(this.georssns, 'georss:polygon');
+
+ node.appendChild(this.buildCoordinatesNode(geometry.components[0]));
+ }
+ // match LineString
+ else if (geometry.CLASS_NAME == "OpenLayers.Geometry.LineString") {
+ node = this.createElementNS(this.georssns, 'georss:line');
+
+ node.appendChild(this.buildCoordinatesNode(geometry));
+ }
+ // match Point
+ else if (geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
+ node = this.createElementNS(this.georssns, 'georss:point');
+ node.appendChild(this.buildCoordinatesNode(geometry));
+ } else {
+ throw "Couldn't parse " + geometry.CLASS_NAME;
+ }
+ return node;
+ },
+
+ /**
+ * Method: buildCoordinatesNode
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry>}
+ */
+ buildCoordinatesNode: function(geometry) {
+ var points = null;
+
+ if (geometry.components) {
+ points = geometry.components;
+ }
+
+ var path;
+ if (points) {
+ var numPoints = points.length;
+ var parts = new Array(numPoints);
+ for (var i = 0; i < numPoints; i++) {
+ parts[i] = points[i].y + " " + points[i].x;
+ }
+ path = parts.join(" ");
+ } else {
+ path = geometry.y + " " + geometry.x;
+ }
+ return this.createTextNode(path);
+ },
+
+ CLASS_NAME: "OpenLayers.Format.GeoRSS"
+});
+/* ======================================================================
+ OpenLayers/Format/KML.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/Format/XML.js
+ * @requires OpenLayers/Feature/Vector.js
+ * @requires OpenLayers/Geometry/Point.js
+ * @requires OpenLayers/Geometry/LineString.js
+ * @requires OpenLayers/Geometry/Polygon.js
+ * @requires OpenLayers/Geometry/Collection.js
+ * @requires OpenLayers/Request/XMLHttpRequest.js
+ */
+
+/**
+ * Class: OpenLayers.Format.KML
+ * Read/Wite KML. Create a new instance with the <OpenLayers.Format.KML>
+ * constructor.
+ *
+ * Inherits from:
+ * - <OpenLayers.Format.XML>
+ */
+OpenLayers.Format.KML = OpenLayers.Class(OpenLayers.Format.XML, {
+
+ /**
+ * APIProperty: kmlns
+ * {String} KML Namespace to use. Defaults to 2.0 namespace.
+ */
+ kmlns: "http://earth.google.com/kml/2.0",
+
+ /**
+ * APIProperty: placemarksDesc
+ * {String} Name of the placemarks. Default is "No description available."
+ */
+ placemarksDesc: "No description available",
+
+ /**
+ * APIProperty: foldersName
+ * {String} Name of the folders. Default is "OpenLayers export."
+ */
+ foldersName: "OpenLayers export",
+
+ /**
+ * APIProperty: foldersDesc
+ * {String} Description of the folders. Default is "Exported on [date]."
+ */
+ foldersDesc: "Exported on " + new Date(),
+
+ /**
+ * APIProperty: extractAttributes
+ * {Boolean} Extract attributes from KML. Default is true.
+ * Extracting styleUrls requires this to be set to true
+ */
+ extractAttributes: true,
+
+ /**
+ * Property: extractStyles
+ * {Boolean} Extract styles from KML. Default is false.
+ * Extracting styleUrls also requires extractAttributes to be
+ * set to true
+ */
+ extractStyles: false,
+
+ /**
+ * Property: internalns
+ * {String} KML Namespace to use -- defaults to the namespace of the
+ * Placemark node being parsed, but falls back to kmlns.
+ */
+ internalns: null,
+
+ /**
+ * Property: features
+ * {Array} Array of features
+ *
+ */
+ features: null,
+
+ /**
+ * Property: styles
+ * {Object} Storage of style objects
+ *
+ */
+ styles: null,
+
+ /**
+ * Property: styleBaseUrl
+ * {String}
+ */
+ styleBaseUrl: "",
+
+ /**
+ * Property: fetched
+ * {Object} Storage of KML URLs that have been fetched before
+ * in order to prevent reloading them.
+ */
+ fetched: null,
+
+ /**
+ * APIProperty: maxDepth
+ * {Integer} Maximum depth for recursive loading external KML URLs
+ * Defaults to 0: do no external fetching
+ */
+ maxDepth: 0,
+
+ /**
+ * Constructor: OpenLayers.Format.KML
+ * Create a new parser for KML.
+ *
+ * Parameters:
+ * options - {Object} An optional object whose properties will be set on
+ * this instance.
+ */
+ initialize: function(options) {
+ // compile regular expressions once instead of every time they are used
+ this.regExes = {
+ trimSpace: (/^\s*|\s*$/g),
+ removeSpace: (/\s*/g),
+ splitSpace: (/\s+/),
+ trimComma: (/\s*,\s*/g),
+ kmlColor: (/(\w{2})(\w{2})(\w{2})(\w{2})/),
+ kmlIconPalette: (/root:\/\/icons\/palette-(\d+)(\.\w+)/),
+ straightBracket: (/\$\[(.*?)\]/g)
+ };
+ OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
+ },
+
+ /**
+ * APIMethod: read
+ * Read data from a string, and return a list of features.
+ *
+ * Parameters:
+ * data - {String} or {DOMElement} data to read/parse.
+ *
+ * Returns:
+ * {Array(<OpenLayers.Feature.Vector>)} List of features.
+ */
+ read: function(data) {
+ this.features = [];
+ this.styles = {};
+ this.fetched = {};
+
+ // Set default options
+ var options = {
+ depth: this.maxDepth,
+ styleBaseUrl: this.styleBaseUrl
+ };
+
+ return this.parseData(data, options);
+ },
+
+ /**
+ * Method: parseData
+ * Read data from a string, and return a list of features.
+ *
+ * Parameters:
+ * data - {String} or {DOMElement} data to read/parse.
+ * options - {Object} Hash of options
+ *
+ * Returns:
+ * {Array(<OpenLayers.Feature.Vector>)} List of features.
+ */
+ parseData: function(data, options) {
+ if(typeof data == "string") {
+ data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
+ }
+
+ // Loop throught the following node types in this order and
+ // process the nodes found
+ var types = ["Link", "NetworkLink", "Style", "StyleMap", "Placemark"];
+ for(var i=0, len=types.length; i<len; ++i) {
+ var type = types[i];
+
+ var nodes = this.getElementsByTagNameNS(data, "*", type);
+
+ // skip to next type if no nodes are found
+ if(nodes.length == 0) {
+ continue;
+ }
+
+ switch (type.toLowerCase()) {
+
+ // Fetch external links
+ case "link":
+ case "networklink":
+ this.parseLinks(nodes, options);
+ break;
+
+ // parse style information
+ case "style":
+ if (this.extractStyles) {
+ this.parseStyles(nodes, options);
+ }
+ break;
+ case "stylemap":
+ if (this.extractStyles) {
+ this.parseStyleMaps(nodes, options);
+ }
+ break;
+
+ // parse features
+ case "placemark":
+ this.parseFeatures(nodes, options);
+ break;
+ }
+ }
+
+ return this.features;
+ },
+
+ /**
+ * Method: parseLinks
+ * Finds URLs of linked KML documents and fetches them
+ *
+ * Parameters:
+ * nodes - {Array} of {DOMElement} data to read/parse.
+ * options - {Object} Hash of options
+ *
+ */
+ parseLinks: function(nodes, options) {
+
+ // Fetch external links <NetworkLink> and <Link>
+ // Don't do anything if we have reached our maximum depth for recursion
+ if (options.depth >= this.maxDepth) {
+ return false;
+ }
+
+ // increase depth
+ var newOptions = OpenLayers.Util.extend({}, options);
+ newOptions.depth++;
+
+ for(var i=0, len=nodes.length; i<len; i++) {
+ var href = this.parseProperty(nodes[i], "*", "href");
+ if(href && !this.fetched[href]) {
+ this.fetched[href] = true; // prevent reloading the same urls
+ var data = this.fetchLink(href);
+ if (data) {
+ this.parseData(data, newOptions);
+ }
+ }
+ }
+
+ },
+
+ /**
+ * Method: fetchLink
+ * Fetches a URL and returns the result
+ *
+ * Parameters:
+ * href - {String} url to be fetched
+ *
+ */
+ fetchLink: function(href) {
+ var request = OpenLayers.Request.GET({url: href, async: false});
+ if (request) {
+ return request.responseText;
+ }
+ },
+
+ /**
+ * Method: parseStyles
+ * Looks for <Style> nodes in the data and parses them
+ * Also parses <StyleMap> nodes, but only uses the 'normal' key
+ *
+ * Parameters:
+ * nodes - {Array} of {DOMElement} data to read/parse.
+ * options - {Object} Hash of options
+ *
+ */
+ parseStyles: function(nodes, options) {
+ for(var i=0, len=nodes.length; i<len; i++) {
+ var style = this.parseStyle(nodes[i]);
+ if(style) {
+ styleName = (options.styleBaseUrl || "") + "#" + style.id;
+
+ this.styles[styleName] = style;
+ }
+ }
+ },
+
+ /**
+ * Method: parseStyle
+ * Parses the children of a <Style> node and builds the style hash
+ * accordingly
+ *
+ * Parameters:
+ * node - {DOMElement} <Style> node
+ *
+ */
+ parseStyle: function(node) {
+ var style = {};
+
+ var types = ["LineStyle", "PolyStyle", "IconStyle", "BalloonStyle"];
+ var type, nodeList, geometry, parser;
+ for(var i=0, len=types.length; i<len; ++i) {
+ type = types[i];
+ styleTypeNode = this.getElementsByTagNameNS(node,
+ "*", type)[0];
+ if(!styleTypeNode) {
+ continue;
+ }
+
+ // only deal with first geometry of this type
+ switch (type.toLowerCase()) {
+ case "linestyle":
+ var color = this.parseProperty(styleTypeNode, "*", "color");
+ if (color) {
+ var matches = (color.toString()).match(
+ this.regExes.kmlColor);
+
+ // transparency
+ var alpha = matches[1];
+ style["strokeOpacity"] = parseInt(alpha, 16) / 255;
+
+ // rgb colors (google uses bgr)
+ var b = matches[2];
+ var g = matches[3];
+ var r = matches[4];
+ style["strokeColor"] = "#" + r + g + b;
+ }
+
+ var width = this.parseProperty(styleTypeNode, "*", "width");
+ if (width) {
+ style["strokeWidth"] = width;
+ }
+
+ case "polystyle":
+ var color = this.parseProperty(styleTypeNode, "*", "color");
+ if (color) {
+ var matches = (color.toString()).match(
+ this.regExes.kmlColor);
+
+ // transparency
+ var alpha = matches[1];
+ style["fillOpacity"] = parseInt(alpha, 16) / 255;
+
+ // rgb colors (google uses bgr)
+ var b = matches[2];
+ var g = matches[3];
+ var r = matches[4];
+ style["fillColor"] = "#" + r + g + b;
+ }
+
+ break;
+ case "iconstyle":
+ // set scale
+ var scale = parseFloat(this.parseProperty(styleTypeNode,
+ "*", "scale") || 1);
+
+ // set default width and height of icon
+ var width = 32 * scale;
+ var height = 32 * scale;
+
+ var iconNode = this.getElementsByTagNameNS(styleTypeNode,
+ "*",
+ "Icon")[0];
+ if (iconNode) {
+ var href = this.parseProperty(iconNode, "*", "href");
+ if (href) {
+
+ var w = this.parseProperty(iconNode, "*", "w");
+ var h = this.parseProperty(iconNode, "*", "h");
+
+ // Settings for Google specific icons that are 64x64
+ // We set the width and height to 64 and halve the
+ // scale to prevent icons from being too big
+ var google = "http://maps.google.com/mapfiles/kml";
+ if (OpenLayers.String.startsWith(
+ href, google) && !w && !h) {
+ w = 64;
+ h = 64;
+ scale = scale / 2;
+ }
+
+ // if only dimension is defined, make sure the
+ // other one has the same value
+ w = w || h;
+ h = h || w;
+
+ if (w) {
+ width = parseInt(w) * scale;
+ }
+
+ if (h) {
+ height = parseInt(h) * scale;
+ }
+
+ // support for internal icons
+ // (/root://icons/palette-x.png)
+ // x and y tell the position on the palette:
+ // - in pixels
+ // - starting from the left bottom
+ // We translate that to a position in the list
+ // and request the appropriate icon from the
+ // google maps website
+ var matches = href.match(this.regExes.kmlIconPalette);
+ if (matches) {
+ var palette = matches[1];
+ var file_extension = matches[2];
+
+ var x = this.parseProperty(iconNode, "*", "x");
+ var y = this.parseProperty(iconNode, "*", "y");
+
+ var posX = x ? x/32 : 0;
+ var posY = y ? (7 - y/32) : 7;
+
+ var pos = posY * 8 + posX;
+ href = "http://maps.google.com/mapfiles/kml/pal"
+ + palette + "/icon" + pos + file_extension;
+ }
+
+ style["graphicOpacity"] = 1; // fully opaque
+ style["externalGraphic"] = href;
+ }
+
+ }
+
+
+ // hotSpots define the offset for an Icon
+ var hotSpotNode = this.getElementsByTagNameNS(styleTypeNode,
+ "*",
+ "hotSpot")[0];
+ if (hotSpotNode) {
+ var x = parseFloat(hotSpotNode.getAttribute("x"));
+ var y = parseFloat(hotSpotNode.getAttribute("y"));
+
+ var xUnits = hotSpotNode.getAttribute("xunits");
+ if (xUnits == "pixels") {
+ style["graphicXOffset"] = -x * scale;
+ }
+ else if (xUnits == "insetPixels") {
+ style["graphicXOffset"] = -width + (x * scale);
+ }
+ else if (xUnits == "fraction") {
+ style["graphicXOffset"] = -width * x;
+ }
+
+ var yUnits = hotSpotNode.getAttribute("yunits");
+ if (yUnits == "pixels") {
+ style["graphicYOffset"] = -height + (y * scale) + 1;
+ }
+ else if (yUnits == "insetPixels") {
+ style["graphicYOffset"] = -(y * scale) + 1;
+ }
+ else if (yUnits == "fraction") {
+ style["graphicYOffset"] = -height * (1 - y) + 1;
+ }
+ }
+
+ style["graphicWidth"] = width;
+ style["graphicHeight"] = height;
+ break;
+
+ case "balloonstyle":
+ var balloonStyle = OpenLayers.Util.getXmlNodeValue(
+ styleTypeNode);
+ if (balloonStyle) {
+ style["balloonStyle"] = balloonStyle.replace(
+ this.regExes.straightBracket, "${$1}");
+ }
+ break;
+ default:
+ }
+ }
+
+ // Some polygons have no line color, so we use the fillColor for that
+ if (!style["strokeColor"] && style["fillColor"]) {
+ style["strokeColor"] = style["fillColor"];
+ }
+
+ var id = node.getAttribute("id");
+ if (id && style) {
+ style.id = id;
+ }
+
+ return style;
+ },
+
+ /**
+ * Method: parseStyleMaps
+ * Looks for <Style> nodes in the data and parses them
+ * Also parses <StyleMap> nodes, but only uses the 'normal' key
+ *
+ * Parameters:
+ * nodes - {Array} of {DOMElement} data to read/parse.
+ * options - {Object} Hash of options
+ *
+ */
+ parseStyleMaps: function(nodes, options) {
+ // Only the default or "normal" part of the StyleMap is processed now
+ // To do the select or "highlight" bit, we'd need to change lots more
+
+ for(var i=0, len=nodes.length; i<len; i++) {
+ var node = nodes[i];
+ var pairs = this.getElementsByTagNameNS(node, "*",
+ "Pair");
+
+ var id = node.getAttribute("id");
+ for (var j=0, jlen=pairs.length; j<jlen; j++) {
+ var pair = pairs[j];
+ // Use the shortcut in the SLD format to quickly retrieve the
+ // value of a node. Maybe it's good to have a method in
+ // Format.XML to do this
+ var key = this.parseProperty(pair, "*", "key");
+ var styleUrl = this.parseProperty(pair, "*", "styleUrl");
+
+ if (styleUrl && key == "normal") {
+ this.styles[(options.styleBaseUrl || "") + "#" + id] =
+ this.styles[(options.styleBaseUrl || "") + styleUrl];
+ }
+
+ if (styleUrl && key == "highlight") {
+ // TODO: implement the "select" part
+ }
+
+ }
+ }
+
+ },
+
+
+ /**
+ * Method: parseFeatures
+ * Loop through all Placemark nodes and parse them.
+ * Will create a list of features
+ *
+ * Parameters:
+ * nodes - {Array} of {DOMElement} data to read/parse.
+ * options - {Object} Hash of options
+ *
+ */
+ parseFeatures: function(nodes, options) {
+ var features = new Array(nodes.length);
+ for(var i=0, len=nodes.length; i<len; i++) {
+ var featureNode = nodes[i];
+ var feature = this.parseFeature.apply(this,[featureNode]) ;
+ if(feature) {
+
+ // Create reference to styleUrl
+ if (this.extractStyles && feature.attributes &&
+ feature.attributes.styleUrl) {
+ feature.style = this.getStyle(feature.attributes.styleUrl);
+ }
+
+ if (this.extractStyles) {
+ // Make sure that <Style> nodes within a placemark are
+ // processed as well
+ var inlineStyleNode = this.getElementsByTagNameNS(featureNode,
+ "*",
+ "Style")[0];
+ if (inlineStyleNode) {
+ var inlineStyle= this.parseStyle(inlineStyleNode);
+ if (inlineStyle) {
+ feature.style = OpenLayers.Util.extend(
+ feature.style, inlineStyle
+ );
+ }
+ }
+ }
+
+ // add feature to list of features
+ features[i] = feature;
+ } else {
+ throw "Bad Placemark: " + i;
+ }
+ }
+
+ // add new features to existing feature list
+ this.features = this.features.concat(features);
+ },
+
+ /**
+ * Method: parseFeature
+ * This function is the core of the KML parsing code in OpenLayers.
+ * It creates the geometries that are then attached to the returned
+ * feature, and calls parseAttributes() to get attribute data out.
+ *
+ * Parameters:
+ * node - {DOMElement}
+ *
+ * Returns:
+ * {<OpenLayers.Feature.Vector>} A vector feature.
+ */
+ parseFeature: function(node) {
+ // only accept one geometry per feature - look for highest "order"
+ var order = ["MultiGeometry", "Polygon", "LineString", "Point"];
+ var type, nodeList, geometry, parser;
+ for(var i=0, len=order.length; i<len; ++i) {
+ type = order[i];
+ this.internalns = node.namespaceURI ?
+ node.namespaceURI : this.kmlns;
+ nodeList = this.getElementsByTagNameNS(node,
+ this.internalns, type);
+ if(nodeList.length > 0) {
+ // only deal with first geometry of this type
+ var parser = this.parseGeometry[type.toLowerCase()];
+ if(parser) {
+ geometry = parser.apply(this, [nodeList[0]]);
+ if (this.internalProjection && this.externalProjection) {
+ geometry.transform(this.externalProjection,
+ this.internalProjection);
+ }
+ } else {
+ OpenLayers.Console.error(OpenLayers.i18n(
+ "unsupportedGeometryType", {'geomType':type}));
+ }
+ // stop looking for different geometry types
+ break;
+ }
+ }
+
+ // construct feature (optionally with attributes)
+ var attributes;
+ if(this.extractAttributes) {
+ attributes = this.parseAttributes(node);
+ }
+ var feature = new OpenLayers.Feature.Vector(geometry, attributes);
+
+ var fid = node.getAttribute("id") || node.getAttribute("name");
+ if(fid != null) {
+ feature.fid = fid;
+ }
+
+ return feature;
+ },
+
+ /**
+ * Method: getStyle
+ * Retrieves a style from a style hash using styleUrl as the key
+ * If the styleUrl doesn't exist yet, we try to fetch it
+ * Internet
+ *
+ * Parameters:
+ * styleUrl - {String} URL of style
+ * options - {Object} Hash of options
+ *
+ * Returns:
+ * {Object} - (reference to) Style hash
+ */
+ getStyle: function(styleUrl, options) {
+
+ var styleBaseUrl = OpenLayers.Util.removeTail(styleUrl);
+
+ var newOptions = OpenLayers.Util.extend({}, options);
+ newOptions.depth++;
+ newOptions.styleBaseUrl = styleBaseUrl;
+
+ // Fetch remote Style URLs (if not fetched before)
+ if (!this.styles[styleUrl]
+ && !OpenLayers.String.startsWith(styleUrl, "#")
+ && newOptions.depth <= this.maxDepth
+ && !this.fetched[styleBaseUrl] ) {
+
+ var data = this.fetchLink(styleBaseUrl);
+ if (data) {
+ this.parseData(data, newOptions);
+ }
+
+ }
+
+ // return requested style
+ var style = this.styles[styleUrl];
+ return style;
+ },
+
+ /**
+ * Property: parseGeometry
+ * Properties of this object are the functions that parse geometries based
+ * on their type.
+ */
+ parseGeometry: {
+
+ /**
+ * Method: parseGeometry.point
+ * Given a KML node representing a point geometry, create an OpenLayers
+ * point geometry.
+ *
+ * Parameters:
+ * node - {DOMElement} A KML Point node.
+ *
+ * Returns:
+ * {<OpenLayers.Geometry.Point>} A point geometry.
+ */
+ point: function(node) {
+ var nodeList = this.getElementsByTagNameNS(node, this.internalns,
+ "coordinates");
+ var coords = [];
+ if(nodeList.length > 0) {
+ var coordString = nodeList[0].firstChild.nodeValue;
+ coordString = coordString.replace(this.regExes.removeSpace, "");
+ coords = coordString.split(",");
+ }
+
+ var point = null;
+ if(coords.length > 1) {
+ // preserve third dimension
+ if(coords.length == 2) {
+ coords[2] = null;
+ }
+ point = new OpenLayers.Geometry.Point(coords[0], coords[1],
+ coords[2]);
+ } else {
+ throw "Bad coordinate string: " + coordString;
+ }
+ return point;
+ },
+
+ /**
+ * Method: parseGeometry.linestring
+ * Given a KML node representing a linestring geometry, create an
+ * OpenLayers linestring geometry.
+ *
+ * Parameters:
+ * node - {DOMElement} A KML LineString node.
+ *
+ * Returns:
+ * {<OpenLayers.Geometry.LineString>} A linestring geometry.
+ */
+ linestring: function(node, ring) {
+ var nodeList = this.getElementsByTagNameNS(node, this.internalns,
+ "coordinates");
+ var line = null;
+ if(nodeList.length > 0) {
+ var coordString = this.concatChildValues(nodeList[0]);
+
+ coordString = coordString.replace(this.regExes.trimSpace,
+ "");
+ coordString = coordString.replace(this.regExes.trimComma,
+ ",");
+ var pointList = coordString.split(this.regExes.splitSpace);
+ var numPoints = pointList.length;
+ var points = new Array(numPoints);
+ var coords, numCoords;
+ for(var i=0; i<numPoints; ++i) {
+ coords = pointList[i].split(",");
+ numCoords = coords.length;
+ if(numCoords > 1) {
+ if(coords.length == 2) {
+ coords[2] = null;
+ }
+ points[i] = new OpenLayers.Geometry.Point(coords[0],
+ coords[1],
+ coords[2]);
+ } else {
+ throw "Bad LineString point coordinates: " +
+ pointList[i];
+ }
+ }
+ if(numPoints) {
+ if(ring) {
+ line = new OpenLayers.Geometry.LinearRing(points);
+ } else {
+ line = new OpenLayers.Geometry.LineString(points);
+ }
+ } else {
+ throw "Bad LineString coordinates: " + coordString;
+ }
+ }
+
+ return line;
+ },
+
+ /**
+ * Method: parseGeometry.polygon
+ * Given a KML node representing a polygon geometry, create an
+ * OpenLayers polygon geometry.
+ *
+ * Parameters:
+ * node - {DOMElement} A KML Polygon node.
+ *
+ * Returns:
+ * {<OpenLayers.Geometry.Polygon>} A polygon geometry.
+ */
+ polygon: function(node) {
+ var nodeList = this.getElementsByTagNameNS(node, this.internalns,
+ "LinearRing");
+ var numRings = nodeList.length;
+ var components = new Array(numRings);
+ if(numRings > 0) {
+ // this assumes exterior ring first, inner rings after
+ var ring;
+ for(var i=0, len=nodeList.length; i<len; ++i) {
+ ring = this.parseGeometry.linestring.apply(this,
+ [nodeList[i], true]);
+ if(ring) {
+ components[i] = ring;
+ } else {
+ throw "Bad LinearRing geometry: " + i;
+ }
+ }
+ }
+ return new OpenLayers.Geometry.Polygon(components);
+ },
+
+ /**
+ * Method: parseGeometry.multigeometry
+ * Given a KML node representing a multigeometry, create an
+ * OpenLayers geometry collection.
+ *
+ * Parameters:
+ * node - {DOMElement} A KML MultiGeometry node.
+ *
+ * Returns:
+ * {<OpenLayers.Geometry.Collection>} A geometry collection.
+ */
+ multigeometry: function(node) {
+ var child, parser;
+ var parts = [];
+ var children = node.childNodes;
+ for(var i=0, len=children.length; i<len; ++i ) {
+ child = children[i];
+ if(child.nodeType == 1) {
+ var type = (child.prefix) ?
+ child.nodeName.split(":")[1] :
+ child.nodeName;
+ var parser = this.parseGeometry[type.toLowerCase()];
+ if(parser) {
+ parts.push(parser.apply(this, [child]));
+ }
+ }
+ }
+ return new OpenLayers.Geometry.Collection(parts);
+ }
+
+ },
+
+ /**
+ * Method: parseAttributes
+ *
+ * Parameters:
+ * node - {DOMElement}
+ *
+ * Returns:
+ * {Object} An attributes object.
+ */
+ parseAttributes: function(node) {
+ var attributes = {};
+ // assume attribute nodes are type 1 children with a type 3 or 4 child
+ var child, grandchildren, grandchild;
+ var children = node.childNodes;
+ for(var i=0, len=children.length; i<len; ++i) {
+ child = children[i];
+ if(child.nodeType == 1) {
+ grandchildren = child.childNodes;
+ if(grandchildren.length == 1 || grandchildren.length == 3) {
+ var grandchild;
+ switch (grandchildren.length) {
+ case 1:
+ grandchild = grandchildren[0];
+ break;
+ case 3:
+ default:
+ grandchild = grandchildren[1];
+ break;
+ }
+ if(grandchild.nodeType == 3 || grandchild.nodeType == 4) {
+ var name = (child.prefix) ?
+ child.nodeName.split(":")[1] :
+ child.nodeName;
+ var value = OpenLayers.Util.getXmlNodeValue(grandchild);
+ if (value) {
+ value = value.replace(this.regExes.trimSpace, "");
+ attributes[name] = value;
+ }
+ }
+ }
+ }
+ }
+ return attributes;
+ },
+
+
+ /**
+ * Method: parseProperty
+ * Convenience method to find a node and return its value
+ *
+ * Parameters:
+ * xmlNode - {<DOMElement>}
+ * namespace - {String} namespace of the node to find
+ * tagName - {String} name of the property to parse
+ *
+ * Returns:
+ * {String} The value for the requested property (defaults to null)
+ */
+ parseProperty: function(xmlNode, namespace, tagName) {
+ var value;
+ var nodeList = this.getElementsByTagNameNS(xmlNode, namespace, tagName);
+ try {
+ value = OpenLayers.Util.getXmlNodeValue(nodeList[0]);
+ } catch(e) {
+ value = null;
+ }
+
+ return value;
+ },
+
+ /**
+ * APIMethod: write
+ * Accept Feature Collection, and return a string.
+ *
+ * Parameters:
+ * features - {Array(<OpenLayers.Feature.Vector>} An array of features.
+ *
+ * Returns:
+ * {String} A KML string.
+ */
+ write: function(features) {
+ if(!(features instanceof Array)) {
+ features = [features];
+ }
+ var kml = this.createElementNS(this.kmlns, "kml");
+ var folder = this.createFolderXML();
+ for(var i=0, len=features.length; i<len; ++i) {
+ folder.appendChild(this.createPlacemarkXML(features[i]));
+ }
+ kml.appendChild(folder);
+ return OpenLayers.Format.XML.prototype.write.apply(this, [kml]);
+ },
+
+ /**
+ * Method: createFolderXML
+ * Creates and returns a KML folder node
+ *
+ * Returns:
+ * {DOMElement}
+ */
+ createFolderXML: function() {
+ // Folder name
+ var folderName = this.createElementNS(this.kmlns, "name");
+ var folderNameText = this.createTextNode(this.foldersName);
+ folderName.appendChild(folderNameText);
+
+ // Folder description
+ var folderDesc = this.createElementNS(this.kmlns, "description");
+ var folderDescText = this.createTextNode(this.foldersDesc);
+ folderDesc.appendChild(folderDescText);
+
+ // Folder
+ var folder = this.createElementNS(this.kmlns, "Folder");
+ folder.appendChild(folderName);
+ folder.appendChild(folderDesc);
+
+ return folder;
+ },
+
+ /**
+ * Method: createPlacemarkXML
+ * Creates and returns a KML placemark node representing the given feature.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>}
+ *
+ * Returns:
+ * {DOMElement}
+ */
+ createPlacemarkXML: function(feature) {
+ // Placemark name
+ var placemarkName = this.createElementNS(this.kmlns, "name");
+ var name = (feature.attributes.name) ?
+ feature.attributes.name : feature.id;
+ placemarkName.appendChild(this.createTextNode(name));
+
+ // Placemark description
+ var placemarkDesc = this.createElementNS(this.kmlns, "description");
+ var desc = (feature.attributes.description) ?
+ feature.attributes.description : this.placemarksDesc;
+ placemarkDesc.appendChild(this.createTextNode(desc));
+
+ // Placemark
+ var placemarkNode = this.createElementNS(this.kmlns, "Placemark");
+ if(feature.fid != null) {
+ placemarkNode.setAttribute("id", feature.fid);
+ }
+ placemarkNode.appendChild(placemarkName);
+ placemarkNode.appendChild(placemarkDesc);
+
+ // Geometry node (Point, LineString, etc. nodes)
+ var geometryNode = this.buildGeometryNode(feature.geometry);
+ placemarkNode.appendChild(geometryNode);
+
+ // TBD - deal with remaining (non name/description) attributes.
+ return placemarkNode;
+ },
+
+ /**
+ * Method: buildGeometryNode
+ * Builds and returns a KML geometry node with the given geometry.
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry>}
+ *
+ * Returns:
+ * {DOMElement}
+ */
+ buildGeometryNode: function(geometry) {
+ if (this.internalProjection && this.externalProjection) {
+ geometry = geometry.clone();
+ geometry.transform(this.internalProjection,
+ this.externalProjection);
+ }
+ var className = geometry.CLASS_NAME;
+ var type = className.substring(className.lastIndexOf(".") + 1);
+ var builder = this.buildGeometry[type.toLowerCase()];
+ var node = null;
+ if(builder) {
+ node = builder.apply(this, [geometry]);
+ }
+ return node;
+ },
+
+ /**
+ * Property: buildGeometry
+ * Object containing methods to do the actual geometry node building
+ * based on geometry type.
+ */
+ buildGeometry: {
+ // TBD: Anybody care about namespace aliases here (these nodes have
+ // no prefixes)?
+
+ /**
+ * Method: buildGeometry.point
+ * Given an OpenLayers point geometry, create a KML point.
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry.Point>} A point geometry.
+ *
+ * Returns:
+ * {DOMElement} A KML point node.
+ */
+ point: function(geometry) {
+ var kml = this.createElementNS(this.kmlns, "Point");
+ kml.appendChild(this.buildCoordinatesNode(geometry));
+ return kml;
+ },
+
+ /**
+ * Method: buildGeometry.multipoint
+ * Given an OpenLayers multipoint geometry, create a KML
+ * GeometryCollection.
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry.Point>} A multipoint geometry.
+ *
+ * Returns:
+ * {DOMElement} A KML GeometryCollection node.
+ */
+ multipoint: function(geometry) {
+ return this.buildGeometry.collection.apply(this, [geometry]);
+ },
+
+ /**
+ * Method: buildGeometry.linestring
+ * Given an OpenLayers linestring geometry, create a KML linestring.
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry.LineString>} A linestring geometry.
+ *
+ * Returns:
+ * {DOMElement} A KML linestring node.
+ */
+ linestring: function(geometry) {
+ var kml = this.createElementNS(this.kmlns, "LineString");
+ kml.appendChild(this.buildCoordinatesNode(geometry));
+ return kml;
+ },
+
+ /**
+ * Method: buildGeometry.multilinestring
+ * Given an OpenLayers multilinestring geometry, create a KML
+ * GeometryCollection.
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry.Point>} A multilinestring geometry.
+ *
+ * Returns:
+ * {DOMElement} A KML GeometryCollection node.
+ */
+ multilinestring: function(geometry) {
+ return this.buildGeometry.collection.apply(this, [geometry]);
+ },
+
+ /**
+ * Method: buildGeometry.linearring
+ * Given an OpenLayers linearring geometry, create a KML linearring.
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry.LinearRing>} A linearring geometry.
+ *
+ * Returns:
+ * {DOMElement} A KML linearring node.
+ */
+ linearring: function(geometry) {
+ var kml = this.createElementNS(this.kmlns, "LinearRing");
+ kml.appendChild(this.buildCoordinatesNode(geometry));
+ return kml;
+ },
+
+ /**
+ * Method: buildGeometry.polygon
+ * Given an OpenLayers polygon geometry, create a KML polygon.
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry.Polygon>} A polygon geometry.
+ *
+ * Returns:
+ * {DOMElement} A KML polygon node.
+ */
+ polygon: function(geometry) {
+ var kml = this.createElementNS(this.kmlns, "Polygon");
+ var rings = geometry.components;
+ var ringMember, ringGeom, type;
+ for(var i=0, len=rings.length; i<len; ++i) {
+ type = (i==0) ? "outerBoundaryIs" : "innerBoundaryIs";
+ ringMember = this.createElementNS(this.kmlns, type);
+ ringGeom = this.buildGeometry.linearring.apply(this,
+ [rings[i]]);
+ ringMember.appendChild(ringGeom);
+ kml.appendChild(ringMember);
+ }
+ return kml;
+ },
+
+ /**
+ * Method: buildGeometry.multipolygon
+ * Given an OpenLayers multipolygon geometry, create a KML
+ * GeometryCollection.
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry.Point>} A multipolygon geometry.
+ *
+ * Returns:
+ * {DOMElement} A KML GeometryCollection node.
+ */
+ multipolygon: function(geometry) {
+ return this.buildGeometry.collection.apply(this, [geometry]);
+ },
+
+ /**
+ * Method: buildGeometry.collection
+ * Given an OpenLayers geometry collection, create a KML MultiGeometry.
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry.Collection>} A geometry collection.
+ *
+ * Returns:
+ * {DOMElement} A KML MultiGeometry node.
+ */
+ collection: function(geometry) {
+ var kml = this.createElementNS(this.kmlns, "MultiGeometry");
+ var child;
+ for(var i=0, len=geometry.components.length; i<len; ++i) {
+ child = this.buildGeometryNode.apply(this,
+ [geometry.components[i]]);
+ if(child) {
+ kml.appendChild(child);
+ }
+ }
+ return kml;
+ }
+ },
+
+ /**
+ * Method: buildCoordinatesNode
+ * Builds and returns the KML coordinates node with the given geometry
+ * <coordinates>...</coordinates>
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry>}
+ *
+ * Return:
+ * {DOMElement}
+ */
+ buildCoordinatesNode: function(geometry) {
+ var coordinatesNode = this.createElementNS(this.kmlns, "coordinates");
+
+ var path;
+ var points = geometry.components;
+ if(points) {
+ // LineString or LinearRing
+ var point;
+ var numPoints = points.length;
+ var parts = new Array(numPoints);
+ for(var i=0; i<numPoints; ++i) {
+ point = points[i];
+ parts[i] = point.x + "," + point.y;
+ }
+ path = parts.join(" ");
+ } else {
+ // Point
+ path = geometry.x + "," + geometry.y;
+ }
+
+ var txtNode = this.createTextNode(path);
+ coordinatesNode.appendChild(txtNode);
+
+ return coordinatesNode;
+ },
+
+ CLASS_NAME: "OpenLayers.Format.KML"
+});
+/* ======================================================================
+ OpenLayers/Format/OSM.js
+ ====================================================================== */
+
+/* Copyright (c) 2006-2007 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/Format/XML.js
+ * @requires OpenLayers/Feature/Vector.js
+ * @requires OpenLayers/Geometry/Point.js
+ * @requires OpenLayers/Geometry/LineString.js
+ * @requires OpenLayers/Geometry/Polygon.js
+ */
+
+/**
+ * Class: OpenLayers.Format.OSM
+ * OSM parser. Create a new instance with the
+ * <OpenLayers.Format.OSM> constructor.
+ *
+ * Inherits from:
+ * - <OpenLayers.Format.XML>
+ */
+OpenLayers.Format.OSM = OpenLayers.Class(OpenLayers.Format.XML, {
+
+ /**
+ * APIProperty: checkTags
+ * {Boolean} Should tags be checked to determine whether something
+ * should be treated as a seperate node. Will slow down parsing.
+ * Default is false.
+ */
+ checkTags: false,
+
+ /**
+ * Property: interestingTagsExclude
+ * {Array} List of tags to exclude from 'interesting' checks on nodes.
+ * Must be set when creating the format. Will only be used if checkTags
+ * is set.
+ */
+ interestingTagsExclude: null,
+
+ /**
+ * APIProperty: areaTags
+ * {Array} List of tags indicating that something is an area.
+ * Must be set when creating the format. Will only be used if
+ * checkTags is true.
+ */
+ areaTags: null,
+
+ /**
+ * Constructor: OpenLayers.Format.OSM
+ * Create a new parser for OSM.
+ *
+ * Parameters:
+ * options - {Object} An optional object whose properties will be set on
+ * this instance.
+ */
+ initialize: function(options) {
+ var layer_defaults = {
+ 'interestingTagsExclude': ['source', 'source_ref',
+ 'source:ref', 'history', 'attribution', 'created_by'],
+ 'areaTags': ['area', 'building', 'leisure', 'tourism', 'ruins',
+ 'historic', 'landuse', 'military', 'natural', 'sport']
+ };
+
+ layer_defaults = OpenLayers.Util.extend(layer_defaults, options);
+
+ var interesting = {};
+ for (var i = 0; i < layer_defaults.interestingTagsExclude.length; i++) {
+ interesting[layer_defaults.interestingTagsExclude[i]] = true;
+ }
+ layer_defaults.interestingTagsExclude = interesting;
+
+ var area = {};
+ for (var i = 0; i < layer_defaults.areaTags.length; i++) {
+ area[layer_defaults.areaTags[i]] = true;
+ }
+ layer_defaults.areaTags = area;
+
+ OpenLayers.Format.XML.prototype.initialize.apply(this, [layer_defaults]);
+ },
+
+ /**
+ * APIMethod: read
+ * Return a list of features from a OSM doc
+
+ * Parameters:
+ * data - {Element}
+ *
+ * Returns:
+ * An Array of <OpenLayers.Feature.Vector>s
+ */
+ read: function(doc) {
+ if (typeof doc == "string") {
+ doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc]);
+ }
+
+ var nodes = this.getNodes(doc);
+ var ways = this.getWays(doc);
+
+ // Geoms will contain at least ways.length entries.
+ var feat_list = new Array(ways.length);
+
+ for (var i = 0; i < ways.length; i++) {
+ // We know the minimal of this one ahead of time. (Could be -1
+ // due to areas/polygons)
+ var point_list = new Array(ways[i].nodes.length);
+
+ var poly = this.isWayArea(ways[i]) ? 1 : 0;
+ for (var j = 0; j < ways[i].nodes.length; j++) {
+ var node = nodes[ways[i].nodes[j]];
+
+ var point = new OpenLayers.Geometry.Point(node.lon, node.lat);
+
+ // Since OSM is topological, we stash the node ID internally.
+ point.osm_id = parseInt(ways[i].nodes[j]);
+ point_list[j] = point;
+
+ // We don't display nodes if they're used inside other
+ // elements.
+ node.used = true;
+ }
+ var geometry = null;
+ if (poly) {
+ geometry = new OpenLayers.Geometry.Polygon(
+ new OpenLayers.Geometry.LinearRing(point_list));
+ } else {
+ geometry = new OpenLayers.Geometry.LineString(point_list);
+ }
+ if (this.internalProjection && this.externalProjection) {
+ geometry.transform(this.externalProjection,
+ this.internalProjection);
+ }
+ var feat = new OpenLayers.Feature.Vector(geometry,
+ ways[i].tags);
+ feat.osm_id = parseInt(ways[i].id);
+ feat.fid = "way." + feat.osm_id;
+ feat_list[i] = feat;
+ }
+ for (var node_id in nodes) {
+ var node = nodes[node_id];
+ if (!node.used || this.checkTags) {
+ var tags = null;
+
+ if (this.checkTags) {
+ var result = this.getTags(node.node, true);
+ if (node.used && !result[1]) {
+ continue;
+ }
+ tags = result[0];
+ } else {
+ tags = this.getTags(node.node);
+ }
+
+ var feat = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(node['lon'], node['lat']),
+ tags);
+ if (this.internalProjection && this.externalProjection) {
+ feat.geometry.transform(this.externalProjection,
+ this.internalProjection);
+ }
+ feat.osm_id = parseInt(node_id);
+ feat.fid = "node." + feat.osm_id;
+ feat_list.push(feat);
+ }
+ // Memory cleanup
+ node.node = null;
+ }
+ return feat_list;
+ },
+
+ /**
+ * Method: getNodes
+ * Return the node items from a doc.
+ *
+ * Parameters:
+ * node - {DOMElement} node to parse tags from
+ */
+ getNodes: function(doc) {
+ var node_list = doc.getElementsByTagName("node");
+ var nodes = {};
+ for (var i = 0; i < node_list.length; i++) {
+ var node = node_list[i];
+ var id = node.getAttribute("id");
+ nodes[id] = {
+ 'lat': node.getAttribute("lat"),
+ 'lon': node.getAttribute("lon"),
+ 'node': node
+ };
+ }
+ return nodes;
+ },
+
+ /**
+ * Method: getWays
+ * Return the way items from a doc.
+ *
+ * Parameters:
+ * node - {DOMElement} node to parse tags from
+ */
+ getWays: function(doc) {
+ var way_list = doc.getElementsByTagName("way");
+ var return_ways = [];
+ for (var i = 0; i < way_list.length; i++) {
+ var way = way_list[i];
+ var way_object = {
+ id: way.getAttribute("id")
+ };
+
+ way_object.tags = this.getTags(way);
+
+ var node_list = way.getElementsByTagName("nd");
+
+ way_object.nodes = new Array(node_list.length);
+
+ for (var j = 0; j < node_list.length; j++) {
+ way_object.nodes[j] = node_list[j].getAttribute("ref");
+ }
+ return_ways.push(way_object);
+ }
+ return return_ways;
+
+ },
+
+ /**
+ * Method: getTags
+ * Return the tags list attached to a specific DOM element.
+ *
+ * Parameters:
+ * node - {DOMElement} node to parse tags from
+ * interesting_tags - {Boolean} whether the return from this function should
+ * return a boolean indicating that it has 'interesting tags' --
+ * tags like attribution and source are ignored. (To change the list
+ * of tags, see interestingTagsExclude)
+ *
+ * Returns:
+ * tags - {Object} hash of tags
+ * interesting - {Boolean} if interesting_tags is passed, returns
+ * whether there are any interesting tags on this element.
+ */
+ getTags: function(dom_node, interesting_tags) {
+ var tag_list = dom_node.getElementsByTagName("tag");
+ var tags = {};
+ var interesting = false;
+ for (var j = 0; j < tag_list.length; j++) {
+ var key = tag_list[j].getAttribute("k");
+ tags[key] = tag_list[j].getAttribute("v");
+ if (interesting_tags) {
+ if (!this.interestingTagsExclude[key]) {
+ interesting = true;
+ }
+ }
+ }
+ return interesting_tags ? [tags, interesting] : tags;
+ },
+
+ /**
+ * Method: isWayArea
+ * Given a way object from getWays, check whether the tags and geometry
+ * indicate something is an area.
+ *
+ * Returns:
+ * {Boolean}
+ */
+ isWayArea: function(way) {
+ var poly_shaped = false;
+ var poly_tags = false;
+
+ if (way.nodes[0] == way.nodes[way.nodes.length - 1]) {
+ poly_shaped = true;
+ }
+ if (this.checkTags) {
+ for(var key in way.tags) {
+ if (this.areaTags[key]) {
+ poly_tags = true;
+ break;
+ }
+ }
+ }
+ return poly_shaped && (this.checkTags ? poly_tags : true);
+ },
+
+ /**
+ * APIMethod: write
+ * Takes a list of features, returns a serialized OSM format file for use
+ * in tools like JOSM.
+ *
+ * Parameters:
+ * features - {Array(<OpenLayers.Feature.Vector>)}
+ */
+ write: function(features) {
+ if (!(features instanceof Array)) {
+ features = [features];
+ }
+
+ this.osm_id = 1;
+ this.created_nodes = {};
+ var root_node = this.createElementNS(null, "osm");
+ root_node.setAttribute("version", "0.5");
+ root_node.setAttribute("generator", "OpenLayers "+ OpenLayers.VERSION_NUMBER);
+
+ // Loop backwards, because the deserializer puts nodes last, and
+ // we want them first if possible
+ for(var i = features.length - 1; i >= 0; i--) {
+ var nodes = this.createFeatureNodes(features[i]);
+ for (var j = 0; j < nodes.length; j++) {
+ root_node.appendChild(nodes[j]);
+ }
+ }
+ return OpenLayers.Format.XML.prototype.write.apply(this, [root_node]);
+ },
+
+ /**
+ * Method: createFeatureNodes
+ * Takes a feature, returns a list of nodes from size 0->n.
+ * Will include all pieces of the serialization that are required which
+ * have not already been created. Calls out to createXML based on geometry
+ * type.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>}
+ */
+ createFeatureNodes: function(feature) {
+ var nodes = [];
+ var className = feature.geometry.CLASS_NAME;
+ var type = className.substring(className.lastIndexOf(".") + 1);
+ type = type.toLowerCase();
+ var builder = this.createXML[type];
+ if (builder) {
+ nodes = builder.apply(this, [feature]);
+ }
+ return nodes;
+ },
+
+ /**
+ * Method: createXML
+ * Takes a feature, returns a list of nodes from size 0->n.
+ * Will include all pieces of the serialization that are required which
+ * have not already been created.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>}
+ */
+ createXML: {
+ 'point': function(point) {
+ var id = null;
+ var geometry = point.geometry ? point.geometry : point;
+ var already_exists = false; // We don't return anything if the node
+ // has already been created
+ if (point.osm_id) {
+ id = point.osm_id;
+ if (this.created_nodes[id]) {
+ already_exists = true;
+ }
+ } else {
+ id = -this.osm_id;
+ this.osm_id++;
+ }
+ if (already_exists) {
+ node = this.created_nodes[id];
+ } else {
+ var node = this.createElementNS(null, "node");
+ }
+ this.created_nodes[id] = node;
+ node.setAttribute("id", id);
+ node.setAttribute("lon", geometry.x);
+ node.setAttribute("lat", geometry.y);
+ if (point.attributes) {
+ this.serializeTags(point, node);
+ }
+ this.setState(point, node);
+ return already_exists ? [] : [node];
+ },
+ linestring: function(feature) {
+ var nodes = [];
+ var geometry = feature.geometry;
+ if (feature.osm_id) {
+ id = feature.osm_id;
+ } else {
+ id = -this.osm_id;
+ this.osm_id++;
+ }
+ var way = this.createElementNS(null, "way");
+ way.setAttribute("id", id);
+ for (var i = 0; i < geometry.components.length; i++) {
+ var node = this.createXML['point'].apply(this, [geometry.components[i]]);
+ if (node.length) {
+ node = node[0];
+ var node_ref = node.getAttribute("id");
+ nodes.push(node);
+ } else {
+ node_ref = geometry.components[i].osm_id;
+ node = this.created_nodes[node_ref];
+ }
+ this.setState(feature, node);
+ var nd_dom = this.createElementNS(null, "nd");
+ nd_dom.setAttribute("ref", node_ref);
+ way.appendChild(nd_dom);
+ }
+ this.serializeTags(feature, way);
+ nodes.push(way);
+
+ return nodes;
+ },
+ polygon: function(feature) {
+ var attrs = OpenLayers.Util.extend({'area':'yes'}, feature.attributes);
+ var feat = new OpenLayers.Feature.Vector(feature.geometry.components[0], attrs);
+ feat.osm_id = feature.osm_id;
+ return this.createXML['linestring'].apply(this, [feat]);
+ }
+ },
+
+ /**
+ * Method: serializeTags
+ * Given a feature, serialize the attributes onto the given node.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>}
+ * node - {DOMNode}
+ */
+ serializeTags: function(feature, node) {
+ for (var key in feature.attributes) {
+ var tag = this.createElementNS(null, "tag");
+ tag.setAttribute("k", key);
+ tag.setAttribute("v", feature.attributes[key]);
+ node.appendChild(tag);
+ }
+ },
+
+ /**
+ * Method: setState
+ * OpenStreetMap has a convention that 'state' is stored for modification or deletion.
+ * This allows the file to be uploaded via JOSM or the bulk uploader tool.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>}
+ * node - {DOMNode}
+ */
+ setState: function(feature, node) {
+ if (feature.state) {
+ var state = null;
+ switch(feature.state) {
+ case OpenLayers.State.UPDATE:
+ state = "modify";
+ case OpenLayers.State.DELETE:
+ state = "delete";
+ }
+ if (state) {
+ node.setAttribute("action", state);
+ }
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Format.OSM"
+});
+/* ======================================================================
+ 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
+ ====================================================================== */
+
+/* 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/Point.js
+ * @requires OpenLayers/Geometry/Point.js
+ * @requires OpenLayers/Geometry/LineString.js
+ */
+
+/**
+ * Class: OpenLayers.Handler.Path
+ * Handler to draw a path on the map. Path is displayed on mouse down,
+ * moves on mouse move, and is finished on mouse up.
+ *
+ * Inherits from:
+ * - <OpenLayers.Handler.Point>
+ */
+OpenLayers.Handler.Path = OpenLayers.Class(OpenLayers.Handler.Point, {
+
+ /**
+ * Property: line
+ * {<OpenLayers.Feature.Vector>}
+ */
+ line: null,
+
+ /**
+ * Property: freehand
+ * {Boolean} In freehand mode, the handler starts the path on mouse down,
+ * adds a point for every mouse move, and finishes the path on mouse up.
+ * Outside of freehand mode, a point is added to the path on every mouse
+ * click and double-click finishes the path.
+ */
+ freehand: false,
+
+ /**
+ * Property: freehandToggle
+ * {String} If set, freehandToggle is checked on mouse events and will set
+ * the freehand mode to the opposite of this.freehand. To disallow
+ * toggling between freehand and non-freehand mode, set freehandToggle to
+ * null. Acceptable toggle values are 'shiftKey', 'ctrlKey', and 'altKey'.
+ */
+ freehandToggle: 'shiftKey',
+
+ /**
+ * Constructor: OpenLayers.Handler.Path
+ * Create a new path hander
+ *
+ * Parameters:
+ * control - {<OpenLayers.Control>}
+ * callbacks - {Object} An object with a 'done' property whos value is a
+ * function to be called when the path drawing is finished. The
+ * callback should expect to recieve a single argument, the line
+ * string geometry. If the callbacks object contains a 'point'
+ * property, this function will be sent each point as they are added.
+ * 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 optional object with properties to be set on the
+ * handler
+ */
+ initialize: function(control, callbacks, options) {
+ OpenLayers.Handler.Point.prototype.initialize.apply(this, arguments);
+ },
+
+ /**
+ * Method: createFeature
+ * Add temporary geometries
+ */
+ createFeature: function() {
+ this.line = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.LineString());
+ this.point = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point());
+ },
+
+ /**
+ * Method: destroyFeature
+ * Destroy temporary geometries
+ */
+ destroyFeature: function() {
+ OpenLayers.Handler.Point.prototype.destroyFeature.apply(this);
+ if(this.line) {
+ this.line.destroy();
+ }
+ this.line = null;
+ },
+
+ /**
+ * Method: addPoint
+ * Add point to geometry. Send the point index to override
+ * the behavior of LinearRing that disregards adding duplicate points.
+ */
+ addPoint: function() {
+ this.line.geometry.addComponent(this.point.geometry.clone(),
+ this.line.geometry.components.length);
+ this.callback("point", [this.point.geometry]);
+ },
+
+ /**
+ * Method: freehandMode
+ * Determine whether to behave in freehand mode or not.
+ *
+ * Returns:
+ * {Boolean}
+ */
+ freehandMode: function(evt) {
+ return (this.freehandToggle && evt[this.freehandToggle]) ?
+ !this.freehand : this.freehand;
+ },
+
+ /**
+ * Method: modifyFeature
+ * Modify the existing geometry given the new point
+ */
+ modifyFeature: function() {
+ var index = this.line.geometry.components.length - 1;
+ this.line.geometry.components[index].x = this.point.geometry.x;
+ this.line.geometry.components[index].y = this.point.geometry.y;
+ this.line.geometry.components[index].clearBounds();
+ },
+
+ /**
+ * Method: drawFeature
+ * Render geometries on the temporary layer.
+ */
+ drawFeature: function() {
+ this.layer.drawFeature(this.line, this.style);
+ this.layer.drawFeature(this.point, this.style);
+ },
+
+ /**
+ * Method: geometryClone
+ * Return a clone of the relevant geometry.
+ *
+ * Returns:
+ * {<OpenLayers.Geometry.LineString>}
+ */
+ geometryClone: function() {
+ return this.line.geometry.clone();
+ },
+
+ /**
+ * Method: mousedown
+ * Handle mouse down. Add a new point to the geometry and
+ * render it. Return determines whether to propagate the event on the map.
+ *
+ * Parameters:
+ * evt - {Event} The browser event
+ *
+ * Returns:
+ * {Boolean} Allow event propagation
+ */
+ mousedown: function(evt) {
+ // ignore double-clicks
+ if (this.lastDown && this.lastDown.equals(evt.xy)) {
+ return false;
+ }
+ if(this.lastDown == null) {
+ this.createFeature();
+ }
+ this.mouseDown = true;
+ this.lastDown = evt.xy;
+ var lonlat = this.control.map.getLonLatFromPixel(evt.xy);
+ this.point.geometry.x = lonlat.lon;
+ this.point.geometry.y = lonlat.lat;
+ if((this.lastUp == null) || !this.lastUp.equals(evt.xy)) {
+ this.addPoint();
+ }
+ this.drawFeature();
+ this.drawing = true;
+ return false;
+ },
+
+ /**
+ * Method: mousemove
+ * Handle mouse move. Adjust the geometry and redraw.
+ * Return determines whether to propagate the event on the map.
+ *
+ * Parameters:
+ * evt - {Event} The browser event
+ *
+ * Returns:
+ * {Boolean} Allow event propagation
+ */
+ mousemove: function (evt) {
+ if(this.drawing) {
+ var lonlat = this.map.getLonLatFromPixel(evt.xy);
+ this.point.geometry.x = lonlat.lon;
+ this.point.geometry.y = lonlat.lat;
+ this.point.geometry.clearBounds();
+ if(this.mouseDown && this.freehandMode(evt)) {
+ this.addPoint();
+ } else {
+ this.modifyFeature();
+ }
+ this.drawFeature();
+ }
+ return true;
+ },
+
+ /**
+ * Method: mouseup
+ * Handle mouse up. Send the latest point in the geometry to
+ * the control. Return determines whether to propagate the event on the map.
+ *
+ * Parameters:
+ * evt - {Event} The browser event
+ *
+ * Returns:
+ * {Boolean} Allow event propagation
+ */
+ mouseup: function (evt) {
+ this.mouseDown = false;
+ if(this.drawing) {
+ if(this.freehandMode(evt)) {
+ this.finalize();
+ } else {
+ if(this.lastUp == null) {
+ this.addPoint();
+ }
+ this.lastUp = evt.xy;
+ }
+ return false;
+ }
+ return true;
+ },
+
+ /**
+ * Method: dblclick
+ * Handle double-clicks. Finish the geometry and send it back
+ * to the control.
+ *
+ * Parameters:
+ * evt - {Event} The browser event
+ *
+ * Returns:
+ * {Boolean} Allow event propagation
+ */
+ dblclick: function(evt) {
+ if(!this.freehandMode(evt)) {
+ var index = this.line.geometry.components.length - 1;
+ this.line.geometry.removeComponent(this.line.geometry.components[index]);
+ this.finalize();
+ }
+ return false;
+ },
+
+ CLASS_NAME: "OpenLayers.Handler.Path"
+});
+/* ======================================================================
+ OpenLayers/Format/WFS.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/Format/GML.js
+ */
+
+/**
+ * Class: OpenLayers.Format.WFS
+ * Read/Write WFS.
+ */
+OpenLayers.Format.WFS = OpenLayers.Class(OpenLayers.Format.GML, {
+
+ /**
+ * Property: layer
+ */
+ layer: null,
+
+ /**
+ * APIProperty: wfsns
+ */
+ wfsns: "http://www.opengis.net/wfs",
+
+ /**
+ * Property: ogcns
+ */
+ ogcns: "http://www.opengis.net/ogc",
+
+ /*
+ * Constructor: OpenLayers.Format.WFS
+ * Create a WFS-T formatter. This requires a layer: that layer should
+ * have two properties: geometry_column and typename. The parser
+ * for this format is subclassed entirely from GML: There is a writer
+ * only, which uses most of the code from the GML layer, and wraps
+ * it in transactional elements.
+ *
+ * Parameters:
+ * options - {Object}
+ * layer - {<OpenLayers.Layer>}
+ */
+
+ initialize: function(options, layer) {
+ OpenLayers.Format.GML.prototype.initialize.apply(this, [options]);
+ this.layer = layer;
+ if (this.layer.featureNS) {
+ this.featureNS = this.layer.featureNS;
+ }
+ if (this.layer.options.geometry_column) {
+ this.geometryName = this.layer.options.geometry_column;
+ }
+ if (this.layer.options.typename) {
+ this.featureName = this.layer.options.typename;
+ }
+ },
+
+ /**
+ * Method: write
+ * Takes a feature list, and generates a WFS-T Transaction
+ *
+ * Parameters:
+ * features - {Array(<OpenLayers.Feature.Vector>)}
+ */
+ write: function(features) {
+
+ var transaction = this.createElementNS(this.wfsns, 'wfs:Transaction');
+ transaction.setAttribute("version","1.0.0");
+ transaction.setAttribute("service","WFS");
+ for (var i=0; i < features.length; i++) {
+ switch (features[i].state) {
+ case OpenLayers.State.INSERT:
+ transaction.appendChild(this.insert(features[i]));
+ break;
+ case OpenLayers.State.UPDATE:
+ transaction.appendChild(this.update(features[i]));
+ break;
+ case OpenLayers.State.DELETE:
+ transaction.appendChild(this.remove(features[i]));
+ break;
+ }
+ }
+
+ return OpenLayers.Format.XML.prototype.write.apply(this,[transaction]);
+ },
+
+ /**
+ * Method: createFeatureXML
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>}
+ */
+ createFeatureXML: function(feature) {
+ var geometryNode = this.buildGeometryNode(feature.geometry);
+ var geomContainer = this.createElementNS(this.featureNS, "feature:" + this.geometryName);
+ geomContainer.appendChild(geometryNode);
+ var featureContainer = this.createElementNS(this.featureNS, "feature:" + this.featureName);
+ featureContainer.appendChild(geomContainer);
+ for(var attr in feature.attributes) {
+ var attrText = this.createTextNode(feature.attributes[attr]);
+ var nodename = attr;
+ if (attr.search(":") != -1) {
+ nodename = attr.split(":")[1];
+ }
+ var attrContainer = this.createElementNS(this.featureNS, "feature:" + nodename);
+ attrContainer.appendChild(attrText);
+ featureContainer.appendChild(attrContainer);
+ }
+ return featureContainer;
+ },
+
+ /**
+ * Method: insert
+ * Takes a feature, and generates a WFS-T Transaction "Insert"
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>}
+ */
+ insert: function(feature) {
+ var insertNode = this.createElementNS(this.wfsns, 'wfs:Insert');
+ insertNode.appendChild(this.createFeatureXML(feature));
+ return insertNode;
+ },
+
+ /**
+ * Method: update
+ * Takes a feature, and generates a WFS-T Transaction "Update"
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>}
+ */
+ update: function(feature) {
+ if (!feature.fid) { OpenLayers.Console.userError(OpenLayers.i18n("noFID")); }
+ var updateNode = this.createElementNS(this.wfsns, 'wfs:Update');
+ updateNode.setAttribute("typeName", this.layerName);
+
+ var propertyNode = this.createElementNS(this.wfsns, 'wfs:Property');
+ var nameNode = this.createElementNS(this.wfsns, 'wfs:Name');
+
+ var txtNode = this.createTextNode(this.geometryName);
+ nameNode.appendChild(txtNode);
+ propertyNode.appendChild(nameNode);
+
+ var valueNode = this.createElementNS(this.wfsns, 'wfs:Value');
+
+ var geometryNode = this.buildGeometryNode(feature.geometry);
+
+ if(feature.layer){
+ geometryNode.setAttribute(
+ "srsName", feature.layer.projection.getCode()
+ );
+ }
+
+ valueNode.appendChild(geometryNode);
+
+ propertyNode.appendChild(valueNode);
+ updateNode.appendChild(propertyNode);
+
+ // add in attributes
+ for(var propName in feature.attributes) {
+ propertyNode = this.createElementNS(this.wfsns, 'wfs:Property');
+ nameNode = this.createElementNS(this.wfsns, 'wfs:Name');
+ nameNode.appendChild(this.createTextNode(propName));
+ propertyNode.appendChild(nameNode);
+ valueNode = this.createElementNS(this.wfsns, 'wfs:Value');
+ valueNode.appendChild(this.createTextNode(feature.attributes[propName]));
+ propertyNode.appendChild(valueNode);
+ updateNode.appendChild(propertyNode);
+ }
+
+
+ var filterNode = this.createElementNS(this.ogcns, 'ogc:Filter');
+ var filterIdNode = this.createElementNS(this.ogcns, 'ogc:FeatureId');
+ filterIdNode.setAttribute("fid", feature.fid);
+ filterNode.appendChild(filterIdNode);
+ updateNode.appendChild(filterNode);
+
+ return updateNode;
+ },
+
+ /**
+ * Method: remove
+ * Takes a feature, and generates a WFS-T Transaction "Delete"
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>}
+ */
+ remove: function(feature) {
+ if (!feature.fid) {
+ OpenLayers.Console.userError(OpenLayers.i18n("noFID"));
+ return false;
+ }
+ var deleteNode = this.createElementNS(this.wfsns, 'wfs:Delete');
+ deleteNode.setAttribute("typeName", this.layerName);
+
+ var filterNode = this.createElementNS(this.ogcns, 'ogc:Filter');
+ var filterIdNode = this.createElementNS(this.ogcns, 'ogc:FeatureId');
+ filterIdNode.setAttribute("fid", feature.fid);
+ filterNode.appendChild(filterIdNode);
+ deleteNode.appendChild(filterNode);
+
+ return deleteNode;
+ },
+
+ /**
+ * APIMethod: destroy
+ * Remove ciruclar ref to layer
+ */
+ destroy: function() {
+ this.layer = null;
+ },
+
+ CLASS_NAME: "OpenLayers.Format.WFS"
+});
+/* ======================================================================
+ OpenLayers/Handler/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/Handler/Path.js
+ * @requires OpenLayers/Geometry/Polygon.js
+ */
+
+/**
+ * Class: OpenLayers.Handler.Polygon
+ * Handler to draw a polygon on the map. Polygon is displayed on mouse down,
+ * moves on mouse move, and is finished on mouse up.
+ *
+ * Inherits from:
+ * - <OpenLayers.Handler.Path>
+ * - <OpenLayers.Handler>
+ */
+OpenLayers.Handler.Polygon = OpenLayers.Class(OpenLayers.Handler.Path, {
+
+ /**
+ * Parameter: polygon
+ * {<OpenLayers.Feature.Vector>}
+ */
+ polygon: null,
+
+ /**
+ * Constructor: OpenLayers.Handler.Polygon
+ * Create a Polygon Handler.
+ *
+ * Parameters:
+ * control - {<OpenLayers.Control>}
+ * callbacks - {Object} An object with a 'done' property whos value is
+ * a function to be called when the path drawing is
+ * finished. The callback should expect to recieve a
+ * single argument, the polygon geometry.
+ * If the callbacks object contains a 'point'
+ * property, this function will be sent each point
+ * as they are added. 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}
+ */
+ initialize: function(control, callbacks, options) {
+ OpenLayers.Handler.Path.prototype.initialize.apply(this, arguments);
+ },
+
+ /**
+ * Method: createFeature
+ * Add temporary geometries
+ */
+ createFeature: function() {
+ this.polygon = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Polygon());
+ this.line = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.LinearRing());
+ this.polygon.geometry.addComponent(this.line.geometry);
+ this.point = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point());
+ },
+
+ /**
+ * Method: destroyFeature
+ * Destroy temporary geometries
+ */
+ destroyFeature: function() {
+ OpenLayers.Handler.Path.prototype.destroyFeature.apply(this);
+ if(this.polygon) {
+ this.polygon.destroy();
+ }
+ this.polygon = null;
+ },
+
+ /**
+ * Method: modifyFeature
+ * Modify the existing geometry given the new point
+ *
+ */
+ modifyFeature: function() {
+ var index = this.line.geometry.components.length - 2;
+ this.line.geometry.components[index].x = this.point.geometry.x;
+ this.line.geometry.components[index].y = this.point.geometry.y;
+ this.line.geometry.components[index].clearBounds();
+ },
+
+ /**
+ * Method: drawFeature
+ * Render geometries on the temporary layer.
+ */
+ drawFeature: function() {
+ this.layer.drawFeature(this.polygon, this.style);
+ this.layer.drawFeature(this.point, this.style);
+ },
+
+ /**
+ * Method: geometryClone
+ * Return a clone of the relevant geometry.
+ *
+ * Returns:
+ * {<OpenLayers.Geometry.Polygon>}
+ */
+ geometryClone: function() {
+ return this.polygon.geometry.clone();
+ },
+
+ /**
+ * Method: dblclick
+ * Handle double-clicks. Finish the geometry and send it back
+ * to the control.
+ *
+ * Parameters:
+ * evt - {Event}
+ */
+ dblclick: function(evt) {
+ if(!this.freehandMode(evt)) {
+ // remove the penultimate point
+ var index = this.line.geometry.components.length - 2;
+ this.line.geometry.removeComponent(this.line.geometry.components[index]);
+ this.finalize();
+ }
+ return false;
+ },
+
+ CLASS_NAME: "OpenLayers.Handler.Polygon"
+});
+/* ======================================================================
+ OpenLayers/Control/EditingToolbar.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/Panel.js
+ * @requires OpenLayers/Control/Navigation.js
+ * @requires OpenLayers/Control/DrawFeature.js
+ * @requires OpenLayers/Handler/Point.js
+ * @requires OpenLayers/Handler/Path.js
+ * @requires OpenLayers/Handler/Polygon.js
+ */
+
+/**
+ * Class: OpenLayers.Control.EditingToolbar
+ */
+OpenLayers.Control.EditingToolbar = OpenLayers.Class(
+ OpenLayers.Control.Panel, {
+
+ /**
+ * Constructor: OpenLayers.Control.EditingToolbar
+ * Create an editing toolbar for a given layer.
+ *
+ * Parameters:
+ * layer - {<OpenLayers.Layer.Vector>}
+ * options - {Object}
+ */
+ initialize: function(layer, options) {
+ OpenLayers.Control.Panel.prototype.initialize.apply(this, [options]);
+
+ this.addControls(
+ [ new OpenLayers.Control.Navigation() ]
+ );
+ var controls = [
+ new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Point, {'displayClass': 'olControlDrawFeaturePoint'}),
+ new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Path, {'displayClass': 'olControlDrawFeaturePath'}),
+ new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Polygon, {'displayClass': 'olControlDrawFeaturePolygon'})
+ ];
+ for (var i=0, len=controls.length; i<len; i++) {
+ controls[i].featureAdded = function(feature) { feature.state = OpenLayers.State.INSERT; };
+ }
+ this.addControls(controls);
+ },
+
+ /**
+ * Method: draw
+ * calls the default draw, and then activates mouse defaults.
+ *
+ * Returns:
+ * {DOMElement}
+ */
+ draw: function() {
+ var div = OpenLayers.Control.Panel.prototype.draw.apply(this, arguments);
+ this.activateControl(this.controls[0]);
+ return div;
+ },
+
+ CLASS_NAME: "OpenLayers.Control.EditingToolbar"
+});
Modified: trunk/lib/OpenLayers/theme/default/style.css
===================================================================
--- trunk/lib/OpenLayers/theme/default/style.css 2008-09-05 14:31:06 UTC (rev 1498)
+++ trunk/lib/OpenLayers/theme/default/style.css 2008-09-05 14:39:19 UTC (rev 1499)
@@ -1,5 +1,15 @@
+div.olMap {
+ z-index: 0;
+ padding: 0px!important;
+ margin: 0px!important;
+}
+
+div.olMapViewport {
+ text-align: left;
+}
+
div.olLayerDiv {
- -moz-user-select: none
+ -moz-user-select: none;
}
.olLayerGoogleCopyright {
@@ -30,15 +40,22 @@
font-size: xx-small;
}
.olControlScaleLineBottom {
- border: solid 2px black;
- border-bottom: none;
- margin-top:-2px;
- text-align: center;
+ background-color: #000;
+ border: 1px solid #fff;
+ margin: 1px;
+ font-size: 9px;
+ padding-left: 2px;
+ line-height: 12px;
+ color: #fff;
}
.olControlScaleLineTop {
- border: solid 2px black;
- border-top: none;
- text-align: center;
+ background-color: #000;
+ border: 1px solid #fff;
+ margin: 1px;
+ font-size: 9px;
+ padding-left: 2px;
+ line-height: 12px;
+ color: #fff;
}
.olControlPermalink {
@@ -130,29 +147,24 @@
position: relative;
}
-.olControlNavigationHistoryPreviousItemActive {
- background-image: url("img/view_previous_on.png");
+.olControlNavigationHistory {
+ background-image: url("img/navigation_history.png");
background-repeat: no-repeat;
width: 24px;
height: 24px;
+
}
+.olControlNavigationHistoryPreviousItemActive {
+ background-position: 0px 0px;
+}
.olControlNavigationHistoryPreviousItemInactive {
- background-image: url("img/view_previous_off.png");
- background-repeat: no-repeat;
- width: 24px;
- height: 24px;
+ background-position: 0px -24px;
}
.olControlNavigationHistoryNextItemActive {
- background-image: url("img/view_next_on.png");
- background-repeat: no-repeat;
- width: 24px;
- height: 24px;
+ background-position: -24px 0px;
}
.olControlNavigationHistoryNextItemInactive {
- background-image: url("img/view_next_off.png");
- background-repeat: no-repeat;
- width: 24px;
- height: 24px;
+ background-position: -24px -24px;
}
.olControlNavToolbar .olControlNavigationItemActive {
@@ -179,51 +191,47 @@
width: 200px;
}
.olControlEditingToolbar div {
+ background-image: url("img/editing_tool_bar.png");
+ background-repeat: no-repeat;
float:right;
width: 24px;
height: 24px;
margin: 5px;
}
.olControlEditingToolbar .olControlNavigationItemActive {
- background-image: url("img/editing_tool_bar.png");
- background-repeat: no-repeat;
background-position: -103px -23px;
}
.olControlEditingToolbar .olControlNavigationItemInactive {
- background-image: url("img/editing_tool_bar.png");
- background-repeat: no-repeat;
background-position: -103px -0px;
}
.olControlEditingToolbar .olControlDrawFeaturePointItemActive {
- background-image: url("img/editing_tool_bar.png");
- background-repeat: no-repeat;
background-position: -77px -23px;
}
.olControlEditingToolbar .olControlDrawFeaturePointItemInactive {
- background-image: url("img/editing_tool_bar.png");
- background-repeat: no-repeat;
background-position: -77px -0px;
}
.olControlEditingToolbar .olControlDrawFeaturePathItemInactive {
- background-image: url("img/editing_tool_bar.png");
- background-repeat: no-repeat;
background-position: -51px 0px;
}
.olControlEditingToolbar .olControlDrawFeaturePathItemActive {
- background-image: url("img/editing_tool_bar.png");
- background-repeat: no-repeat;
background-position: -51px -23px;
}
.olControlEditingToolbar .olControlDrawFeaturePolygonItemInactive {
- background-image: url("img/editing_tool_bar.png");
- background-repeat: no-repeat;
background-position: -26px 0px;
}
.olControlEditingToolbar .olControlDrawFeaturePolygonItemActive {
- background-image: url("img/editing_tool_bar.png");
- background-repeat: no-repeat;
background-position: -26px -23px ;
}
+.olControlSaveFeaturesItemActive {
+ background-image: url(img/save_features_on.png);
+ background-repeat: no-repeat;
+ background-position: 0px 1px;
+}
+.olControlSaveFeaturesItemInactive {
+ background-image: url(img/save_features_off.png);
+ background-repeat: no-repeat;
+ background-position: 0px 1px;
+}
.olHandlerBoxZoomBox {
border: 2px solid red;
@@ -232,6 +240,14 @@
opacity: 0.50;
font-size: 1px;
filter: alpha(opacity=50);
+}
+.olHandlerBoxSelectFeature {
+ border: 2px solid blue;
+ position: absolute;
+ background-color: white;
+ opacity: 0.50;
+ font-size: 1px;
+ filter: alpha(opacity=50);
}
/*
More information about the fusion-commits
mailing list