diff options
Diffstat (limited to 'misc/openlayers/OpenLayers.light.debug.js')
-rw-r--r-- | misc/openlayers/OpenLayers.light.debug.js | 37230 |
1 files changed, 0 insertions, 37230 deletions
diff --git a/misc/openlayers/OpenLayers.light.debug.js b/misc/openlayers/OpenLayers.light.debug.js deleted file mode 100644 index 536a033..0000000 --- a/misc/openlayers/OpenLayers.light.debug.js +++ /dev/null @@ -1,37230 +0,0 @@ -/* - - OpenLayers.js -- OpenLayers Map Viewer Library - - Copyright (c) 2006-2013 by OpenLayers Contributors - Published under the 2-clause BSD license. - See http://openlayers.org/dev/license.txt for the full text of the license, and http://openlayers.org/dev/authors.txt for full list of contributors. - - Includes compressed code under the following licenses: - - (For uncompressed versions of the code used, please see the - OpenLayers Github repository: <https://github.com/openlayers/openlayers>) - -*/ - -/** - * 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.Util.pagePosition is based on Yahoo's getXY method, which is - * Copyright (c) 2006, Yahoo! Inc. - * All rights reserved. - * - * Redistribution and use of this software in source and binary forms, with or - * without modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * * Neither the name of Yahoo! Inc. nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission of Yahoo! Inc. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -/* ====================================================================== - OpenLayers/SingleFile.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -var OpenLayers = { - /** - * Constant: VERSION_NUMBER - */ - VERSION_NUMBER: "Release 2.13.1", - - /** - * Constant: singleFile - * TODO: remove this in 3.0 when we stop supporting build profiles that - * include OpenLayers.js - */ - singleFile: true, - - /** - * Method: _getScriptLocation - * Return the path to this script. This is also implemented in - * OpenLayers.js - * - * Returns: - * {String} Path to this script - */ - _getScriptLocation: (function() { - var r = new RegExp("(^|(.*?\\/))(OpenLayers[^\\/]*?\\.js)(\\?|$)"), - s = document.getElementsByTagName('script'), - src, m, l = ""; - for(var i=0, len=s.length; i<len; i++) { - src = s[i].getAttribute('src'); - if(src) { - m = src.match(r); - if(m) { - l = m[1]; - break; - } - } - } - return (function() { return l; }); - })(), - - /** - * Property: ImgPath - * {String} Set this to the path where control images are stored, a path - * given here must end with a slash. If set to '' (which is the default) - * OpenLayers will use its script location + "img/". - * - * You will need to set this property when you have a singlefile build of - * OpenLayers that either is not named "OpenLayers.js" or if you move - * the file in a way such that the image directory cannot be derived from - * the script location. - * - * If your custom OpenLayers build is named "my-custom-ol.js" and the images - * of OpenLayers are in a folder "/resources/external/images/ol" a correct - * way of including OpenLayers in your HTML would be: - * - * (code) - * <script src="/path/to/my-custom-ol.js" type="text/javascript"></script> - * <script type="text/javascript"> - * // tell OpenLayers where the control images are - * // remember the trailing slash - * OpenLayers.ImgPath = "/resources/external/images/ol/"; - * </script> - * (end code) - * - * Please remember that when your OpenLayers script is not named - * "OpenLayers.js" you will have to make sure that the default theme is - * loaded into the page by including an appropriate <link>-tag, - * e.g.: - * - * (code) - * <link rel="stylesheet" href="/path/to/default/style.css" type="text/css"> - * (end code) - */ - ImgPath : '' -}; -/* ====================================================================== - OpenLayers/BaseTypes.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @requires OpenLayers/SingleFile.js - */ - -/** - * Header: OpenLayers Base Types - * OpenLayers custom string, number and function functions are described here. - */ - -/** - * Namespace: OpenLayers.String - * Contains convenience functions for string manipulation. - */ -OpenLayers.String = { - - /** - * APIFunction: startsWith - * Test whether a string starts with another string. - * - * Parameters: - * str - {String} The string to test. - * sub - {String} The substring to look for. - * - * Returns: - * {Boolean} The first string starts with the second. - */ - startsWith: function(str, sub) { - return (str.indexOf(sub) == 0); - }, - - /** - * APIFunction: contains - * Test whether a string contains another string. - * - * Parameters: - * str - {String} The string to test. - * sub - {String} The substring to look for. - * - * Returns: - * {Boolean} The first string contains the second. - */ - contains: function(str, sub) { - return (str.indexOf(sub) != -1); - }, - - /** - * APIFunction: trim - * Removes leading and trailing whitespace characters from a string. - * - * Parameters: - * str - {String} The (potentially) space padded string. This string is not - * modified. - * - * Returns: - * {String} A trimmed version of the string with all leading and - * trailing spaces removed. - */ - trim: function(str) { - return str.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); - }, - - /** - * APIFunction: camelize - * Camel-case a hyphenated string. - * Ex. "chicken-head" becomes "chickenHead", and - * "-chicken-head" becomes "ChickenHead". - * - * Parameters: - * str - {String} The string to be camelized. The original is not modified. - * - * Returns: - * {String} The string, camelized - */ - 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; - }, - - /** - * APIFunction: format - * Given a string with tokens in the form ${token}, return a string - * with tokens replaced with properties from the given context - * object. Represent a literal "${" by doubling it, e.g. "${${". - * - * Parameters: - * template - {String} A string with tokens to be replaced. A template - * has the form "literal ${token}" where the token will be replaced - * by the value of context["token"]. - * context - {Object} An optional object with properties corresponding - * to the tokens in the format string. If no context is sent, the - * window object will be used. - * args - {Array} Optional arguments to pass to any functions found in - * the context. If a context property is a function, the token - * will be replaced by the return from the function called with - * these arguments. - * - * Returns: - * {String} A string with tokens replaced from the context object. - */ - format: function(template, context, args) { - if(!context) { - context = window; - } - - // Example matching: - // str = ${foo.bar} - // match = foo.bar - var replacer = function(str, match) { - var replacement; - - // Loop through all subs. Example: ${a.b.c} - // 0 -> replacement = context[a]; - // 1 -> replacement = context[a][b]; - // 2 -> replacement = context[a][b][c]; - var subs = match.split(/\.+/); - for (var i=0; i< subs.length; i++) { - if (i == 0) { - replacement = context; - } - if (replacement === undefined) { - break; - } - replacement = replacement[subs[i]]; - } - - if(typeof replacement == "function") { - replacement = args ? - replacement.apply(null, args) : - replacement(); - } - - // If replacement is undefined, return the string 'undefined'. - // This is a workaround for a bugs in browsers not properly - // dealing with non-participating groups in regular expressions: - // http://blog.stevenlevithan.com/archives/npcg-javascript - if (typeof replacement == 'undefined') { - return 'undefined'; - } else { - return replacement; - } - }; - - return template.replace(OpenLayers.String.tokenRegEx, replacer); - }, - - /** - * Property: tokenRegEx - * Used to find tokens in a string. - * Examples: ${a}, ${a.b.c}, ${a-b}, ${5} - */ - tokenRegEx: /\$\{([\w.]+?)\}/g, - - /** - * Property: numberRegEx - * Used to test strings as numbers. - */ - numberRegEx: /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/, - - /** - * APIFunction: 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); - }, - - /** - * APIFunction: numericIf - * Converts a string that appears to be a numeric value into a number. - * - * Parameters: - * value - {String} - * trimWhitespace - {Boolean} - * - * Returns: - * {Number|String} a Number if the passed value is a number, a String - * otherwise. - */ - numericIf: function(value, trimWhitespace) { - var originalValue = value; - if (trimWhitespace === true && value != null && value.replace) { - value = value.replace(/^\s*|\s*$/g, ""); - } - return OpenLayers.String.isNumeric(value) ? parseFloat(value) : originalValue; - } - -}; - -/** - * Namespace: OpenLayers.Number - * Contains convenience functions for manipulating numbers. - */ -OpenLayers.Number = { - - /** - * Property: decimalSeparator - * Decimal separator to use when formatting numbers. - */ - decimalSeparator: ".", - - /** - * Property: thousandsSeparator - * Thousands separator to use when formatting numbers. - */ - thousandsSeparator: ",", - - /** - * APIFunction: limitSigDigs - * Limit the number of significant digits on a float. - * - * Parameters: - * num - {Float} - * sig - {Integer} - * - * Returns: - * {Float} The number, rounded to the specified number of significant - * digits. - */ - limitSigDigs: function(num, sig) { - var fig = 0; - if (sig > 0) { - fig = parseFloat(num.toPrecision(sig)); - } - return fig; - }, - - /** - * APIFunction: format - * Formats a number for output. - * - * Parameters: - * num - {Float} - * dec - {Integer} Number of decimal places to round to. - * Defaults to 0. Set to null to leave decimal places unchanged. - * tsep - {String} Thousands separator. - * Default is ",". - * dsep - {String} Decimal separator. - * Default is ".". - * - * Returns: - * {String} A string representing the formatted number. - */ - 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) { - // integer where we do not want to touch the decimals - 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; - }, - - /** - * Method: zeroPad - * Create a zero padded string optionally with a radix for casting numbers. - * - * Parameters: - * num - {Number} The number to be zero padded. - * len - {Number} The length of the string to be returned. - * radix - {Number} An integer between 2 and 36 specifying the base to use - * for representing numeric values. - */ - zeroPad: function(num, len, radix) { - var str = num.toString(radix || 10); - while (str.length < len) { - str = "0" + str; - } - return str; - } -}; - -/** - * Namespace: OpenLayers.Function - * Contains convenience functions for function manipulation. - */ -OpenLayers.Function = { - /** - * APIFunction: bind - * Bind a function to an object. Method to easily create closures with - * 'this' altered. - * - * Parameters: - * func - {Function} Input function. - * object - {Object} The object to bind to the input function (as this). - * - * Returns: - * {Function} A closure with 'this' set to the passed in object. - */ - bind: function(func, object) { - // create a reference to all arguments past the second one - var args = Array.prototype.slice.apply(arguments, [2]); - return function() { - // Push on any additional arguments from the actual function call. - // These will come after those sent to the bind call. - var newArgs = args.concat( - Array.prototype.slice.apply(arguments, [0]) - ); - return func.apply(object, newArgs); - }; - }, - - /** - * APIFunction: bindAsEventListener - * Bind a function to an object, and configure it to receive the event - * object as first parameter when called. - * - * Parameters: - * func - {Function} Input function to serve as an event listener. - * object - {Object} A reference to this. - * - * Returns: - * {Function} - */ - bindAsEventListener: function(func, object) { - return function(event) { - return func.call(object, event || window.event); - }; - }, - - /** - * APIFunction: False - * A simple function to that just does "return false". We use this to - * avoid attaching anonymous functions to DOM event handlers, which - * causes "issues" on IE<8. - * - * Usage: - * document.onclick = OpenLayers.Function.False; - * - * Returns: - * {Boolean} - */ - False : function() { - return false; - }, - - /** - * APIFunction: True - * A simple function to that just does "return true". We use this to - * avoid attaching anonymous functions to DOM event handlers, which - * causes "issues" on IE<8. - * - * Usage: - * document.onclick = OpenLayers.Function.True; - * - * Returns: - * {Boolean} - */ - True : function() { - return true; - }, - - /** - * APIFunction: Void - * A reusable function that returns ``undefined``. - * - * Returns: - * {undefined} - */ - Void: function() {} - -}; - -/** - * Namespace: OpenLayers.Array - * Contains convenience functions for array manipulation. - */ -OpenLayers.Array = { - - /** - * APIMethod: filter - * Filter an array. Provides the functionality of the - * Array.prototype.filter extension to the ECMA-262 standard. Where - * available, Array.prototype.filter will be used. - * - * Based on well known example from http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/filter - * - * Parameters: - * array - {Array} The array to be filtered. This array is not mutated. - * Elements added to this array by the callback will not be visited. - * callback - {Function} A function that is called for each element in - * the array. If this function returns true, the element will be - * included in the return. The function will be called with three - * arguments: the element in the array, the index of that element, and - * the array itself. If the optional caller parameter is specified - * the callback will be called with this set to caller. - * caller - {Object} Optional object to be set as this when the callback - * is called. - * - * Returns: - * {Array} An array of elements from the passed in array for which the - * callback returns true. - */ - 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/BaseTypes/Class.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @requires OpenLayers/SingleFile.js - */ - -/** - * Constructor: OpenLayers.Class - * Base class used to construct all other classes. Includes support for - * multiple inheritance. - * - * This constructor is new in OpenLayers 2.5. At OpenLayers 3.0, the old - * syntax for creating classes and dealing with inheritance - * will be removed. - * - * To create a new OpenLayers-style class, use the following syntax: - * (code) - * var MyClass = OpenLayers.Class(prototype); - * (end) - * - * To create a new OpenLayers-style class with multiple inheritance, use the - * following syntax: - * (code) - * var MyClass = OpenLayers.Class(Class1, Class2, prototype); - * (end) - * - * Note that instanceof reflection will only reveal Class1 as superclass. - * - */ -OpenLayers.Class = function() { - var len = arguments.length; - var P = arguments[0]; - var F = arguments[len-1]; - - var C = typeof F.initialize == "function" ? - F.initialize : - function(){ P.prototype.initialize.apply(this, arguments); }; - - if (len > 1) { - var newArgs = [C, P].concat( - Array.prototype.slice.call(arguments).slice(1, len-1), F); - OpenLayers.inherit.apply(null, newArgs); - } else { - C.prototype = F; - } - return C; -}; - -/** - * Function: OpenLayers.inherit - * - * Parameters: - * C - {Object} the class that inherits - * P - {Object} the superclass to inherit from - * - * In addition to the mandatory C and P parameters, an arbitrary number of - * objects can be passed, which will extend C. - */ -OpenLayers.inherit = function(C, P) { - var F = function() {}; - F.prototype = P.prototype; - C.prototype = new F; - var i, l, o; - for(i=2, l=arguments.length; i<l; i++) { - o = arguments[i]; - if(typeof o === "function") { - o = o.prototype; - } - OpenLayers.Util.extend(C.prototype, o); - } -}; - -/** - * APIFunction: extend - * Copy all properties of a source object to a destination object. Modifies - * the passed in destination object. Any properties on the source object - * that are set to undefined will not be (re)set on the destination object. - * - * Parameters: - * destination - {Object} The object that will be modified - * source - {Object} The object with properties to be set on the destination - * - * Returns: - * {Object} The destination object. - */ -OpenLayers.Util = OpenLayers.Util || {}; -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; - } - } - - /** - * IE doesn't include the toString property when iterating over an object's - * properties with the for(property in object) syntax. Explicitly check if - * the source has its own toString property. - */ - - /* - * FF/Windows < 2.0.0.13 reports "Illegal operation on WrappedNative - * prototype object" when calling hawOwnProperty if the source object - * is an instance of window.Event. - */ - - var sourceIsEvt = typeof window.Event == "function" - && source instanceof window.Event; - - if (!sourceIsEvt - && source.hasOwnProperty && source.hasOwnProperty("toString")) { - destination.toString = source.toString; - } - } - return destination; -}; -/* ====================================================================== - OpenLayers/BaseTypes/Bounds.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @requires OpenLayers/BaseTypes/Class.js - */ - -/** - * Class: OpenLayers.Bounds - * Instances of this class represent bounding boxes. Data stored as left, - * bottom, right, top floats. All values are initialized to null, however, - * you should make sure you set them before using the bounds for anything. - * - * Possible use case: - * (code) - * bounds = new OpenLayers.Bounds(); - * bounds.extend(new OpenLayers.LonLat(4,5)); - * bounds.extend(new OpenLayers.LonLat(5,6)); - * bounds.toBBOX(); // returns 4,5,5,6 - * (end) - */ -OpenLayers.Bounds = OpenLayers.Class({ - - /** - * Property: left - * {Number} Minimum horizontal coordinate. - */ - left: null, - - /** - * Property: bottom - * {Number} Minimum vertical coordinate. - */ - bottom: null, - - /** - * Property: right - * {Number} Maximum horizontal coordinate. - */ - right: null, - - /** - * Property: top - * {Number} Maximum vertical coordinate. - */ - top: null, - - /** - * Property: centerLonLat - * {<OpenLayers.LonLat>} A cached center location. This should not be - * accessed directly. Use <getCenterLonLat> instead. - */ - centerLonLat: null, - - /** - * Constructor: OpenLayers.Bounds - * Construct a new bounds object. Coordinates can either be passed as four - * arguments, or as a single argument. - * - * Parameters (four arguments): - * left - {Number} The left bounds of the box. Note that for width - * calculations, this is assumed to be less than the right value. - * bottom - {Number} The bottom bounds of the box. Note that for height - * calculations, this is assumed to be less than the top value. - * right - {Number} The right bounds. - * top - {Number} The top bounds. - * - * Parameters (single argument): - * bounds - {Array(Number)} [left, bottom, right, top] - */ - initialize: function(left, bottom, right, top) { - if (OpenLayers.Util.isArray(left)) { - top = left[3]; - right = left[2]; - bottom = left[1]; - left = left[0]; - } - if (left != null) { - this.left = OpenLayers.Util.toFloat(left); - } - if (bottom != null) { - this.bottom = OpenLayers.Util.toFloat(bottom); - } - if (right != null) { - this.right = OpenLayers.Util.toFloat(right); - } - if (top != null) { - this.top = OpenLayers.Util.toFloat(top); - } - }, - - /** - * Method: clone - * Create a cloned instance of this bounds. - * - * Returns: - * {<OpenLayers.Bounds>} A fresh copy of the bounds - */ - clone:function() { - return new OpenLayers.Bounds(this.left, this.bottom, - this.right, this.top); - }, - - /** - * Method: equals - * Test a two bounds for equivalence. - * - * Parameters: - * bounds - {<OpenLayers.Bounds>} - * - * Returns: - * {Boolean} The passed-in bounds object has the same left, - * right, top, bottom components as this. Note that if bounds - * passed in is null, returns false. - */ - equals:function(bounds) { - var equals = false; - if (bounds != null) { - equals = ((this.left == bounds.left) && - (this.right == bounds.right) && - (this.top == bounds.top) && - (this.bottom == bounds.bottom)); - } - return equals; - }, - - /** - * APIMethod: toString - * Returns a string representation of the bounds object. - * - * Returns: - * {String} String representation of bounds object. - */ - toString:function() { - return [this.left, this.bottom, this.right, this.top].join(","); - }, - - /** - * APIMethod: toArray - * Returns an array representation of the bounds object. - * - * Returns an array of left, bottom, right, top properties, or -- when the - * optional parameter is true -- an array of the bottom, left, top, - * right properties. - * - * Parameters: - * reverseAxisOrder - {Boolean} Should we reverse the axis order? - * - * Returns: - * {Array} array of left, bottom, right, top - */ - toArray: function(reverseAxisOrder) { - if (reverseAxisOrder === true) { - return [this.bottom, this.left, this.top, this.right]; - } else { - return [this.left, this.bottom, this.right, this.top]; - } - }, - - /** - * APIMethod: toBBOX - * Returns a boundingbox-string representation of the bounds object. - * - * Parameters: - * decimal - {Integer} How many significant digits in the bbox coords? - * Default is 6 - * reverseAxisOrder - {Boolean} Should we reverse the axis order? - * - * Returns: - * {String} Simple String representation of bounds object. - * (e.g. "5,42,10,45") - */ - toBBOX:function(decimal, reverseAxisOrder) { - if (decimal== null) { - decimal = 6; - } - var mult = Math.pow(10, decimal); - var xmin = Math.round(this.left * mult) / mult; - var ymin = Math.round(this.bottom * mult) / mult; - var xmax = Math.round(this.right * mult) / mult; - var ymax = Math.round(this.top * mult) / mult; - if (reverseAxisOrder === true) { - return ymin + "," + xmin + "," + ymax + "," + xmax; - } else { - return xmin + "," + ymin + "," + xmax + "," + ymax; - } - }, - - /** - * APIMethod: toGeometry - * Create a new polygon geometry based on this bounds. - * - * Returns: - * {<OpenLayers.Geometry.Polygon>} A new polygon with the coordinates - * of this bounds. - */ - 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) - ]) - ]); - }, - - /** - * APIMethod: getWidth - * Returns the width of the bounds. - * - * Returns: - * {Float} The width of the bounds (right minus left). - */ - getWidth:function() { - return (this.right - this.left); - }, - - /** - * APIMethod: getHeight - * Returns the height of the bounds. - * - * Returns: - * {Float} The height of the bounds (top minus bottom). - */ - getHeight:function() { - return (this.top - this.bottom); - }, - - /** - * APIMethod: getSize - * Returns an <OpenLayers.Size> object of the bounds. - * - * Returns: - * {<OpenLayers.Size>} The size of the bounds. - */ - getSize:function() { - return new OpenLayers.Size(this.getWidth(), this.getHeight()); - }, - - /** - * APIMethod: getCenterPixel - * Returns the <OpenLayers.Pixel> object which represents the center of the - * bounds. - * - * Returns: - * {<OpenLayers.Pixel>} The center of the bounds in pixel space. - */ - getCenterPixel:function() { - return new OpenLayers.Pixel( (this.left + this.right) / 2, - (this.bottom + this.top) / 2); - }, - - /** - * APIMethod: getCenterLonLat - * Returns the <OpenLayers.LonLat> object which represents the center of the - * bounds. - * - * Returns: - * {<OpenLayers.LonLat>} The center of the bounds in map space. - */ - getCenterLonLat:function() { - if(!this.centerLonLat) { - this.centerLonLat = new OpenLayers.LonLat( - (this.left + this.right) / 2, (this.bottom + this.top) / 2 - ); - } - return this.centerLonLat; - }, - - /** - * APIMethod: 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.Bounds>} A new bounds that is scaled by ratio - * from origin. - */ - scale: function(ratio, origin){ - if(origin == null){ - origin = this.getCenterLonLat(); - } - - 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 - * Shifts the coordinates of the bound by the given horizontal and vertical - * deltas. - * - * (start code) - * var bounds = new OpenLayers.Bounds(0, 0, 10, 10); - * bounds.toString(); - * // => "0,0,10,10" - * - * bounds.add(-1.5, 4).toString(); - * // => "-1.5,4,8.5,14" - * (end) - * - * This method will throw a TypeError if it is passed null as an argument. - * - * Parameters: - * x - {Float} horizontal delta - * y - {Float} vertical delta - * - * Returns: - * {<OpenLayers.Bounds>} A new bounds whose coordinates are the same as - * this, but shifted by the passed-in x and y values. - */ - add:function(x, y) { - if ( (x == null) || (y == null) ) { - throw new TypeError('Bounds.add cannot receive null values'); - } - return new OpenLayers.Bounds(this.left + x, this.bottom + y, - this.right + x, this.top + y); - }, - - /** - * APIMethod: extend - * Extend the bounds to include the <OpenLayers.LonLat>, - * <OpenLayers.Geometry.Point> or <OpenLayers.Bounds> specified. - * - * Please note that this function assumes that left < right and - * bottom < top. - * - * Parameters: - * object - {<OpenLayers.LonLat>, <OpenLayers.Geometry.Point> or - * <OpenLayers.Bounds>} The object to be included in the new bounds - * object. - */ - extend:function(object) { - if (object) { - switch(object.CLASS_NAME) { - case "OpenLayers.LonLat": - this.extendXY(object.lon, object.lat); - break; - case "OpenLayers.Geometry.Point": - this.extendXY(object.x, object.y); - break; - - case "OpenLayers.Bounds": - // clear cached center location - this.centerLonLat = null; - - if ( (this.left == null) || (object.left < this.left)) { - this.left = object.left; - } - if ( (this.bottom == null) || (object.bottom < this.bottom) ) { - this.bottom = object.bottom; - } - if ( (this.right == null) || (object.right > this.right) ) { - this.right = object.right; - } - if ( (this.top == null) || (object.top > this.top) ) { - this.top = object.top; - } - break; - } - } - }, - - /** - * APIMethod: extendXY - * Extend the bounds to include the XY coordinate specified. - * - * Parameters: - * x - {number} The X part of the the coordinate. - * y - {number} The Y part of the the coordinate. - */ - extendXY:function(x, y) { - // clear cached center location - this.centerLonLat = null; - - if ((this.left == null) || (x < this.left)) { - this.left = x; - } - if ((this.bottom == null) || (y < this.bottom)) { - this.bottom = y; - } - if ((this.right == null) || (x > this.right)) { - this.right = x; - } - if ((this.top == null) || (y > this.top)) { - this.top = y; - } - }, - - /** - * APIMethod: containsLonLat - * Returns whether the bounds object contains the given <OpenLayers.LonLat>. - * - * Parameters: - * ll - {<OpenLayers.LonLat>|Object} OpenLayers.LonLat or an - * object with a 'lon' and 'lat' properties. - * options - {Object} Optional parameters - * - * Acceptable options: - * inclusive - {Boolean} Whether or not to include the border. - * Default is true. - * worldBounds - {<OpenLayers.Bounds>} If a worldBounds is provided, the - * ll will be considered as contained if it exceeds the world bounds, - * but can be wrapped around the dateline so it is contained by this - * bounds. - * - * Returns: - * {Boolean} The passed-in lonlat is within this bounds. - */ - containsLonLat: function(ll, options) { - if (typeof options === "boolean") { - options = {inclusive: options}; - } - options = options || {}; - var contains = this.contains(ll.lon, ll.lat, options.inclusive), - worldBounds = options.worldBounds; - if (worldBounds && !contains) { - var worldWidth = worldBounds.getWidth(); - var worldCenterX = (worldBounds.left + worldBounds.right) / 2; - var worldsAway = Math.round((ll.lon - worldCenterX) / worldWidth); - contains = this.containsLonLat({ - lon: ll.lon - worldsAway * worldWidth, - lat: ll.lat - }, {inclusive: options.inclusive}); - } - return contains; - }, - - /** - * APIMethod: containsPixel - * Returns whether the bounds object contains the given <OpenLayers.Pixel>. - * - * Parameters: - * px - {<OpenLayers.Pixel>} - * inclusive - {Boolean} Whether or not to include the border. Default is - * true. - * - * Returns: - * {Boolean} The passed-in pixel is within this bounds. - */ - containsPixel:function(px, inclusive) { - return this.contains(px.x, px.y, inclusive); - }, - - /** - * APIMethod: contains - * Returns whether the bounds object contains the given x and y. - * - * Parameters: - * x - {Float} - * y - {Float} - * inclusive - {Boolean} Whether or not to include the border. Default is - * true. - * - * Returns: - * {Boolean} Whether or not the passed-in coordinates are within this - * bounds. - */ - contains:function(x, y, inclusive) { - //set default - if (inclusive == null) { - inclusive = true; - } - - if (x == null || y == null) { - return false; - } - - x = OpenLayers.Util.toFloat(x); - y = OpenLayers.Util.toFloat(y); - - var contains = false; - if (inclusive) { - contains = ((x >= this.left) && (x <= this.right) && - (y >= this.bottom) && (y <= this.top)); - } else { - contains = ((x > this.left) && (x < this.right) && - (y > this.bottom) && (y < this.top)); - } - return contains; - }, - - /** - * APIMethod: intersectsBounds - * Determine whether the target bounds intersects this bounds. Bounds are - * considered intersecting if any of their edges intersect or if one - * bounds contains the other. - * - * Parameters: - * bounds - {<OpenLayers.Bounds>} The target bounds. - * options - {Object} Optional parameters. - * - * Acceptable options: - * inclusive - {Boolean} Treat coincident borders as intersecting. Default - * is true. If false, bounds that do not overlap but only touch at the - * border will not be considered as intersecting. - * worldBounds - {<OpenLayers.Bounds>} If a worldBounds is provided, two - * bounds will be considered as intersecting if they intersect when - * shifted to within the world bounds. This applies only to bounds that - * cross or are completely outside the world bounds. - * - * Returns: - * {Boolean} The passed-in bounds object intersects this bounds. - */ - intersectsBounds:function(bounds, options) { - if (typeof options === "boolean") { - options = {inclusive: options}; - } - options = options || {}; - if (options.worldBounds) { - var self = this.wrapDateLine(options.worldBounds); - bounds = bounds.wrapDateLine(options.worldBounds); - } else { - self = this; - } - if (options.inclusive == null) { - options.inclusive = true; - } - var intersects = false; - var mightTouch = ( - self.left == bounds.right || - self.right == bounds.left || - self.top == bounds.bottom || - self.bottom == bounds.top - ); - - // if the two bounds only touch at an edge, and inclusive is false, - // then the bounds don't *really* intersect. - if (options.inclusive || !mightTouch) { - // otherwise, if one of the boundaries even partially contains another, - // inclusive of the edges, then they do intersect. - var inBottom = ( - ((bounds.bottom >= self.bottom) && (bounds.bottom <= self.top)) || - ((self.bottom >= bounds.bottom) && (self.bottom <= bounds.top)) - ); - var inTop = ( - ((bounds.top >= self.bottom) && (bounds.top <= self.top)) || - ((self.top > bounds.bottom) && (self.top < bounds.top)) - ); - var inLeft = ( - ((bounds.left >= self.left) && (bounds.left <= self.right)) || - ((self.left >= bounds.left) && (self.left <= bounds.right)) - ); - var inRight = ( - ((bounds.right >= self.left) && (bounds.right <= self.right)) || - ((self.right >= bounds.left) && (self.right <= bounds.right)) - ); - intersects = ((inBottom || inTop) && (inLeft || inRight)); - } - // document me - if (options.worldBounds && !intersects) { - var world = options.worldBounds; - var width = world.getWidth(); - var selfCrosses = !world.containsBounds(self); - var boundsCrosses = !world.containsBounds(bounds); - if (selfCrosses && !boundsCrosses) { - bounds = bounds.add(-width, 0); - intersects = self.intersectsBounds(bounds, {inclusive: options.inclusive}); - } else if (boundsCrosses && !selfCrosses) { - self = self.add(-width, 0); - intersects = bounds.intersectsBounds(self, {inclusive: options.inclusive}); - } - } - return intersects; - }, - - /** - * APIMethod: containsBounds - * Returns whether the bounds object contains the given <OpenLayers.Bounds>. - * - * bounds - {<OpenLayers.Bounds>} The target bounds. - * partial - {Boolean} If any of the target corners is within this bounds - * consider the bounds contained. Default is false. If false, the - * entire target bounds must be contained within this bounds. - * inclusive - {Boolean} Treat shared edges as contained. Default is - * true. - * - * Returns: - * {Boolean} The passed-in bounds object is contained within this bounds. - */ - containsBounds:function(bounds, partial, inclusive) { - if (partial == null) { - partial = false; - } - if (inclusive == null) { - inclusive = true; - } - var bottomLeft = this.contains(bounds.left, bounds.bottom, inclusive); - var bottomRight = this.contains(bounds.right, bounds.bottom, inclusive); - var topLeft = this.contains(bounds.left, bounds.top, inclusive); - var topRight = this.contains(bounds.right, bounds.top, inclusive); - - return (partial) ? (bottomLeft || bottomRight || topLeft || topRight) - : (bottomLeft && bottomRight && topLeft && topRight); - }, - - /** - * APIMethod: determineQuadrant - * Returns the the quadrant ("br", "tr", "tl", "bl") in which the given - * <OpenLayers.LonLat> lies. - * - * Parameters: - * lonlat - {<OpenLayers.LonLat>} - * - * Returns: - * {String} The quadrant ("br" "tr" "tl" "bl") of the bounds in which the - * coordinate lies. - */ - 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; - }, - - /** - * APIMethod: transform - * Transform the Bounds object from source to dest. - * - * Parameters: - * source - {<OpenLayers.Projection>} Source projection. - * dest - {<OpenLayers.Projection>} Destination projection. - * - * Returns: - * {<OpenLayers.Bounds>} Itself, for use in chaining operations. - */ - transform: function(source, dest) { - // clear cached center location - this.centerLonLat = null; - var ll = OpenLayers.Projection.transform( - {'x': this.left, 'y': this.bottom}, source, dest); - var lr = OpenLayers.Projection.transform( - {'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; - }, - - /** - * APIMethod: wrapDateLine - * Wraps the bounds object around the dateline. - * - * Parameters: - * maxExtent - {<OpenLayers.Bounds>} - * options - {Object} Some possible options are: - * - * Allowed Options: - * leftTolerance - {float} Allow for a margin of error - * with the 'left' value of this - * bound. - * Default is 0. - * rightTolerance - {float} Allow for a margin of error - * with the 'right' value of - * this bound. - * Default is 0. - * - * Returns: - * {<OpenLayers.Bounds>} A copy of this bounds, but wrapped around the - * "dateline" (as specified by the borders of - * maxExtent). Note that this function only returns - * a different bounds value if this bounds is - * *entirely* outside of the maxExtent. If this - * bounds straddles the dateline (is part in/part - * out of maxExtent), the returned bounds will always - * cross the left edge of the given maxExtent. - *. - */ - wrapDateLine: function(maxExtent, options) { - options = options || {}; - - var leftTolerance = options.leftTolerance || 0; - var rightTolerance = options.rightTolerance || 0; - - var newBounds = this.clone(); - - if (maxExtent) { - var width = maxExtent.getWidth(); - - //shift right? - while (newBounds.left < maxExtent.left && - newBounds.right - rightTolerance <= maxExtent.left ) { - newBounds = newBounds.add(width, 0); - } - - //shift left? - while (newBounds.left + leftTolerance >= maxExtent.right && - newBounds.right > maxExtent.right ) { - newBounds = newBounds.add(-width, 0); - } - - // crosses right only? force left - var newLeft = newBounds.left + leftTolerance; - if (newLeft < maxExtent.right && newLeft > maxExtent.left && - newBounds.right - rightTolerance > maxExtent.right) { - newBounds = newBounds.add(-width, 0); - } - } - - return newBounds; - }, - - CLASS_NAME: "OpenLayers.Bounds" -}); - -/** - * APIFunction: fromString - * Alternative constructor that builds a new OpenLayers.Bounds from a - * parameter string. - * - * (begin code) - * OpenLayers.Bounds.fromString("5,42,10,45"); - * // => equivalent to ... - * new OpenLayers.Bounds(5, 42, 10, 45); - * (end) - * - * Parameters: - * str - {String} Comma-separated bounds string. (e.g. "5,42,10,45") - * reverseAxisOrder - {Boolean} Does the string use reverse axis order? - * - * Returns: - * {<OpenLayers.Bounds>} New bounds object built from the - * passed-in String. - */ -OpenLayers.Bounds.fromString = function(str, reverseAxisOrder) { - var bounds = str.split(","); - return OpenLayers.Bounds.fromArray(bounds, reverseAxisOrder); -}; - -/** - * APIFunction: fromArray - * Alternative constructor that builds a new OpenLayers.Bounds from an array. - * - * (begin code) - * OpenLayers.Bounds.fromArray( [5, 42, 10, 45] ); - * // => equivalent to ... - * new OpenLayers.Bounds(5, 42, 10, 45); - * (end) - * - * Parameters: - * bbox - {Array(Float)} Array of bounds values (e.g. [5,42,10,45]) - * reverseAxisOrder - {Boolean} Does the array use reverse axis order? - * - * Returns: - * {<OpenLayers.Bounds>} New bounds object built from the passed-in Array. - */ -OpenLayers.Bounds.fromArray = function(bbox, reverseAxisOrder) { - return reverseAxisOrder === true ? - new OpenLayers.Bounds(bbox[1], bbox[0], bbox[3], bbox[2]) : - new OpenLayers.Bounds(bbox[0], bbox[1], bbox[2], bbox[3]); -}; - -/** - * APIFunction: fromSize - * Alternative constructor that builds a new OpenLayers.Bounds from a size. - * - * (begin code) - * OpenLayers.Bounds.fromSize( new OpenLayers.Size(10, 20) ); - * // => equivalent to ... - * new OpenLayers.Bounds(0, 20, 10, 0); - * (end) - * - * Parameters: - * size - {<OpenLayers.Size> or Object} <OpenLayers.Size> or an object with - * both 'w' and 'h' properties. - * - * Returns: - * {<OpenLayers.Bounds>} New bounds object built from the passed-in size. - */ -OpenLayers.Bounds.fromSize = function(size) { - return new OpenLayers.Bounds(0, - size.h, - size.w, - 0); -}; - -/** - * Function: oppositeQuadrant - * Get the opposite quadrant for a given quadrant string. - * - * (begin code) - * OpenLayers.Bounds.oppositeQuadrant( "tl" ); - * // => "br" - * - * OpenLayers.Bounds.oppositeQuadrant( "tr" ); - * // => "bl" - * (end) - * - * Parameters: - * quadrant - {String} two character quadrant shortstring - * - * Returns: - * {String} The opposing quadrant ("br" "tr" "tl" "bl"). For Example, if - * you pass in "bl" it returns "tr", if you pass in "br" it - * returns "tl", etc. - */ -OpenLayers.Bounds.oppositeQuadrant = function(quadrant) { - var opp = ""; - - opp += (quadrant.charAt(0) == 't') ? 'b' : 't'; - opp += (quadrant.charAt(1) == 'l') ? 'r' : 'l'; - - return opp; -}; -/* ====================================================================== - OpenLayers/BaseTypes/Element.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @requires OpenLayers/Util.js - * @requires OpenLayers/BaseTypes.js - */ - -/** - * Namespace: OpenLayers.Element - */ -OpenLayers.Element = { - - /** - * APIFunction: visible - * - * Parameters: - * element - {DOMElement} - * - * Returns: - * {Boolean} Is the element visible? - */ - visible: function(element) { - return OpenLayers.Util.getElement(element).style.display != 'none'; - }, - - /** - * APIFunction: toggle - * Toggle the visibility of element(s) passed in - * - * Parameters: - * element - {DOMElement} Actually user can pass any number of elements - */ - 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) ? 'none' - : ''; - element.style.display = display; - } - }, - - /** - * APIFunction: remove - * Remove the specified element from the DOM. - * - * Parameters: - * element - {DOMElement} - */ - remove: function(element) { - element = OpenLayers.Util.getElement(element); - element.parentNode.removeChild(element); - }, - - /** - * APIFunction: getHeight - * - * Parameters: - * element - {DOMElement} - * - * Returns: - * {Integer} The offset height of the element passed in - */ - getHeight: function(element) { - element = OpenLayers.Util.getElement(element); - return element.offsetHeight; - }, - - /** - * 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: - * element - {DOMElement} - * style - {?} - * - * Returns: - * {?} - */ - 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/BaseTypes/LonLat.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @requires OpenLayers/BaseTypes/Class.js - */ - -/** - * Class: OpenLayers.LonLat - * This class represents a longitude and latitude pair - */ -OpenLayers.LonLat = OpenLayers.Class({ - - /** - * APIProperty: lon - * {Float} The x-axis coodinate in map units - */ - lon: 0.0, - - /** - * APIProperty: lat - * {Float} The y-axis coordinate in map units - */ - lat: 0.0, - - /** - * Constructor: OpenLayers.LonLat - * Create a new map location. Coordinates can be passed either as two - * arguments, or as a single argument. - * - * Parameters (two arguments): - * lon - {Number} The x-axis coordinate in map units. If your map is in - * a geographic projection, this will be the Longitude. Otherwise, - * it will be the x coordinate of the map location in your map units. - * lat - {Number} The y-axis coordinate in map units. If your map is in - * a geographic projection, this will be the Latitude. Otherwise, - * it will be the y coordinate of the map location in your map units. - * - * Parameters (single argument): - * location - {Array(Float)} [lon, lat] - */ - initialize: function(lon, lat) { - if (OpenLayers.Util.isArray(lon)) { - lat = lon[1]; - lon = lon[0]; - } - this.lon = OpenLayers.Util.toFloat(lon); - this.lat = OpenLayers.Util.toFloat(lat); - }, - - /** - * Method: toString - * Return a readable string version of the lonlat - * - * Returns: - * {String} String representation of OpenLayers.LonLat object. - * (e.g. <i>"lon=5,lat=42"</i>) - */ - toString:function() { - return ("lon=" + this.lon + ",lat=" + this.lat); - }, - - /** - * APIMethod: toShortString - * - * Returns: - * {String} Shortened String representation of OpenLayers.LonLat object. - * (e.g. <i>"5, 42"</i>) - */ - toShortString:function() { - return (this.lon + ", " + this.lat); - }, - - /** - * APIMethod: clone - * - * Returns: - * {<OpenLayers.LonLat>} New OpenLayers.LonLat object with the same lon - * and lat values - */ - clone:function() { - return new OpenLayers.LonLat(this.lon, this.lat); - }, - - /** - * APIMethod: add - * - * Parameters: - * lon - {Float} - * lat - {Float} - * - * Returns: - * {<OpenLayers.LonLat>} A new OpenLayers.LonLat object with the lon and - * lat passed-in added to this's. - */ - add:function(lon, lat) { - if ( (lon == null) || (lat == null) ) { - throw new TypeError('LonLat.add cannot receive null values'); - } - return new OpenLayers.LonLat(this.lon + OpenLayers.Util.toFloat(lon), - this.lat + OpenLayers.Util.toFloat(lat)); - }, - - /** - * APIMethod: equals - * - * Parameters: - * ll - {<OpenLayers.LonLat>} - * - * Returns: - * {Boolean} Boolean value indicating whether the passed-in - * <OpenLayers.LonLat> object has the same lon and lat - * components as this. - * Note: if ll passed in is null, returns false - */ - 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; - }, - - /** - * APIMethod: transform - * 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. - * dest - {<OpenLayers.Projection>} Destination projection. - * - * Returns: - * {<OpenLayers.LonLat>} Itself, for use in chaining operations. - */ - 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; - }, - - /** - * APIMethod: wrapDateLine - * - * Parameters: - * maxExtent - {<OpenLayers.Bounds>} - * - * Returns: - * {<OpenLayers.LonLat>} A copy of this lonlat, but wrapped around the - * "dateline" (as specified by the borders of - * maxExtent) - */ - wrapDateLine: function(maxExtent) { - - var newLonLat = this.clone(); - - if (maxExtent) { - //shift right? - while (newLonLat.lon < maxExtent.left) { - newLonLat.lon += maxExtent.getWidth(); - } - - //shift left? - while (newLonLat.lon > maxExtent.right) { - newLonLat.lon -= maxExtent.getWidth(); - } - } - - return newLonLat; - }, - - CLASS_NAME: "OpenLayers.LonLat" -}); - -/** - * Function: fromString - * Alternative constructor that builds a new <OpenLayers.LonLat> from a - * parameter string - * - * Parameters: - * str - {String} Comma-separated Lon,Lat coordinate string. - * (e.g. <i>"5,40"</i>) - * - * Returns: - * {<OpenLayers.LonLat>} New <OpenLayers.LonLat> object built from the - * passed-in String. - */ -OpenLayers.LonLat.fromString = function(str) { - var pair = str.split(","); - return new OpenLayers.LonLat(pair[0], pair[1]); -}; - -/** - * Function: fromArray - * Alternative constructor that builds a new <OpenLayers.LonLat> from an - * array of two numbers that represent lon- and lat-values. - * - * Parameters: - * arr - {Array(Float)} Array of lon/lat values (e.g. [5,-42]) - * - * Returns: - * {<OpenLayers.LonLat>} New <OpenLayers.LonLat> object built from the - * passed-in array. - */ -OpenLayers.LonLat.fromArray = function(arr) { - var gotArr = OpenLayers.Util.isArray(arr), - lon = gotArr && arr[0], - lat = gotArr && arr[1]; - return new OpenLayers.LonLat(lon, lat); -}; -/* ====================================================================== - OpenLayers/BaseTypes/Pixel.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @requires OpenLayers/BaseTypes/Class.js - */ - -/** - * Class: OpenLayers.Pixel - * This class represents a screen coordinate, in x and y coordinates - */ -OpenLayers.Pixel = OpenLayers.Class({ - - /** - * APIProperty: x - * {Number} The x coordinate - */ - x: 0.0, - - /** - * APIProperty: y - * {Number} The y coordinate - */ - y: 0.0, - - /** - * Constructor: OpenLayers.Pixel - * Create a new OpenLayers.Pixel instance - * - * Parameters: - * x - {Number} The x coordinate - * y - {Number} The y coordinate - * - * Returns: - * An instance of OpenLayers.Pixel - */ - initialize: function(x, y) { - this.x = parseFloat(x); - this.y = parseFloat(y); - }, - - /** - * Method: toString - * Cast this object into a string - * - * Returns: - * {String} The string representation of Pixel. ex: "x=200.4,y=242.2" - */ - toString:function() { - return ("x=" + this.x + ",y=" + this.y); - }, - - /** - * APIMethod: clone - * Return a clone of this pixel object - * - * Returns: - * {<OpenLayers.Pixel>} A clone pixel - */ - clone:function() { - return new OpenLayers.Pixel(this.x, this.y); - }, - - /** - * APIMethod: equals - * Determine whether one pixel is equivalent to another - * - * Parameters: - * px - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or an object with - * a 'x' and 'y' properties. - * - * Returns: - * {Boolean} The point passed in as parameter is equal to this. Note that - * if px passed in is null, returns false. - */ - equals:function(px) { - var equals = false; - if (px != null) { - equals = ((this.x == px.x && this.y == px.y) || - (isNaN(this.x) && isNaN(this.y) && isNaN(px.x) && isNaN(px.y))); - } - return equals; - }, - - /** - * APIMethod: distanceTo - * Returns the distance to the pixel point passed in as a parameter. - * - * Parameters: - * px - {<OpenLayers.Pixel>} - * - * Returns: - * {Float} The pixel point passed in as parameter to calculate the - * distance to. - */ - distanceTo:function(px) { - return Math.sqrt( - Math.pow(this.x - px.x, 2) + - Math.pow(this.y - px.y, 2) - ); - }, - - /** - * APIMethod: add - * - * Parameters: - * x - {Integer} - * y - {Integer} - * - * Returns: - * {<OpenLayers.Pixel>} A new Pixel with this pixel's x&y augmented by the - * values passed in. - */ - add:function(x, y) { - if ( (x == null) || (y == null) ) { - throw new TypeError('Pixel.add cannot receive null values'); - } - return new OpenLayers.Pixel(this.x + x, this.y + y); - }, - - /** - * APIMethod: offset - * - * Parameters - * px - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or an object with - * a 'x' and 'y' properties. - * - * Returns: - * {<OpenLayers.Pixel>} A new Pixel with this pixel's x&y augmented by the - * x&y values of the pixel passed in. - */ - offset:function(px) { - var newPx = this.clone(); - if (px) { - newPx = this.add(px.x, px.y); - } - return newPx; - }, - - CLASS_NAME: "OpenLayers.Pixel" -}); -/* ====================================================================== - OpenLayers/BaseTypes/Size.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @requires OpenLayers/BaseTypes/Class.js - */ - -/** - * 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. - * (e.g. <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>|Object} An OpenLayers.Size or an object with - * a 'w' and 'h' properties. - * - * 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-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @requires OpenLayers/BaseTypes/Class.js - */ - -/** - * 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: - * error - {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. - */ - var scripts = document.getElementsByTagName("script"); - for(var i=0, len=scripts.length; i<len; ++i) { - if(scripts[i].src.indexOf("firebug.js") != -1) { - if(console) { - OpenLayers.Util.extend(OpenLayers.Console, console); - break; - } - } - } -})(); -/* ====================================================================== - OpenLayers/Lang.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @requires OpenLayers/BaseTypes.js - * @requires OpenLayers/Console.js - */ - -/** - * Namespace: OpenLayers.Lang - * Internationalization namespace. Contains dictionaries in various languages - * and methods to set and get the current language. - */ -OpenLayers.Lang = { - - /** - * Property: code - * {String} Current language code to use in OpenLayers. Use the - * <setCode> method to set this value and the <getCode> method to - * retrieve it. - */ - code: null, - - /** - * APIProperty: defaultCode - * {String} Default language to use when a specific language can't be - * found. Default is "en". - */ - defaultCode: "en", - - /** - * APIFunction: getCode - * Get the current language code. - * - * Returns: - * {String} The current language code. - */ - getCode: function() { - if(!OpenLayers.Lang.code) { - OpenLayers.Lang.setCode(); - } - return OpenLayers.Lang.code; - }, - - /** - * APIFunction: setCode - * Set the language code for string translation. This code is used by - * the <OpenLayers.Lang.translate> method. - * - * Parameters: - * code - {String} These codes follow the IETF recommendations at - * http://www.ietf.org/rfc/rfc3066.txt. If no value is set, the - * browser's language setting will be tested. If no <OpenLayers.Lang> - * dictionary exists for the code, the <OpenLayers.String.defaultLang> - * will be used. - */ - setCode: function(code) { - var lang; - if(!code) { - code = (OpenLayers.BROWSER_NAME == "msie") ? - navigator.userLanguage : navigator.language; - } - var parts = code.split('-'); - parts[0] = parts[0].toLowerCase(); - if(typeof OpenLayers.Lang[parts[0]] == "object") { - lang = parts[0]; - } - - // check for regional extensions - if(parts[1]) { - var testLang = parts[0] + '-' + parts[1].toUpperCase(); - if(typeof OpenLayers.Lang[testLang] == "object") { - lang = testLang; - } - } - 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; - }, - - /** - * APIMethod: translate - * Looks up a key from a dictionary based on the current language string. - * The value of <getCode> will be used to determine the appropriate - * dictionary. Dictionaries are stored in <OpenLayers.Lang>. - * - * Parameters: - * key - {String} The key for an i18n string value in the dictionary. - * context - {Object} Optional context to be used with - * <OpenLayers.String.format>. - * - * Returns: - * {String} A internationalized string. - */ - translate: function(key, context) { - var dictionary = OpenLayers.Lang[OpenLayers.Lang.getCode()]; - var message = dictionary && dictionary[key]; - if(!message) { - // Message not found, fall back to message key - message = key; - } - if(context) { - message = OpenLayers.String.format(message, context); - } - return message; - } - -}; - - -/** - * APIMethod: OpenLayers.i18n - * Alias for <OpenLayers.Lang.translate>. Looks up a key from a dictionary - * based on the current language string. The value of - * <OpenLayers.Lang.getCode> will be used to determine the appropriate - * dictionary. Dictionaries are stored in <OpenLayers.Lang>. - * - * Parameters: - * key - {String} The key for an i18n string value in the dictionary. - * context - {Object} Optional context to be used with - * <OpenLayers.String.format>. - * - * Returns: - * {String} A internationalized string. - */ -OpenLayers.i18n = OpenLayers.Lang.translate; -/* ====================================================================== - OpenLayers/Util.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @requires OpenLayers/BaseTypes.js - * @requires OpenLayers/BaseTypes/Bounds.js - * @requires OpenLayers/BaseTypes/Element.js - * @requires OpenLayers/BaseTypes/LonLat.js - * @requires OpenLayers/BaseTypes/Pixel.js - * @requires OpenLayers/BaseTypes/Size.js - * @requires OpenLayers/Lang.js - */ - -/** - * Namespace: Util - */ -OpenLayers.Util = OpenLayers.Util || {}; - -/** - * Function: getElement - * This is the old $() from prototype - * - * Parameters: - * e - {String or DOMElement or Window} - * - * Returns: - * {Array(DOMElement) or DOMElement} - */ -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; -}; - -/** - * Function: isElement - * A cross-browser implementation of "e instanceof Element". - * - * Parameters: - * o - {Object} The object to test. - * - * Returns: - * {Boolean} - */ -OpenLayers.Util.isElement = function(o) { - return !!(o && o.nodeType === 1); -}; - -/** - * Function: isArray - * Tests that the provided object is an array. - * This test handles the cross-IFRAME case not caught - * by "a instanceof Array" and should be used instead. - * - * Parameters: - * a - {Object} the object test. - * - * Returns: - * {Boolean} true if the object is an array. - */ -OpenLayers.Util.isArray = function(a) { - return (Object.prototype.toString.call(a) === '[object Array]'); -}; - -/** - * Function: removeItem - * Remove an object from an array. Iterates through the array - * to find the item, then removes it. - * - * Parameters: - * array - {Array} - * item - {Object} - * - * Returns: - * {Array} A reference to the array - */ -OpenLayers.Util.removeItem = function(array, item) { - for(var i = array.length - 1; i >= 0; i--) { - if(array[i] == item) { - array.splice(i,1); - //break;more than once?? - } - } - return array; -}; - -/** - * Function: indexOf - * Seems to exist already in FF, but not in MOZ. - * - * Parameters: - * array - {Array} - * obj - {*} - * - * Returns: - * {Integer} The index at which the first object was found in the array. - * If not found, returns -1. - */ -OpenLayers.Util.indexOf = function(array, obj) { - // use the build-in function if available. - if (typeof array.indexOf == "function") { - return array.indexOf(obj); - } else { - for (var i = 0, len = array.length; i < len; i++) { - if (array[i] == obj) { - return i; - } - } - return -1; - } -}; - - -/** - * Property: dotless - * {RegExp} - * Compiled regular expression to match dots ("."). This is used for replacing - * dots in identifiers. Because object identifiers are frequently used for - * DOM element identifiers by the library, we avoid using dots to make for - * more sensible CSS selectors. - * - * TODO: Use a module pattern to avoid bloating the API with stuff like this. - */ -OpenLayers.Util.dotless = /\./g; - -/** - * Function: modifyDOMElement - * - * Modifies many properties of a DOM element all at once. Passing in - * null to an individual parameter will avoid setting the attribute. - * - * Parameters: - * element - {DOMElement} DOM element to modify. - * id - {String} The element id attribute to set. Note that dots (".") will be - * replaced with underscore ("_") in setting the element id. - * px - {<OpenLayers.Pixel>|Object} The element left and top position, - * OpenLayers.Pixel or an object with - * a 'x' and 'y' properties. - * sz - {<OpenLayers.Size>|Object} The element width and height, - * OpenLayers.Size or an object with a - * 'w' and 'h' properties. - * position - {String} The position attribute. eg: absolute, - * relative, etc. - * border - {String} The style.border attribute. eg: - * solid black 2px - * overflow - {String} The style.overview attribute. - * opacity - {Float} Fractional value (0.0 - 1.0) - */ -OpenLayers.Util.modifyDOMElement = function(element, id, px, sz, position, - border, overflow, opacity) { - - if (id) { - element.id = id.replace(OpenLayers.Util.dotless, "_"); - } - 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"; - } - if (position) { - element.style.position = position; - } - if (border) { - element.style.border = border; - } - if (overflow) { - element.style.overflow = overflow; - } - if (parseFloat(opacity) >= 0.0 && parseFloat(opacity) < 1.0) { - element.style.filter = 'alpha(opacity=' + (opacity * 100) + ')'; - element.style.opacity = opacity; - } else if (parseFloat(opacity) == 1.0) { - element.style.filter = ''; - element.style.opacity = ''; - } -}; - -/** - * 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. - * Note - zIndex is NOT set on the resulting div. - * - * Parameters: - * id - {String} An identifier for this element. If no id is - * passed an identifier will be created - * automatically. Note that dots (".") will be replaced with - * underscore ("_") when generating ids. - * px - {<OpenLayers.Pixel>|Object} The element left and top position, - * OpenLayers.Pixel or an object with - * a 'x' and 'y' properties. - * sz - {<OpenLayers.Size>|Object} The element width and height, - * OpenLayers.Size or an object with a - * 'w' and 'h' properties. - * imgURL - {String} A url pointing to an image to use as a - * background image. - * position - {String} The style.position value. eg: absolute, - * relative etc. - * border - {String} The the style.border value. - * eg: 2px solid black - * overflow - {String} The style.overflow value. Eg. hidden - * opacity - {Float} Fractional value (0.0 - 1.0) - * - * Returns: - * {DOMElement} A DOM Div created with the specified attributes. - */ -OpenLayers.Util.createDiv = function(id, px, sz, imgURL, position, - border, overflow, opacity) { - - var dom = document.createElement('div'); - - if (imgURL) { - dom.style.backgroundImage = 'url(' + imgURL + ')'; - } - - //set generic properties - if (!id) { - id = OpenLayers.Util.createUniqueID("OpenLayersDiv"); - } - if (!position) { - position = "absolute"; - } - OpenLayers.Util.modifyDOMElement(dom, id, px, sz, position, - border, overflow, opacity); - - return dom; -}; - -/** - * Function: createImage - * Creates an img element with specific attribute values. - * - * Parameters: - * id - {String} The id field for the img. If none assigned one will be - * automatically generated. - * px - {<OpenLayers.Pixel>|Object} The element left and top position, - * OpenLayers.Pixel or an object with - * a 'x' and 'y' properties. - * sz - {<OpenLayers.Size>|Object} The element width and height, - * OpenLayers.Size or an object with a - * 'w' and 'h' properties. - * imgURL - {String} The url to use as the image source. - * position - {String} The style.position value. - * border - {String} The border to place around the image. - * opacity - {Float} Fractional value (0.0 - 1.0) - * delayDisplay - {Boolean} If true waits until the image has been - * loaded. - * - * Returns: - * {DOMElement} A DOM Image created with the specified attributes. - */ -OpenLayers.Util.createImage = function(id, px, sz, imgURL, position, border, - opacity, delayDisplay) { - - var image = document.createElement("img"); - - //set generic properties - if (!id) { - id = OpenLayers.Util.createUniqueID("OpenLayersDiv"); - } - if (!position) { - position = "relative"; - } - OpenLayers.Util.modifyDOMElement(image, id, px, sz, position, - border, null, opacity); - - if (delayDisplay) { - image.style.display = "none"; - function display() { - image.style.display = ""; - OpenLayers.Event.stopObservingElement(image); - } - OpenLayers.Event.observe(image, "load", display); - OpenLayers.Event.observe(image, "error", display); - } - - //set special properties - image.style.alt = id; - image.galleryImg = "no"; - if (imgURL) { - image.src = imgURL; - } - - return image; -}; - -/** - * Property: IMAGE_RELOAD_ATTEMPTS - * {Integer} How many times should we try to reload an image before giving up? - * Default is 0 - */ -OpenLayers.IMAGE_RELOAD_ATTEMPTS = 0; - -/** - * Property: alphaHackNeeded - * {Boolean} true if the png alpha hack is necessary and possible, false otherwise. - */ -OpenLayers.Util.alphaHackNeeded = null; - -/** - * Function: alphaHack - * Checks whether it's necessary (and possible) to use the png alpha - * hack which allows alpha transparency for png images under Internet - * Explorer. - * - * Returns: - * {Boolean} true if the png alpha hack is necessary and possible, false otherwise. - */ -OpenLayers.Util.alphaHack = function() { - if (OpenLayers.Util.alphaHackNeeded == null) { - var arVersion = navigator.appVersion.split("MSIE"); - var version = parseFloat(arVersion[1]); - var filter = false; - - // IEs4Lin dies when trying to access document.body.filters, because - // the property is there, but requires a DLL that can't be provided. This - // means that we need to wrap this in a try/catch so that this can - // continue. - - try { - filter = !!(document.body.filters); - } catch (e) {} - - OpenLayers.Util.alphaHackNeeded = (filter && - (version >= 5.5) && (version < 7)); - } - return OpenLayers.Util.alphaHackNeeded; -}; - -/** - * Function: modifyAlphaImageDiv - * - * Parameters: - * div - {DOMElement} Div containing Alpha-adjusted Image - * id - {String} - * px - {<OpenLayers.Pixel>|Object} OpenLayers.Pixel or an object with - * a 'x' and 'y' properties. - * sz - {<OpenLayers.Size>|Object} OpenLayers.Size or an object with - * a 'w' and 'h' properties. - * imgURL - {String} - * position - {String} - * border - {String} - * sizing - {String} 'crop', 'scale', or 'image'. Default is "scale" - * opacity - {Float} Fractional value (0.0 - 1.0) - */ -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)"; - } -}; - -/** - * Function: createAlphaImageDiv - * - * Parameters: - * id - {String} - * px - {<OpenLayers.Pixel>|Object} OpenLayers.Pixel or an object with - * a 'x' and 'y' properties. - * sz - {<OpenLayers.Size>|Object} OpenLayers.Size or an object with - * a 'w' and 'h' properties. - * imgURL - {String} - * position - {String} - * border - {String} - * sizing - {String} 'crop', 'scale', or 'image'. Default is "scale" - * opacity - {Float} Fractional value (0.0 - 1.0) - * delayDisplay - {Boolean} If true waits until the image has been - * loaded. - * - * Returns: - * {DOMElement} A DOM Div created with a DOM Image inside it. If the hack is - * needed for transparency in IE, it is added. - */ -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, delayDisplay); - img.className = "olAlphaImg"; - div.appendChild(img); - - OpenLayers.Util.modifyAlphaImageDiv(div, id, px, sz, imgURL, position, - border, sizing, opacity); - - return div; -}; - - -/** - * Function: upperCaseObject - * Creates a new hashtable and copies over all the keys from the - * passed-in object, but storing them under an uppercased - * version of the key at which they were stored. - * - * Parameters: - * object - {Object} - * - * Returns: - * {Object} A new Object with all the same keys but uppercased - */ -OpenLayers.Util.upperCaseObject = function (object) { - var uObject = {}; - for (var key in object) { - uObject[key.toUpperCase()] = object[key]; - } - return uObject; -}; - -/** - * Function: applyDefaults - * Takes an object and copies any properties that don't exist from - * another properties, by analogy with OpenLayers.Util.extend() from - * Prototype.js. - * - * Parameters: - * to - {Object} The destination object. - * from - {Object} The source object. Any properties of this object that - * are undefined in the to object will be set on the to object. - * - * Returns: - * {Object} A reference to the to object. Note that the to argument is modified - * 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 - * instance of window.Event. - */ - 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]; - } - } - /** - * IE doesn't include the toString property when iterating over an object's - * properties with the for(property in object) syntax. Explicitly check if - * the source has its own toString property. - */ - if(!fromIsEvt && from && from.hasOwnProperty - && from.hasOwnProperty('toString') && !to.hasOwnProperty('toString')) { - to.toString = from.toString; - } - - return to; -}; - -/** - * Function: getParameterString - * - * Parameters: - * params - {Object} - * - * Returns: - * {String} A concatenation of the properties of an object in - * http parameter notation. - * (ex. <i>"key1=value1&key2=value2&key3=value3"</i>) - * If a parameter is actually a list, that parameter will then - * be set to a comma-seperated list of values (foo,bar) instead - * of being URL escaped (foo%3Abar). - */ -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) { - /* value is an array; encode items and separate with "," */ - var encodedItemArray = []; - var item; - for (var itemIndex=0, len=value.length; itemIndex<len; itemIndex++) { - item = value[itemIndex]; - encodedItemArray.push(encodeURIComponent( - (item === null || item === undefined) ? "" : item) - ); - } - encodedValue = encodedItemArray.join(","); - } - else { - /* value is a string; simply encode */ - encodedValue = encodeURIComponent(value); - } - paramsArray.push(encodeURIComponent(key) + "=" + encodedValue); - } - } - - return paramsArray.join("&"); -}; - -/** - * Function: urlAppend - * Appends a parameter string to a url. This function includes the logic for - * using the appropriate character (none, & or ?) to append to the url before - * appending the param string. - * - * Parameters: - * url - {String} The url to append to - * paramStr - {String} The param string to append - * - * Returns: - * {String} The new url - */ -OpenLayers.Util.urlAppend = function(url, paramStr) { - var newUrl = url; - if(paramStr) { - var parts = (url + " ").split(/[?&]/); - newUrl += (parts.pop() === " " ? - paramStr : - parts.length ? "&" + paramStr : "?" + paramStr); - } - return newUrl; -}; - -/** - * Function: getImagesLocation - * - * Returns: - * {String} The fully formatted image location string - */ -OpenLayers.Util.getImagesLocation = function() { - return OpenLayers.ImgPath || (OpenLayers._getScriptLocation() + "img/"); -}; - -/** - * Function: getImageLocation - * - * Returns: - * {String} The fully formatted location string for a specified image - */ -OpenLayers.Util.getImageLocation = function(image) { - return OpenLayers.Util.getImagesLocation() + image; -}; - - -/** - * Function: Try - * Execute functions until one of them doesn't throw an error. - * Capitalized because "try" is a reserved word in JavaScript. - * Taken directly from OpenLayers.Util.Try() - * - * Parameters: - * [*] - {Function} Any number of parameters may be passed to Try() - * It will attempt to execute each of them until one of them - * successfully executes. - * If none executes successfully, returns null. - * - * Returns: - * {*} The value returned by the first successfully executed function. - */ -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; -}; - -/** - * Function: getXmlNodeValue - * - * Parameters: - * node - {XMLNode} - * - * Returns: - * {String} The text value of the given node, without breaking in firefox or IE - */ -OpenLayers.Util.getXmlNodeValue = function(node) { - var val = null; - OpenLayers.Util.Try( - function() { - val = node.text; - if (!val) { - val = node.textContent; - } - if (!val) { - val = node.firstChild.nodeValue; - } - }, - function() { - val = node.textContent; - }); - return val; -}; - -/** - * Function: mouseLeft - * - * Parameters: - * evt - {Event} - * div - {HTMLDivElement} - * - * Returns: - * {Boolean} - */ -OpenLayers.Util.mouseLeft = function (evt, div) { - // start with the element to which the mouse has moved - var target = (evt.relatedTarget) ? evt.relatedTarget : evt.toElement; - // walk up the DOM tree. - while (target != div && target != null) { - target = target.parentNode; - } - // if the target we stop at isn't the div, then we've left the div. - return (target != div); -}; - -/** - * Property: precision - * {Number} The number of significant digits to retain to avoid - * floating point precision errors. - * - * We use 14 as a "safe" default because, although IEEE 754 double floats - * (standard on most modern operating systems) support up to about 16 - * significant digits, 14 significant digits are sufficient to represent - * sub-millimeter accuracy in any coordinate system that anyone is likely to - * use with OpenLayers. - * - * If DEFAULT_PRECISION is set to 0, the original non-truncating behavior - * of OpenLayers <2.8 is preserved. Be aware that this will cause problems - * with certain projections, e.g. spherical Mercator. - * - */ -OpenLayers.Util.DEFAULT_PRECISION = 14; - -/** - * Function: toFloat - * Convenience method to cast an object to a Number, rounded to the - * desired floating point precision. - * - * Parameters: - * number - {Number} The number to cast and round. - * precision - {Number} An integer suitable for use with - * Number.toPrecision(). Defaults to OpenLayers.Util.DEFAULT_PRECISION. - * If set to 0, no rounding is performed. - * - * Returns: - * {Number} The cast, rounded number. - */ -OpenLayers.Util.toFloat = function (number, precision) { - if (precision == null) { - precision = OpenLayers.Util.DEFAULT_PRECISION; - } - if (typeof number !== "number") { - number = parseFloat(number); - } - return precision === 0 ? number : - parseFloat(number.toPrecision(precision)); -}; - -/** - * Function: rad - * - * Parameters: - * x - {Float} - * - * Returns: - * {Float} - */ -OpenLayers.Util.rad = function(x) {return x*Math.PI/180;}; - -/** - * Function: deg - * - * Parameters: - * x - {Float} - * - * Returns: - * {Float} - */ -OpenLayers.Util.deg = function(x) {return x*180/Math.PI;}; - -/** - * Property: VincentyConstants - * {Object} Constants for Vincenty functions. - */ -OpenLayers.Util.VincentyConstants = { - a: 6378137, - b: 6356752.3142, - f: 1/298.257223563 -}; - -/** - * APIFunction: distVincenty - * Given two objects representing points with geographic coordinates, this - * calculates the distance between those points on the surface of an - * ellipsoid. - * - * Parameters: - * p1 - {<OpenLayers.LonLat>} (or any object with both .lat, .lon properties) - * p2 - {<OpenLayers.LonLat>} (or any object with both .lat, .lon properties) - * - * Returns: - * {Float} The distance (in km) between the two input points as measured on an - * ellipsoid. Note that the input point objects must be in geographic - * coordinates (decimal degrees) and the return distance is in kilometers. - */ -OpenLayers.Util.distVincenty = function(p1, p2) { - var ct = OpenLayers.Util.VincentyConstants; - var a = ct.a, b = ct.b, f = ct.f; - - var L = OpenLayers.Util.rad(p2.lon - p1.lon); - var U1 = Math.atan((1-f) * Math.tan(OpenLayers.Util.rad(p1.lat))); - var U2 = Math.atan((1-f) * Math.tan(OpenLayers.Util.rad(p2.lat))); - var sinU1 = Math.sin(U1), cosU1 = Math.cos(U1); - var sinU2 = Math.sin(U2), cosU2 = Math.cos(U2); - var lambda = L, lambdaP = 2*Math.PI; - var iterLimit = 20; - while (Math.abs(lambda-lambdaP) > 1e-12 && --iterLimit>0) { - var sinLambda = Math.sin(lambda), cosLambda = Math.cos(lambda); - var sinSigma = Math.sqrt((cosU2*sinLambda) * (cosU2*sinLambda) + - (cosU1*sinU2-sinU1*cosU2*cosLambda) * (cosU1*sinU2-sinU1*cosU2*cosLambda)); - if (sinSigma==0) { - return 0; // co-incident points - } - var cosSigma = sinU1*sinU2 + cosU1*cosU2*cosLambda; - var sigma = Math.atan2(sinSigma, cosSigma); - var alpha = Math.asin(cosU1 * cosU2 * sinLambda / sinSigma); - var cosSqAlpha = Math.cos(alpha) * Math.cos(alpha); - var cos2SigmaM = cosSigma - 2*sinU1*sinU2/cosSqAlpha; - var C = f/16*cosSqAlpha*(4+f*(4-3*cosSqAlpha)); - lambdaP = lambda; - lambda = L + (1-C) * f * Math.sin(alpha) * - (sigma + C*sinSigma*(cos2SigmaM+C*cosSigma*(-1+2*cos2SigmaM*cos2SigmaM))); - } - if (iterLimit==0) { - return NaN; // formula failed to converge - } - 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; // round to 1mm precision - return d; -}; - -/** - * APIFunction: destinationVincenty - * Calculate destination point given start point lat/long (numeric degrees), - * bearing (numeric degrees) & distance (in m). - * Adapted from Chris Veness work, see - * http://www.movable-type.co.uk/scripts/latlong-vincenty-direct.html - * - * Parameters: - * lonlat - {<OpenLayers.LonLat>} (or any object with both .lat, .lon - * properties) The start point. - * brng - {Float} The bearing (degrees). - * dist - {Float} The ground distance (meters). - * - * Returns: - * {<OpenLayers.LonLat>} The destination point. - */ -OpenLayers.Util.destinationVincenty = function(lonlat, brng, dist) { - var u = OpenLayers.Util; - var ct = u.VincentyConstants; - var a = ct.a, b = ct.b, f = ct.f; - - var lon1 = lonlat.lon; - var lat1 = lonlat.lat; - - var s = dist; - var alpha1 = u.rad(brng); - var sinAlpha1 = Math.sin(alpha1); - var cosAlpha1 = Math.cos(alpha1); - - var tanU1 = (1-f) * Math.tan(u.rad(lat1)); - var cosU1 = 1 / Math.sqrt((1 + tanU1*tanU1)), sinU1 = tanU1*cosU1; - var sigma1 = Math.atan2(tanU1, cosAlpha1); - var sinAlpha = cosU1 * sinAlpha1; - var cosSqAlpha = 1 - sinAlpha*sinAlpha; - 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 sigma = s / (b*A), sigmaP = 2*Math.PI; - while (Math.abs(sigma-sigmaP) > 1e-12) { - var cos2SigmaM = Math.cos(2*sigma1 + sigma); - var sinSigma = Math.sin(sigma); - var cosSigma = Math.cos(sigma); - var deltaSigma = B*sinSigma*(cos2SigmaM+B/4*(cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)- - B/6*cos2SigmaM*(-3+4*sinSigma*sinSigma)*(-3+4*cos2SigmaM*cos2SigmaM))); - sigmaP = sigma; - sigma = s / (b*A) + deltaSigma; - } - - var tmp = sinU1*sinSigma - cosU1*cosSigma*cosAlpha1; - var lat2 = Math.atan2(sinU1*cosSigma + cosU1*sinSigma*cosAlpha1, - (1-f)*Math.sqrt(sinAlpha*sinAlpha + tmp*tmp)); - var lambda = Math.atan2(sinSigma*sinAlpha1, cosU1*cosSigma - sinU1*sinSigma*cosAlpha1); - var C = f/16*cosSqAlpha*(4+f*(4-3*cosSqAlpha)); - var L = lambda - (1-C) * f * sinAlpha * - (sigma + C*sinSigma*(cos2SigmaM+C*cosSigma*(-1+2*cos2SigmaM*cos2SigmaM))); - - var revAz = Math.atan2(sinAlpha, -tmp); // final bearing - - return new OpenLayers.LonLat(lon1+u.deg(L), u.deg(lat2)); -}; - -/** - * Function: getParameters - * Parse the parameters from a URL or from the current page itself into a - * JavaScript Object. Note that parameter values with commas are separated - * out into an Array. - * - * Parameters: - * url - {String} Optional url used to extract the query string. - * If url is null or is not supplied, query string is taken - * from the page location. - * options - {Object} Additional options. Optional. - * - * Valid options: - * splitArgs - {Boolean} Split comma delimited params into arrays? Default is - * true. - * - * Returns: - * {Object} An object of key/value pairs from the query string. - */ -OpenLayers.Util.getParameters = function(url, options) { - options = options || {}; - // if no url specified, take it from the location bar - url = (url === null || url === undefined) ? window.location.href : url; - - //parse out parameters portion of url string - 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, len=pairs.length; i<len; ++i) { - var keyValue = pairs[i].split('='); - if (keyValue[0]) { - - var key = keyValue[0]; - try { - key = decodeURIComponent(key); - } catch (err) { - key = unescape(key); - } - - // being liberal by replacing "+" with " " - var value = (keyValue[1] || '').replace(/\+/g, " "); - - try { - value = decodeURIComponent(value); - } catch (err) { - value = unescape(value); - } - - // follow OGC convention of comma delimited values - if (options.splitArgs !== false) { - value = value.split(","); - } - - //if there's only one value, do not return as array - if (value.length == 1) { - value = value[0]; - } - - parameters[key] = value; - } - } - return parameters; -}; - -/** - * Property: lastSeqID - * {Integer} The ever-incrementing count variable. - * Used for generating unique ids. - */ -OpenLayers.Util.lastSeqID = 0; - -/** - * Function: createUniqueID - * Create a unique identifier for this session. Each time this function - * is called, a counter is incremented. The return will be the optional - * prefix (defaults to "id_") appended with the counter value. - * - * Parameters: - * prefix - {String} Optional string to prefix unique id. Default is "id_". - * Note that dots (".") in the prefix will be replaced with underscore ("_"). - * - * Returns: - * {String} A unique id string, built on the passed in prefix. - */ -OpenLayers.Util.createUniqueID = function(prefix) { - if (prefix == null) { - prefix = "id_"; - } else { - prefix = prefix.replace(OpenLayers.Util.dotless, "_"); - } - OpenLayers.Util.lastSeqID += 1; - return prefix + OpenLayers.Util.lastSeqID; -}; - -/** - * Constant: INCHES_PER_UNIT - * {Object} Constant inches per unit -- borrowed from MapServer mapscale.c - * derivation of nautical miles from http://en.wikipedia.org/wiki/Nautical_mile - * Includes the full set of units supported by CS-MAP (http://trac.osgeo.org/csmap/) - * and PROJ.4 (http://trac.osgeo.org/proj/) - * The hardcoded table is maintain in a CS-MAP source code module named CSdataU.c - * The hardcoded table of PROJ.4 units are in pj_units.c. - */ -OpenLayers.INCHES_PER_UNIT = { - 'inches': 1.0, - 'ft': 12.0, - 'mi': 63360.0, - 'm': 39.37, - 'km': 39370, - '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; - -// Units from CS-Map -OpenLayers.METERS_PER_INCH = 0.02540005080010160020; -OpenLayers.Util.extend(OpenLayers.INCHES_PER_UNIT, { - "Inch": OpenLayers.INCHES_PER_UNIT.inches, - "Meter": 1.0 / OpenLayers.METERS_PER_INCH, //EPSG:9001 - "Foot": 0.30480060960121920243 / OpenLayers.METERS_PER_INCH, //EPSG:9003 - "IFoot": 0.30480000000000000000 / OpenLayers.METERS_PER_INCH, //EPSG:9002 - "ClarkeFoot": 0.3047972651151 / OpenLayers.METERS_PER_INCH, //EPSG:9005 - "SearsFoot": 0.30479947153867624624 / OpenLayers.METERS_PER_INCH, //EPSG:9041 - "GoldCoastFoot": 0.30479971018150881758 / OpenLayers.METERS_PER_INCH, //EPSG:9094 - "IInch": 0.02540000000000000000 / OpenLayers.METERS_PER_INCH, - "MicroInch": 0.00002540000000000000 / OpenLayers.METERS_PER_INCH, - "Mil": 0.00000002540000000000 / OpenLayers.METERS_PER_INCH, - "Centimeter": 0.01000000000000000000 / OpenLayers.METERS_PER_INCH, - "Kilometer": 1000.00000000000000000000 / OpenLayers.METERS_PER_INCH, //EPSG:9036 - "Yard": 0.91440182880365760731 / OpenLayers.METERS_PER_INCH, - "SearsYard": 0.914398414616029 / OpenLayers.METERS_PER_INCH, //EPSG:9040 - "IndianYard": 0.91439853074444079983 / OpenLayers.METERS_PER_INCH, //EPSG:9084 - "IndianYd37": 0.91439523 / OpenLayers.METERS_PER_INCH, //EPSG:9085 - "IndianYd62": 0.9143988 / OpenLayers.METERS_PER_INCH, //EPSG:9086 - "IndianYd75": 0.9143985 / OpenLayers.METERS_PER_INCH, //EPSG:9087 - "IndianFoot": 0.30479951 / OpenLayers.METERS_PER_INCH, //EPSG:9080 - "IndianFt37": 0.30479841 / OpenLayers.METERS_PER_INCH, //EPSG:9081 - "IndianFt62": 0.3047996 / OpenLayers.METERS_PER_INCH, //EPSG:9082 - "IndianFt75": 0.3047995 / OpenLayers.METERS_PER_INCH, //EPSG:9083 - "Mile": 1609.34721869443738887477 / OpenLayers.METERS_PER_INCH, - "IYard": 0.91440000000000000000 / OpenLayers.METERS_PER_INCH, //EPSG:9096 - "IMile": 1609.34400000000000000000 / OpenLayers.METERS_PER_INCH, //EPSG:9093 - "NautM": 1852.00000000000000000000 / OpenLayers.METERS_PER_INCH, //EPSG:9030 - "Lat-66": 110943.316488932731 / OpenLayers.METERS_PER_INCH, - "Lat-83": 110946.25736872234125 / OpenLayers.METERS_PER_INCH, - "Decimeter": 0.10000000000000000000 / OpenLayers.METERS_PER_INCH, - "Millimeter": 0.00100000000000000000 / OpenLayers.METERS_PER_INCH, - "Dekameter": 10.00000000000000000000 / OpenLayers.METERS_PER_INCH, - "Decameter": 10.00000000000000000000 / OpenLayers.METERS_PER_INCH, - "Hectometer": 100.00000000000000000000 / OpenLayers.METERS_PER_INCH, - "GermanMeter": 1.0000135965 / OpenLayers.METERS_PER_INCH, //EPSG:9031 - "CaGrid": 0.999738 / OpenLayers.METERS_PER_INCH, - "ClarkeChain": 20.1166194976 / OpenLayers.METERS_PER_INCH, //EPSG:9038 - "GunterChain": 20.11684023368047 / OpenLayers.METERS_PER_INCH, //EPSG:9033 - "BenoitChain": 20.116782494375872 / OpenLayers.METERS_PER_INCH, //EPSG:9062 - "SearsChain": 20.11676512155 / OpenLayers.METERS_PER_INCH, //EPSG:9042 - "ClarkeLink": 0.201166194976 / OpenLayers.METERS_PER_INCH, //EPSG:9039 - "GunterLink": 0.2011684023368047 / OpenLayers.METERS_PER_INCH, //EPSG:9034 - "BenoitLink": 0.20116782494375872 / OpenLayers.METERS_PER_INCH, //EPSG:9063 - "SearsLink": 0.2011676512155 / OpenLayers.METERS_PER_INCH, //EPSG:9043 - "Rod": 5.02921005842012 / OpenLayers.METERS_PER_INCH, - "IntnlChain": 20.1168 / OpenLayers.METERS_PER_INCH, //EPSG:9097 - "IntnlLink": 0.201168 / OpenLayers.METERS_PER_INCH, //EPSG:9098 - "Perch": 5.02921005842012 / OpenLayers.METERS_PER_INCH, - "Pole": 5.02921005842012 / OpenLayers.METERS_PER_INCH, - "Furlong": 201.1684023368046 / OpenLayers.METERS_PER_INCH, - "Rood": 3.778266898 / OpenLayers.METERS_PER_INCH, - "CapeFoot": 0.3047972615 / OpenLayers.METERS_PER_INCH, - "Brealey": 375.00000000000000000000 / OpenLayers.METERS_PER_INCH, - "ModAmFt": 0.304812252984505969011938 / OpenLayers.METERS_PER_INCH, - "Fathom": 1.8288 / OpenLayers.METERS_PER_INCH, - "NautM-UK": 1853.184 / OpenLayers.METERS_PER_INCH, - "50kilometers": 50000.0 / OpenLayers.METERS_PER_INCH, - "150kilometers": 150000.0 / OpenLayers.METERS_PER_INCH -}); - -//unit abbreviations supported by PROJ.4 -OpenLayers.Util.extend(OpenLayers.INCHES_PER_UNIT, { - "mm": OpenLayers.INCHES_PER_UNIT["Meter"] / 1000.0, - "cm": OpenLayers.INCHES_PER_UNIT["Meter"] / 100.0, - "dm": OpenLayers.INCHES_PER_UNIT["Meter"] * 100.0, - "km": OpenLayers.INCHES_PER_UNIT["Meter"] * 1000.0, - "kmi": OpenLayers.INCHES_PER_UNIT["nmi"], //International Nautical Mile - "fath": OpenLayers.INCHES_PER_UNIT["Fathom"], //International Fathom - "ch": OpenLayers.INCHES_PER_UNIT["IntnlChain"], //International Chain - "link": OpenLayers.INCHES_PER_UNIT["IntnlLink"], //International Link - "us-in": OpenLayers.INCHES_PER_UNIT["inches"], //U.S. Surveyor's Inch - "us-ft": OpenLayers.INCHES_PER_UNIT["Foot"], //U.S. Surveyor's Foot - "us-yd": OpenLayers.INCHES_PER_UNIT["Yard"], //U.S. Surveyor's Yard - "us-ch": OpenLayers.INCHES_PER_UNIT["GunterChain"], //U.S. Surveyor's Chain - "us-mi": OpenLayers.INCHES_PER_UNIT["Mile"], //U.S. Surveyor's Statute Mile - "ind-yd": OpenLayers.INCHES_PER_UNIT["IndianYd37"], //Indian Yard - "ind-ft": OpenLayers.INCHES_PER_UNIT["IndianFt37"], //Indian Foot - "ind-ch": 20.11669506 / OpenLayers.METERS_PER_INCH //Indian Chain -}); - -/** - * Constant: DOTS_PER_INCH - * {Integer} 72 (A sensible default) - */ -OpenLayers.DOTS_PER_INCH = 72; - -/** - * Function: normalizeScale - * - * Parameters: - * scale - {float} - * - * Returns: - * {Float} A normalized scale value, in 1 / X format. - * This means that if a value less than one ( already 1/x) is passed - * in, it just returns scale directly. Otherwise, it returns - * 1 / scale - */ -OpenLayers.Util.normalizeScale = function (scale) { - var normScale = (scale > 1.0) ? (1.0 / scale) - : scale; - return normScale; -}; - -/** - * Function: getResolutionFromScale - * - * Parameters: - * scale - {Float} - * units - {String} Index into OpenLayers.INCHES_PER_UNIT hashtable. - * Default is degrees - * - * Returns: - * {Float} The corresponding resolution given passed-in scale and unit - * parameters. If the given scale is falsey, the returned resolution will - * be undefined. - */ -OpenLayers.Util.getResolutionFromScale = function (scale, units) { - var resolution; - if (scale) { - if (units == null) { - units = "degrees"; - } - var normScale = OpenLayers.Util.normalizeScale(scale); - resolution = 1 / (normScale * OpenLayers.INCHES_PER_UNIT[units] - * OpenLayers.DOTS_PER_INCH); - } - return resolution; -}; - -/** - * Function: getScaleFromResolution - * - * Parameters: - * resolution - {Float} - * units - {String} Index into OpenLayers.INCHES_PER_UNIT hashtable. - * Default is degrees - * - * Returns: - * {Float} The corresponding scale given passed-in resolution and unit - * parameters. - */ -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; -}; - -/** - * Function: pagePosition - * Calculates the position of an element on the page (see - * http://code.google.com/p/doctype/wiki/ArticlePageOffset) - * - * OpenLayers.Util.pagePosition is based on Yahoo's getXY method, which is - * Copyright (c) 2006, Yahoo! Inc. - * All rights reserved. - * - * Redistribution and use of this software in source and binary forms, with or - * without modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * * Neither the name of Yahoo! Inc. nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission of Yahoo! Inc. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * Parameters: - * forElement - {DOMElement} - * - * Returns: - * {Array} two item array, Left value then Top value. - */ -OpenLayers.Util.pagePosition = function(forElement) { - // NOTE: If element is hidden (display none or disconnected or any the - // ancestors are hidden) we get (0,0) by default but we still do the - // accumulation of scroll position. - - var pos = [0, 0]; - var viewportElement = OpenLayers.Util.getViewportElement(); - if (!forElement || forElement == window || forElement == viewportElement) { - // viewport is always at 0,0 as that defined the coordinate system for - // this function - this avoids special case checks in the code below - return pos; - } - - // Gecko browsers normally use getBoxObjectFor to calculate the position. - // When invoked for an element with an implicit absolute position though it - // can be off by one. Therefore the recursive implementation is used in - // those (relatively rare) cases. - var BUGGY_GECKO_BOX_OBJECT = - OpenLayers.IS_GECKO && document.getBoxObjectFor && - OpenLayers.Element.getStyle(forElement, 'position') == 'absolute' && - (forElement.style.top == '' || forElement.style.left == ''); - - var parent = null; - var box; - - if (forElement.getBoundingClientRect) { // IE - box = forElement.getBoundingClientRect(); - var scrollTop = window.pageYOffset || viewportElement.scrollTop; - var scrollLeft = window.pageXOffset || viewportElement.scrollLeft; - - pos[0] = box.left + scrollLeft; - pos[1] = box.top + scrollTop; - - } else if (document.getBoxObjectFor && !BUGGY_GECKO_BOX_OBJECT) { // gecko - // Gecko ignores the scroll values for ancestors, up to 1.9. See: - // https://bugzilla.mozilla.org/show_bug.cgi?id=328881 and - // https://bugzilla.mozilla.org/show_bug.cgi?id=330619 - - box = document.getBoxObjectFor(forElement); - var vpBox = document.getBoxObjectFor(viewportElement); - pos[0] = box.screenX - vpBox.screenX; - pos[1] = box.screenY - vpBox.screenY; - - } else { // safari/opera - pos[0] = forElement.offsetLeft; - pos[1] = forElement.offsetTop; - parent = forElement.offsetParent; - if (parent != forElement) { - while (parent) { - pos[0] += parent.offsetLeft; - pos[1] += parent.offsetTop; - parent = parent.offsetParent; - } - } - - var browser = OpenLayers.BROWSER_NAME; - - // opera & (safari absolute) incorrectly account for body offsetTop - if (browser == "opera" || (browser == "safari" && - OpenLayers.Element.getStyle(forElement, 'position') == 'absolute')) { - pos[1] -= document.body.offsetTop; - } - - // accumulate the scroll positions for everything but the body element - parent = forElement.offsetParent; - while (parent && parent != document.body) { - pos[0] -= parent.scrollLeft; - // see https://bugs.opera.com/show_bug.cgi?id=249965 - if (browser != "opera" || parent.tagName != 'TR') { - pos[1] -= parent.scrollTop; - } - parent = parent.offsetParent; - } - } - - return pos; -}; - -/** - * Function: getViewportElement - * Returns die viewport element of the document. The viewport element is - * usually document.documentElement, except in IE,where it is either - * document.body or document.documentElement, depending on the document's - * compatibility mode (see - * http://code.google.com/p/doctype/wiki/ArticleClientViewportElement) - * - * Returns: - * {DOMElement} - */ -OpenLayers.Util.getViewportElement = function() { - var viewportElement = arguments.callee.viewportElement; - if (viewportElement == undefined) { - viewportElement = (OpenLayers.BROWSER_NAME == "msie" && - document.compatMode != 'CSS1Compat') ? document.body : - document.documentElement; - arguments.callee.viewportElement = viewportElement; - } - return viewportElement; -}; - -/** - * Function: isEquivalentUrl - * Test two URLs for equivalence. - * - * Setting 'ignoreCase' allows for case-independent comparison. - * - * Comparison is based on: - * - Protocol - * - Host (evaluated without the port) - * - Port (set 'ignorePort80' to ignore "80" values) - * - Hash ( set 'ignoreHash' to disable) - * - Pathname (for relative <-> absolute comparison) - * - Arguments (so they can be out of order) - * - * Parameters: - * url1 - {String} - * url2 - {String} - * options - {Object} Allows for customization of comparison: - * 'ignoreCase' - Default is True - * 'ignorePort80' - Default is True - * 'ignoreHash' - Default is True - * - * Returns: - * {Boolean} Whether or not the two URLs are equivalent - */ -OpenLayers.Util.isEquivalentUrl = function(url1, url2, options) { - options = options || {}; - - OpenLayers.Util.applyDefaults(options, { - ignoreCase: true, - ignorePort80: true, - ignoreHash: true, - splitArgs: false - }); - - var urlObj1 = OpenLayers.Util.createUrlObject(url1, options); - var urlObj2 = OpenLayers.Util.createUrlObject(url2, options); - - //compare all keys except for "args" (treated below) - for(var key in urlObj1) { - if(key !== "args") { - if(urlObj1[key] != urlObj2[key]) { - return false; - } - } - } - - // compare search args - irrespective of order - for(var key in urlObj1.args) { - if(urlObj1.args[key] != urlObj2.args[key]) { - return false; - } - delete urlObj2.args[key]; - } - // urlObj2 shouldn't have any args left - for(var key in urlObj2.args) { - return false; - } - - return true; -}; - -/** - * Function: createUrlObject - * - * Parameters: - * url - {String} - * options - {Object} A hash of options. - * - * Valid options: - * ignoreCase - {Boolean} lowercase url, - * ignorePort80 - {Boolean} don't include explicit port if port is 80, - * ignoreHash - {Boolean} Don't include part of url after the hash (#). - * splitArgs - {Boolean} Split comma delimited params into arrays? Default is - * true. - * - * Returns: - * {Object} An object with separate url, a, port, host, and args parsed out - * and ready for comparison - */ -OpenLayers.Util.createUrlObject = function(url, options) { - options = options || {}; - - // deal with relative urls first - if(!(/^\w+:\/\//).test(url)) { - var loc = window.location; - var port = loc.port ? ":" + loc.port : ""; - var fullUrl = loc.protocol + "//" + loc.host.split(":").shift() + port; - if(url.indexOf("/") === 0) { - // full pathname - url = fullUrl + url; - } else { - // relative to current path - var parts = loc.pathname.split("/"); - parts.pop(); - url = fullUrl + parts.join("/") + "/" + url; - } - } - - if (options.ignoreCase) { - url = url.toLowerCase(); - } - - var a = document.createElement('a'); - a.href = url; - - var urlObject = {}; - - //host (without port) - urlObject.host = a.host.split(":").shift(); - - //protocol - urlObject.protocol = a.protocol; - - //port (get uniform browser behavior with port 80 here) - if(options.ignorePort80) { - urlObject.port = (a.port == "80" || a.port == "0") ? "" : a.port; - } else { - urlObject.port = (a.port == "" || a.port == "0") ? "80" : a.port; - } - - //hash - urlObject.hash = (options.ignoreHash || a.hash === "#") ? "" : a.hash; - - //args - var queryString = a.search; - if (!queryString) { - var qMark = url.indexOf("?"); - queryString = (qMark != -1) ? url.substr(qMark) : ""; - } - urlObject.args = OpenLayers.Util.getParameters(queryString, - {splitArgs: options.splitArgs}); - - // pathname - // - // This is a workaround for Internet Explorer where - // window.location.pathname has a leading "/", but - // a.pathname has no leading "/". - urlObject.pathname = (a.pathname.charAt(0) == "/") ? a.pathname : "/" + a.pathname; - - return urlObject; -}; - -/** - * Function: removeTail - * Takes a url and removes everything after the ? and # - * - * Parameters: - * url - {String} The url to process - * - * Returns: - * {String} The string with all queryString and Hash removed - */ -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; -}; - -/** - * Constant: IS_GECKO - * {Boolean} True if the userAgent reports the browser to use the Gecko engine - */ -OpenLayers.IS_GECKO = (function() { - var ua = navigator.userAgent.toLowerCase(); - return ua.indexOf("webkit") == -1 && ua.indexOf("gecko") != -1; -})(); - -/** - * Constant: CANVAS_SUPPORTED - * {Boolean} True if canvas 2d is supported. - */ -OpenLayers.CANVAS_SUPPORTED = (function() { - var elem = document.createElement('canvas'); - return !!(elem.getContext && elem.getContext('2d')); -})(); - -/** - * Constant: BROWSER_NAME - * {String} - * A substring of the navigator.userAgent property. Depending on the userAgent - * property, this will be the empty string or one of the following: - * * "opera" -- Opera - * * "msie" -- Internet Explorer - * * "safari" -- Safari - * * "firefox" -- Firefox - * * "mozilla" -- Mozilla - */ -OpenLayers.BROWSER_NAME = (function() { - var name = ""; - var ua = navigator.userAgent.toLowerCase(); - if (ua.indexOf("opera") != -1) { - name = "opera"; - } else if (ua.indexOf("msie") != -1) { - name = "msie"; - } else if (ua.indexOf("safari") != -1) { - name = "safari"; - } else if (ua.indexOf("mozilla") != -1) { - if (ua.indexOf("firefox") != -1) { - name = "firefox"; - } else { - name = "mozilla"; - } - } - return name; -})(); - -/** - * Function: getBrowserName - * - * Returns: - * {String} A string which specifies which is the current - * browser in which we are running. - * - * Currently-supported browser detection and codes: - * * 'opera' -- Opera - * * 'msie' -- Internet Explorer - * * 'safari' -- Safari - * * 'firefox' -- Firefox - * * 'mozilla' -- Mozilla - * - * If we are unable to property identify the browser, we - * return an empty string. - */ -OpenLayers.Util.getBrowserName = function() { - return OpenLayers.BROWSER_NAME; -}; - -/** - * Method: getRenderedDimensions - * Renders the contentHTML offscreen to determine actual dimensions for - * popup sizing. As we need layout to determine dimensions the content - * is rendered -9999px to the left and absolute to ensure the - * 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} - * - * Allowed Options: - * displayClass - {String} Optional parameter. A CSS class name(s) string - * to provide the CSS context of the rendered content. - * containerElement - {DOMElement} Optional parameter. Insert the HTML to - * this node instead of the body root when calculating dimensions. - * - * Returns: - * {<OpenLayers.Size>} - */ -OpenLayers.Util.getRenderedDimensions = function(contentHTML, size, options) { - - var w, h; - - // create temp container div with restricted size - var container = document.createElement("div"); - container.style.visibility = "hidden"; - - var containerElement = (options && options.containerElement) - ? options.containerElement : document.body; - - // Opera and IE7 can't handle a node with position:aboslute if it inherits - // position:absolute from a parent. - var parentHasPositionAbsolute = false; - var superContainer = null; - var parent = containerElement; - while (parent && parent.tagName.toLowerCase()!="body") { - var parentPosition = OpenLayers.Element.getStyle(parent, "position"); - if(parentPosition == "absolute") { - parentHasPositionAbsolute = true; - break; - } else if (parentPosition && parentPosition != "static") { - break; - } - parent = parent.parentNode; - } - if(parentHasPositionAbsolute && (containerElement.clientHeight === 0 || - containerElement.clientWidth === 0) ){ - superContainer = document.createElement("div"); - superContainer.style.visibility = "hidden"; - superContainer.style.position = "absolute"; - superContainer.style.overflow = "visible"; - superContainer.style.width = document.body.clientWidth + "px"; - superContainer.style.height = document.body.clientHeight + "px"; - superContainer.appendChild(container); - } - container.style.position = "absolute"; - - //fix a dimension, if specified. - 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"; - } - } - - //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"); - content.innerHTML = contentHTML; - - // we need overflow visible when calculating the size - content.style.overflow = "visible"; - if (content.childNodes) { - for (var i=0, l=content.childNodes.length; i<l; i++) { - if (!content.childNodes[i].style) continue; - content.childNodes[i].style.overflow = "visible"; - } - } - - // add content to restricted container - container.appendChild(content); - - // append container to body for rendering - if (superContainer) { - containerElement.appendChild(superContainer); - } else { - containerElement.appendChild(container); - } - - // calculate scroll width of content and add corners and shadow width - if (!w) { - w = parseInt(content.scrollWidth); - - // update container width to allow height to adjust - container.style.width = w + "px"; - } - // capture height and add shadow and corner image widths - if (!h) { - h = parseInt(content.scrollHeight); - } - - // remove elements - container.removeChild(content); - if (superContainer) { - superContainer.removeChild(container); - containerElement.removeChild(superContainer); - } else { - containerElement.removeChild(container); - } - - return new OpenLayers.Size(w, h); -}; - -/** - * APIFunction: getScrollbarWidth - * This function has been modified by the OpenLayers from the original version, - * written by Matthew Eernisse and released under the Apache 2 - * license here: - * - * http://www.fleegix.org/articles/2006/05/30/getting-the-scrollbar-width-in-pixels - * - * It has been modified simply to cache its value, since it is physically - * impossible that this code could ever run in more than one browser at - * once. - * - * Returns: - * {Integer} - */ -OpenLayers.Util.getScrollbarWidth = function() { - - var scrollbarWidth = OpenLayers.Util._scrollbarWidth; - - if (scrollbarWidth == null) { - var scr = null; - var inn = null; - var wNoScroll = 0; - var wScroll = 0; - - // Outer scrolling div - scr = document.createElement('div'); - scr.style.position = 'absolute'; - scr.style.top = '-1000px'; - scr.style.left = '-1000px'; - scr.style.width = '100px'; - scr.style.height = '50px'; - // Start with no scrollbar - scr.style.overflow = 'hidden'; - - // Inner content div - inn = document.createElement('div'); - inn.style.width = '100%'; - inn.style.height = '200px'; - - // Put the inner div in the scrolling div - scr.appendChild(inn); - // Append the scrolling div to the doc - document.body.appendChild(scr); - - // Width of the inner div sans scrollbar - wNoScroll = inn.offsetWidth; - - // Add the scrollbar - scr.style.overflow = 'scroll'; - // Width of the inner div width scrollbar - wScroll = inn.offsetWidth; - - // Remove the scrolling div from the doc - document.body.removeChild(document.body.lastChild); - - // Pixel width of the scroller - OpenLayers.Util._scrollbarWidth = (wNoScroll - wScroll); - scrollbarWidth = OpenLayers.Util._scrollbarWidth; - } - - return scrollbarWidth; -}; - -/** - * APIFunction: getFormattedLonLat - * This function will return latitude or longitude value formatted as - * - * Parameters: - * coordinate - {Float} the coordinate value to be formatted - * axis - {String} value of either 'lat' or 'lon' to indicate which axis is to - * to be formatted (default = lat) - * dmsOption - {String} specify the precision of the output can be one of: - * 'dms' show degrees minutes and seconds - * 'dm' show only degrees and minutes - * 'd' show only degrees - * - * Returns: - * {String} the coordinate value formatted as a string - */ -OpenLayers.Util.getFormattedLonLat = function(coordinate, axis, dmsOption) { - if (!dmsOption) { - dmsOption = 'dms'; //default to show degree, minutes, seconds - } - - coordinate = (coordinate+540)%360 - 180; // normalize for sphere being round - - var abscoordinate = Math.abs(coordinate); - var coordinatedegrees = Math.floor(abscoordinate); - - var coordinateminutes = (abscoordinate - coordinatedegrees)/(1/60); - var tempcoordinateminutes = coordinateminutes; - coordinateminutes = Math.floor(coordinateminutes); - var coordinateseconds = (tempcoordinateminutes - coordinateminutes)/(1/60); - coordinateseconds = Math.round(coordinateseconds*10); - coordinateseconds /= 10; - - if( coordinateseconds >= 60) { - coordinateseconds -= 60; - coordinateminutes += 1; - if( coordinateminutes >= 60) { - coordinateminutes -= 60; - coordinatedegrees += 1; - } - } - - if( coordinatedegrees < 10 ) { - coordinatedegrees = "0" + coordinatedegrees; - } - var str = coordinatedegrees + "\u00B0"; - - if (dmsOption.indexOf('dm') >= 0) { - if( coordinateminutes < 10 ) { - coordinateminutes = "0" + coordinateminutes; - } - str += coordinateminutes + "'"; - - if (dmsOption.indexOf('dms') >= 0) { - if( coordinateseconds < 10 ) { - coordinateseconds = "0" + coordinateseconds; - } - str += coordinateseconds + '"'; - } - } - - if (axis == "lon") { - str += coordinate < 0 ? OpenLayers.i18n("W") : OpenLayers.i18n("E"); - } else { - str += coordinate < 0 ? OpenLayers.i18n("S") : OpenLayers.i18n("N"); - } - return str; -}; - -/* ====================================================================== - OpenLayers/Events.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - - -/** - * @requires OpenLayers/Util.js - */ - -/** - * Namespace: OpenLayers.Event - * Utility functions for event handling. - */ -OpenLayers.Event = { - - /** - * Property: observers - * {Object} A hashtable cache of the event observers. Keyed by - * element._eventCacheID - */ - observers: false, - - /** - * Constant: KEY_SPACE - * {int} - */ - KEY_SPACE: 32, - - /** - * Constant: KEY_BACKSPACE - * {int} - */ - KEY_BACKSPACE: 8, - - /** - * Constant: KEY_TAB - * {int} - */ - KEY_TAB: 9, - - /** - * Constant: KEY_RETURN - * {int} - */ - KEY_RETURN: 13, - - /** - * Constant: KEY_ESC - * {int} - */ - KEY_ESC: 27, - - /** - * Constant: KEY_LEFT - * {int} - */ - KEY_LEFT: 37, - - /** - * Constant: KEY_UP - * {int} - */ - KEY_UP: 38, - - /** - * Constant: KEY_RIGHT - * {int} - */ - KEY_RIGHT: 39, - - /** - * Constant: KEY_DOWN - * {int} - */ - KEY_DOWN: 40, - - /** - * Constant: KEY_DELETE - * {int} - */ - KEY_DELETE: 46, - - - /** - * Method: element - * Cross browser event element detection. - * - * Parameters: - * event - {Event} - * - * Returns: - * {DOMElement} The element that caused the event - */ - element: function(event) { - return event.target || event.srcElement; - }, - - /** - * Method: isSingleTouch - * Determine whether event was caused by a single touch - * - * Parameters: - * event - {Event} - * - * Returns: - * {Boolean} - */ - isSingleTouch: function(event) { - return event.touches && event.touches.length == 1; - }, - - /** - * Method: isMultiTouch - * Determine whether event was caused by a multi touch - * - * Parameters: - * event - {Event} - * - * Returns: - * {Boolean} - */ - isMultiTouch: function(event) { - return event.touches && event.touches.length > 1; - }, - - /** - * Method: isLeftClick - * Determine whether event was caused by a left click. - * - * Parameters: - * event - {Event} - * - * Returns: - * {Boolean} - */ - isLeftClick: function(event) { - return (((event.which) && (event.which == 1)) || - ((event.button) && (event.button == 1))); - }, - - /** - * 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. - * - * Parameters: - * event - {Event} - * allowDefault - {Boolean} If true, we stop the event chain but - * still allow the default browser behaviour (text selection, - * radio-button clicking, etc). Default is false. - */ - stop: function(event, allowDefault) { - - if (!allowDefault) { - OpenLayers.Event.preventDefault(event); - } - - if (event.stopPropagation) { - event.stopPropagation(); - } else { - event.cancelBubble = true; - } - }, - - /** - * Method: preventDefault - * Cancels the event if it is cancelable, without stopping further - * propagation of the event. - * - * Parameters: - * event - {Event} - */ - preventDefault: function(event) { - if (event.preventDefault) { - event.preventDefault(); - } else { - event.returnValue = false; - } - }, - - /** - * Method: findElement - * - * Parameters: - * event - {Event} - * tagName - {String} - * - * Returns: - * {DOMElement} The first node with the given tagName, starting from the - * node the event was triggered on and traversing the DOM upwards - */ - 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; - }, - - /** - * Method: observe - * - * Parameters: - * elementParam - {DOMElement || String} - * name - {String} - * observer - {function} - * useCapture - {Boolean} - */ - 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 observers cache has not yet been created, create it - if (!this.observers) { - this.observers = {}; - } - - //if not already assigned, make a new unique cache ID - if (!element._eventCacheID) { - var idPrefix = "eventCacheID_"; - if (element.id) { - idPrefix = element.id + "_" + idPrefix; - } - element._eventCacheID = OpenLayers.Util.createUniqueID(idPrefix); - } - - var cacheID = element._eventCacheID; - - //if there is not yet a hash entry for this element, add one - if (!this.observers[cacheID]) { - this.observers[cacheID] = []; - } - - //add a new observer to this element's list - this.observers[cacheID].push({ - 'element': element, - 'name': name, - 'observer': observer, - 'useCapture': useCapture - }); - - //add the actual browser event listener - if (element.addEventListener) { - element.addEventListener(name, observer, useCapture); - } else if (element.attachEvent) { - element.attachEvent('on' + name, observer); - } - }, - - /** - * Method: stopObservingElement - * Given the id of an element to stop observing, cycle through the - * element's cached observers, calling stopObserving on each one, - * skipping those entries which can no longer be removed. - * - * parameters: - * elementParam - {DOMElement || String} - */ - stopObservingElement: function(elementParam) { - var element = OpenLayers.Util.getElement(elementParam); - var cacheID = element._eventCacheID; - - this._removeElementObservers(OpenLayers.Event.observers[cacheID]); - }, - - /** - * Method: _removeElementObservers - * - * Parameters: - * elementObservers - {Array(Object)} Array of (element, name, - * observer, usecapture) objects, - * taken directly from hashtable - */ - _removeElementObservers: function(elementObservers) { - if (elementObservers) { - for(var i = elementObservers.length-1; i >= 0; i--) { - var entry = elementObservers[i]; - OpenLayers.Event.stopObserving.apply(this, [ - entry.element, entry.name, entry.observer, entry.useCapture - ]); - } - } - }, - - /** - * Method: stopObserving - * - * Parameters: - * elementParam - {DOMElement || String} - * name - {String} - * observer - {function} - * useCapture - {Boolean} - * - * Returns: - * {Boolean} Whether or not the event observer was removed - */ - stopObserving: function(elementParam, name, observer, useCapture) { - useCapture = useCapture || false; - - var element = OpenLayers.Util.getElement(elementParam); - var cacheID = element._eventCacheID; - - if (name == 'keypress') { - if ( navigator.appVersion.match(/Konqueror|Safari|KHTML/) || - element.detachEvent) { - name = 'keydown'; - } - } - - // find element's entry in this.observers cache and remove it - var foundEntry = false; - var elementObservers = OpenLayers.Event.observers[cacheID]; - if (elementObservers) { - - // find the specific event type in the element's list - var i=0; - while(!foundEntry && i < elementObservers.length) { - var cacheEntry = elementObservers[i]; - - if ((cacheEntry.name == name) && - (cacheEntry.observer == observer) && - (cacheEntry.useCapture == useCapture)) { - - elementObservers.splice(i, 1); - if (elementObservers.length == 0) { - delete OpenLayers.Event.observers[cacheID]; - } - foundEntry = true; - break; - } - i++; - } - } - - //actually remove the event listener from browser - if (foundEntry) { - if (element.removeEventListener) { - element.removeEventListener(name, observer, useCapture); - } else if (element && element.detachEvent) { - element.detachEvent('on' + name, observer); - } - } - return foundEntry; - }, - - /** - * Method: unloadCache - * Cycle through all the element entries in the events cache and call - * stopObservingElement on each. - */ - unloadCache: function() { - // check for OpenLayers.Event before checking for observers, because - // OpenLayers.Event may be undefined in IE if no map instance was - // created - 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" -}; - -/* prevent memory leaks in IE */ -OpenLayers.Event.observe(window, 'unload', OpenLayers.Event.unloadCache, false); - -/** - * Class: OpenLayers.Events - */ -OpenLayers.Events = OpenLayers.Class({ - - /** - * Constant: BROWSER_EVENTS - * {Array(String)} supported events - */ - BROWSER_EVENTS: [ - "mouseover", "mouseout", - "mousedown", "mouseup", "mousemove", - "click", "dblclick", "rightclick", "dblrightclick", - "resize", "focus", "blur", - "touchstart", "touchmove", "touchend", - "keydown" - ], - - /** - * Property: listeners - * {Object} Hashtable of Array(Function): events listener functions - */ - listeners: null, - - /** - * Property: object - * {Object} the code object issuing application events - */ - object: null, - - /** - * Property: element - * {DOMElement} the DOM element receiving browser events - */ - element: null, - - /** - * Property: eventHandler - * {Function} bound event handler attached to elements - */ - eventHandler: null, - - /** - * APIProperty: fallThrough - * {Boolean} - */ - 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, - - /** - * APIProperty: extensions - * {Object} Event extensions registered with this instance. Keys are - * event types, values are {OpenLayers.Events.*} extension instances or - * {Boolean} for events that an instantiated extension provides in - * addition to the one it was created for. - * - * Extensions create an event in addition to browser events, which usually - * fires when a sequence of browser events is completed. Extensions are - * automatically instantiated when a listener is registered for an event - * provided by an extension. - * - * Extensions are created in the <OpenLayers.Events> namespace using - * <OpenLayers.Class>, and named after the event they provide. - * The constructor receives the target <OpenLayers.Events> instance as - * argument. Extensions that need to capture browser events before they - * propagate can register their listeners events using <register>, with - * {extension: true} as 4th argument. - * - * If an extension creates more than one event, an alias for each event - * type should be created and reference the same class. The constructor - * should set a reference in the target's extensions registry to itself. - * - * Below is a minimal extension that provides the "foostart" and "fooend" - * event types, which replace the native "click" event type if clicked on - * an element with the css class "foo": - * - * (code) - * OpenLayers.Events.foostart = OpenLayers.Class({ - * initialize: function(target) { - * this.target = target; - * this.target.register("click", this, this.doStuff, {extension: true}); - * // only required if extension provides more than one event type - * this.target.extensions["foostart"] = true; - * this.target.extensions["fooend"] = true; - * }, - * destroy: function() { - * var target = this.target; - * target.unregister("click", this, this.doStuff); - * delete this.target; - * // only required if extension provides more than one event type - * delete target.extensions["foostart"]; - * delete target.extensions["fooend"]; - * }, - * doStuff: function(evt) { - * var propagate = true; - * if (OpenLayers.Event.element(evt).className === "foo") { - * propagate = false; - * var target = this.target; - * target.triggerEvent("foostart"); - * window.setTimeout(function() { - * target.triggerEvent("fooend"); - * }, 1000); - * } - * return propagate; - * } - * }); - * // only required if extension provides more than one event type - * OpenLayers.Events.fooend = OpenLayers.Events.foostart; - * (end) - * - */ - extensions: null, - - /** - * Property: extensionCount - * {Object} Keys are event types (like in <listeners>), values are the - * number of extension listeners for each event type. - */ - extensionCount: null, - - /** - * Method: clearMouseListener - * A version of <clearMouseCache> that is bound to this instance so that - * it can be used with <OpenLayers.Event.observe> and - * <OpenLayers.Event.stopObserving>. - */ - clearMouseListener: null, - - /** - * Constructor: OpenLayers.Events - * Construct an OpenLayers.Events object. - * - * Parameters: - * object - {Object} The js object to which this Events object is being added - * element - {DOMElement} A dom element to respond to browser events - * eventTypes - {Array(String)} Deprecated. Array of custom application - * events. A listener may be registered for any named event, regardless - * of the values provided here. - * 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, options) { - OpenLayers.Util.extend(this, options); - this.object = object; - this.fallThrough = fallThrough; - this.listeners = {}; - this.extensions = {}; - this.extensionCount = {}; - this._msTouches = []; - - // if a dom element is specified, add a listeners list - // for browser events on the element and register them - if (element != null) { - this.attachToElement(element); - } - }, - - /** - * APIMethod: destroy - */ - destroy: function () { - for (var e in this.extensions) { - if (typeof this.extensions[e] !== "boolean") { - this.extensions[e].destroy(); - } - } - this.extensions = null; - if (this.element) { - OpenLayers.Event.stopObservingElement(this.element); - if(this.element.hasScrollEvent) { - OpenLayers.Event.stopObserving( - window, "scroll", this.clearMouseListener - ); - } - } - this.element = null; - - this.listeners = null; - this.object = null; - this.fallThrough = null; - this.eventHandler = null; - }, - - /** - * APIMethod: addEventType - * Deprecated. Any event can be triggered without adding it first. - * - * Parameters: - * eventName - {String} - */ - addEventType: function(eventName) { - }, - - /** - * Method: attachToElement - * - * Parameters: - * element - {HTMLDOMElement} a DOM element to attach browser events to - */ - attachToElement: function (element) { - if (this.element) { - OpenLayers.Event.stopObservingElement(this.element); - } else { - // keep a bound copy of handleBrowserEvent() so that we can - // pass the same function to both Event.observe() and .stopObserving() - this.eventHandler = OpenLayers.Function.bindAsEventListener( - this.handleBrowserEvent, this - ); - - // to be used with observe and stopObserving - this.clearMouseListener = OpenLayers.Function.bind( - this.clearMouseCache, this - ); - } - this.element = element; - var msTouch = !!window.navigator.msMaxTouchPoints; - var type; - for (var i = 0, len = this.BROWSER_EVENTS.length; i < len; i++) { - type = this.BROWSER_EVENTS[i]; - // register the event cross-browser - OpenLayers.Event.observe(element, type, this.eventHandler - ); - if (msTouch && type.indexOf('touch') === 0) { - this.addMsTouchListener(element, type, this.eventHandler); - } - } - // disable dragstart in IE so that mousedown/move/up works normally - OpenLayers.Event.observe(element, "dragstart", OpenLayers.Event.stop); - }, - - /** - * APIMethod: on - * Convenience method for registering listeners with a common scope. - * Internally, this method calls <register> as shown in the examples - * below. - * - * Example use: - * (code) - * // register a single listener for the "loadstart" event - * events.on({"loadstart": loadStartListener}); - * - * // this is equivalent to the following - * events.register("loadstart", undefined, loadStartListener); - * - * // register multiple listeners to be called with the same `this` object - * events.on({ - * "loadstart": loadStartListener, - * "loadend": loadEndListener, - * scope: object - * }); - * - * // this is equivalent to the following - * events.register("loadstart", object, loadStartListener); - * events.register("loadend", object, loadEndListener); - * (end) - * - * Parameters: - * object - {Object} - */ - on: function(object) { - for(var type in object) { - if(type != "scope" && object.hasOwnProperty(type)) { - this.register(type, object.scope, object[type]); - } - } - }, - - /** - * APIMethod: register - * Register an event on the events object. - * - * When the event is triggered, the 'func' function will be called, in the - * context of 'obj'. Imagine we were to register an event, specifying an - * OpenLayers.Bounds Object as 'obj'. When the event is triggered, the - * context in the callback function will be our Bounds object. This means - * that within our callback function, we can access the properties and - * methods of the Bounds object through the "this" variable. So our - * callback could execute something like: - * : leftStr = "Left: " + this.left; - * - * or - * - * : centerStr = "Center: " + this.getCenterLonLat(); - * - * Parameters: - * type - {String} Name of the event to register - * obj - {Object} The object to bind the context to for the callback#. - * If no object is specified, default is the Events's 'object' property. - * func - {Function} The callback function. If no callback is - * specified, this function does nothing. - * priority - {Boolean|Object} If true, adds the new listener to the - * *front* of the events queue instead of to the end. - * - * Valid options for priority: - * extension - {Boolean} If true, then the event will be registered as - * extension event. Extension events are handled before all other - * events. - */ - register: function (type, obj, func, priority) { - if (type in OpenLayers.Events && !this.extensions[type]) { - this.extensions[type] = new OpenLayers.Events[type](this); - } - if (func != null) { - if (obj == null) { - obj = this.object; - } - var listeners = this.listeners[type]; - if (!listeners) { - listeners = []; - this.listeners[type] = listeners; - this.extensionCount[type] = 0; - } - var listener = {obj: obj, func: func}; - if (priority) { - listeners.splice(this.extensionCount[type], 0, listener); - if (typeof priority === "object" && priority.extension) { - this.extensionCount[type]++; - } - } else { - listeners.push(listener); - } - } - }, - - /** - * APIMethod: registerPriority - * Same as register() but adds the new listener to the *front* of the - * events queue instead of to the end. - * - * TODO: get rid of this in 3.0 - Decide whether listeners should be - * called in the order they were registered or in reverse order. - * - * - * Parameters: - * type - {String} Name of the event to register - * obj - {Object} The object to bind the context to for the callback#. - * If no object is specified, default is the Events's - * 'object' property. - * func - {Function} The callback function. If no callback is - * specified, this function does nothing. - */ - registerPriority: function (type, obj, func) { - this.register(type, obj, func, true); - }, - - /** - * APIMethod: un - * Convenience method for unregistering listeners with a common scope. - * Internally, this method calls <unregister> as shown in the examples - * below. - * - * Example use: - * (code) - * // unregister a single listener for the "loadstart" event - * events.un({"loadstart": loadStartListener}); - * - * // this is equivalent to the following - * events.unregister("loadstart", undefined, loadStartListener); - * - * // unregister multiple listeners with the same `this` object - * events.un({ - * "loadstart": loadStartListener, - * "loadend": loadEndListener, - * scope: object - * }); - * - * // this is equivalent to the following - * events.unregister("loadstart", object, loadStartListener); - * events.unregister("loadend", object, loadEndListener); - * (end) - */ - un: function(object) { - for(var type in object) { - if(type != "scope" && object.hasOwnProperty(type)) { - this.unregister(type, object.scope, object[type]); - } - } - }, - - /** - * APIMethod: unregister - * - * Parameters: - * type - {String} - * obj - {Object} If none specified, defaults to this.object - * func - {Function} - */ - unregister: function (type, obj, func) { - if (obj == null) { - obj = this.object; - } - 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; - } - } - } - }, - - /** - * Method: remove - * Remove all listeners for a given event type. If type is not registered, - * does nothing. - * - * Parameters: - * type - {String} - */ - remove: function(type) { - if (this.listeners[type] != null) { - this.listeners[type] = []; - } - }, - - /** - * APIMethod: triggerEvent - * Trigger a specified registered event. - * - * Parameters: - * type - {String} - * evt - {Event || Object} will be passed to the listeners. - * - * Returns: - * {Boolean} The last listener return. If a listener returns false, the - * chain of listeners will stop getting called. - */ - triggerEvent: function (type, evt) { - var listeners = this.listeners[type]; - - // fast path - if(!listeners || listeners.length == 0) { - return undefined; - } - - // prep evt object with object & div references - if (evt == null) { - evt = {}; - } - evt.object = this.object; - evt.element = this.element; - if(!evt.type) { - evt.type = type; - } - - // execute all callbacks registered for specified type - // get a clone of the listeners array to - // allow for splicing during callbacks - listeners = listeners.slice(); - var continueChain; - 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]); - - if ((continueChain != undefined) && (continueChain == false)) { - // if callback returns false, execute no more callbacks. - break; - } - } - // don't fall through to other DOM elements - if (!this.fallThrough) { - OpenLayers.Event.stop(evt, true); - } - return continueChain; - }, - - /** - * Method: handleBrowserEvent - * Basically just a wrapper to the triggerEvent() function, but takes - * care to set a property 'xy' on the event with the current mouse - * position. - * - * Parameters: - * evt - {Event} - */ - handleBrowserEvent: function (evt) { - var type = evt.type, listeners = this.listeners[type]; - if(!listeners || listeners.length == 0) { - // noone's listening, bail out - return; - } - // add clientX & clientY to all events - corresponds to average x, y - var touches = evt.touches; - if (touches && touches[0]) { - var x = 0; - var y = 0; - var num = touches.length; - var touch; - for (var i=0; i<num; ++i) { - touch = this.getTouchClientXY(touches[i]); - x += touch.clientX; - y += touch.clientY; - } - evt.clientX = x / num; - evt.clientY = y / num; - } - if (this.includeXY) { - evt.xy = this.getMousePosition(evt); - } - this.triggerEvent(type, evt); - }, - - /** - * Method: getTouchClientXY - * WebKit has a few bugs for clientX/clientY. This method detects them - * and calculate the correct values. - * - * Parameters: - * evt - {Touch} a Touch object from a TouchEvent - * - * Returns: - * {Object} An object with only clientX and clientY properties with the - * calculated values. - */ - getTouchClientXY: function (evt) { - // olMochWin is to override window, used for testing - var win = window.olMockWin || window, - winPageX = win.pageXOffset, - winPageY = win.pageYOffset, - x = evt.clientX, - y = evt.clientY; - - if (evt.pageY === 0 && Math.floor(y) > Math.floor(evt.pageY) || - evt.pageX === 0 && Math.floor(x) > Math.floor(evt.pageX)) { - // iOS4 include scroll offset in clientX/Y - x = x - winPageX; - y = y - winPageY; - } else if (y < (evt.pageY - winPageY) || x < (evt.pageX - winPageX) ) { - // Some Android browsers have totally bogus values for clientX/Y - // when scrolling/zooming a page - x = evt.pageX - winPageX; - y = evt.pageY - winPageY; - } - - evt.olClientX = x; - evt.olClientY = y; - - return { - clientX: x, - clientY: y - }; - }, - - /** - * 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: - * evt - {Event} - * - * Returns: - * {<OpenLayers.Pixel>} The current xy coordinate of the mouse, adjusted - * for offsets - */ - getMousePosition: function (evt) { - if (!this.includeXY) { - this.clearMouseCache(); - } else if (!this.element.hasScrollEvent) { - OpenLayers.Event.observe(window, "scroll", this.clearMouseListener); - this.element.hasScrollEvent = true; - } - - if (!this.element.scrolls) { - var viewportElement = OpenLayers.Util.getViewportElement(); - this.element.scrolls = [ - window.pageXOffset || viewportElement.scrollLeft, - window.pageYOffset || viewportElement.scrollTop - ]; - } - - if (!this.element.lefttop) { - this.element.lefttop = [ - (document.documentElement.clientLeft || 0), - (document.documentElement.clientTop || 0) - ]; - } - - if (!this.element.offsets) { - this.element.offsets = OpenLayers.Util.pagePosition(this.element); - } - - 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] - ); - }, - - /** - * Method: addMsTouchListener - * - * Parameters: - * element - {DOMElement} The DOM element to register the listener on - * type - {String} The event type - * handler - {Function} the handler - */ - addMsTouchListener: function (element, type, handler) { - var eventHandler = this.eventHandler; - var touches = this._msTouches; - - function msHandler(evt) { - handler(OpenLayers.Util.applyDefaults({ - stopPropagation: function() { - for (var i=touches.length-1; i>=0; --i) { - touches[i].stopPropagation(); - } - }, - preventDefault: function() { - for (var i=touches.length-1; i>=0; --i) { - touches[i].preventDefault(); - } - }, - type: type - }, evt)); - } - - switch (type) { - case 'touchstart': - return this.addMsTouchListenerStart(element, type, msHandler); - case 'touchend': - return this.addMsTouchListenerEnd(element, type, msHandler); - case 'touchmove': - return this.addMsTouchListenerMove(element, type, msHandler); - default: - throw 'Unknown touch event type'; - } - }, - - /** - * Method: addMsTouchListenerStart - * - * Parameters: - * element - {DOMElement} The DOM element to register the listener on - * type - {String} The event type - * handler - {Function} the handler - */ - addMsTouchListenerStart: function(element, type, handler) { - var touches = this._msTouches; - - var cb = function(e) { - - var alreadyInArray = false; - for (var i=0, ii=touches.length; i<ii; ++i) { - if (touches[i].pointerId == e.pointerId) { - alreadyInArray = true; - break; - } - } - if (!alreadyInArray) { - touches.push(e); - } - - e.touches = touches.slice(); - handler(e); - }; - - OpenLayers.Event.observe(element, 'MSPointerDown', cb); - - // Need to also listen for end events to keep the _msTouches list - // accurate - var internalCb = function(e) { - for (var i=0, ii=touches.length; i<ii; ++i) { - if (touches[i].pointerId == e.pointerId) { - touches.splice(i, 1); - break; - } - } - }; - OpenLayers.Event.observe(element, 'MSPointerUp', internalCb); - }, - - /** - * Method: addMsTouchListenerMove - * - * Parameters: - * element - {DOMElement} The DOM element to register the listener on - * type - {String} The event type - * handler - {Function} the handler - */ - addMsTouchListenerMove: function (element, type, handler) { - var touches = this._msTouches; - var cb = function(e) { - - //Don't fire touch moves when mouse isn't down - if (e.pointerType == e.MSPOINTER_TYPE_MOUSE && e.buttons == 0) { - return; - } - - if (touches.length == 1 && touches[0].pageX == e.pageX && - touches[0].pageY == e.pageY) { - // don't trigger event when pointer has not moved - return; - } - for (var i=0, ii=touches.length; i<ii; ++i) { - if (touches[i].pointerId == e.pointerId) { - touches[i] = e; - break; - } - } - - e.touches = touches.slice(); - handler(e); - }; - - OpenLayers.Event.observe(element, 'MSPointerMove', cb); - }, - - /** - * Method: addMsTouchListenerEnd - * - * Parameters: - * element - {DOMElement} The DOM element to register the listener on - * type - {String} The event type - * handler - {Function} the handler - */ - addMsTouchListenerEnd: function (element, type, handler) { - var touches = this._msTouches; - - var cb = function(e) { - - for (var i=0, ii=touches.length; i<ii; ++i) { - if (touches[i].pointerId == e.pointerId) { - touches.splice(i, 1); - break; - } - } - - e.touches = touches.slice(); - handler(e); - }; - - OpenLayers.Event.observe(element, 'MSPointerUp', cb); - }, - - CLASS_NAME: "OpenLayers.Events" -}); -/* ====================================================================== - OpenLayers/Events/buttonclick.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @requires OpenLayers/Events.js - */ - -/** - * Class: OpenLayers.Events.buttonclick - * Extension event type for handling buttons on top of a dom element. This - * event type fires "buttonclick" on its <target> when a button was - * clicked. Buttons are detected by the "olButton" class. - * - * This event type makes sure that button clicks do not interfere with other - * events that are registered on the same <element>. - * - * Event types provided by this extension: - * - *buttonclick* Triggered when a button is clicked. Listeners receive an - * object with a *buttonElement* property referencing the dom element of - * the clicked button, and an *buttonXY* property with the click position - * relative to the button. - */ -OpenLayers.Events.buttonclick = OpenLayers.Class({ - - /** - * Property: target - * {<OpenLayers.Events>} The events instance that the buttonclick event will - * be triggered on. - */ - target: null, - - /** - * Property: events - * {Array} Events to observe and conditionally stop from propagating when - * an element with the olButton class (or its olAlphaImg child) is - * clicked. - */ - events: [ - 'mousedown', 'mouseup', 'click', 'dblclick', - 'touchstart', 'touchmove', 'touchend', 'keydown' - ], - - /** - * Property: startRegEx - * {RegExp} Regular expression to test Event.type for events that start - * a buttonclick sequence. - */ - startRegEx: /^mousedown|touchstart$/, - - /** - * Property: cancelRegEx - * {RegExp} Regular expression to test Event.type for events that cancel - * a buttonclick sequence. - */ - cancelRegEx: /^touchmove$/, - - /** - * Property: completeRegEx - * {RegExp} Regular expression to test Event.type for events that complete - * a buttonclick sequence. - */ - completeRegEx: /^mouseup|touchend$/, - - /** - * Property: startEvt - * {Event} The event that started the click sequence - */ - - /** - * Constructor: OpenLayers.Events.buttonclick - * Construct a buttonclick event type. Applications are not supposed to - * create instances of this class - they are created on demand by - * <OpenLayers.Events> instances. - * - * Parameters: - * target - {<OpenLayers.Events>} The events instance that the buttonclick - * event will be triggered on. - */ - initialize: function(target) { - this.target = target; - for (var i=this.events.length-1; i>=0; --i) { - this.target.register(this.events[i], this, this.buttonClick, { - extension: true - }); - } - }, - - /** - * Method: destroy - */ - destroy: function() { - for (var i=this.events.length-1; i>=0; --i) { - this.target.unregister(this.events[i], this, this.buttonClick); - } - delete this.target; - }, - - /** - * Method: getPressedButton - * Get the pressed button, if any. Returns undefined if no button - * was pressed. - * - * Arguments: - * element - {DOMElement} The event target. - * - * Returns: - * {DOMElement} The button element, or undefined. - */ - getPressedButton: function(element) { - var depth = 3, // limit the search depth - button; - do { - if(OpenLayers.Element.hasClass(element, "olButton")) { - // hit! - button = element; - break; - } - element = element.parentNode; - } while(--depth > 0 && element); - return button; - }, - - /** - * Method: ignore - * Check for event target elements that should be ignored by OpenLayers. - * - * Parameters: - * element - {DOMElement} The event target. - */ - ignore: function(element) { - var depth = 3, - ignore = false; - do { - if (element.nodeName.toLowerCase() === 'a') { - ignore = true; - break; - } - element = element.parentNode; - } while (--depth > 0 && element); - return ignore; - }, - - /** - * Method: buttonClick - * Check if a button was clicked, and fire the buttonclick event - * - * Parameters: - * evt - {Event} - */ - buttonClick: function(evt) { - var propagate = true, - element = OpenLayers.Event.element(evt); - if (element && (OpenLayers.Event.isLeftClick(evt) || !~evt.type.indexOf("mouse"))) { - // was a button pressed? - var button = this.getPressedButton(element); - if (button) { - if (evt.type === "keydown") { - switch (evt.keyCode) { - case OpenLayers.Event.KEY_RETURN: - case OpenLayers.Event.KEY_SPACE: - this.target.triggerEvent("buttonclick", { - buttonElement: button - }); - OpenLayers.Event.stop(evt); - propagate = false; - break; - } - } else if (this.startEvt) { - if (this.completeRegEx.test(evt.type)) { - var pos = OpenLayers.Util.pagePosition(button); - var viewportElement = OpenLayers.Util.getViewportElement(); - var scrollTop = window.pageYOffset || viewportElement.scrollTop; - var scrollLeft = window.pageXOffset || viewportElement.scrollLeft; - pos[0] = pos[0] - scrollLeft; - pos[1] = pos[1] - scrollTop; - - this.target.triggerEvent("buttonclick", { - buttonElement: button, - buttonXY: { - x: this.startEvt.clientX - pos[0], - y: this.startEvt.clientY - pos[1] - } - }); - } - if (this.cancelRegEx.test(evt.type)) { - delete this.startEvt; - } - OpenLayers.Event.stop(evt); - propagate = false; - } - if (this.startRegEx.test(evt.type)) { - this.startEvt = evt; - OpenLayers.Event.stop(evt); - propagate = false; - } - } else { - propagate = !this.ignore(OpenLayers.Event.element(evt)); - delete this.startEvt; - } - } - return propagate; - } - -}); -/* ====================================================================== - OpenLayers/Util/vendorPrefix.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @requires OpenLayers/SingleFile.js - */ - -OpenLayers.Util = OpenLayers.Util || {}; -/** - * Namespace: OpenLayers.Util.vendorPrefix - * A collection of utility functions to detect vendor prefixed features - */ -OpenLayers.Util.vendorPrefix = (function() { - "use strict"; - - var VENDOR_PREFIXES = ["", "O", "ms", "Moz", "Webkit"], - divStyle = document.createElement("div").style, - cssCache = {}, - jsCache = {}; - - - /** - * Function: domToCss - * Converts a upper camel case DOM style property name to a CSS property - * i.e. transformOrigin -> transform-origin - * or WebkitTransformOrigin -> -webkit-transform-origin - * - * Parameters: - * prefixedDom - {String} The property to convert - * - * Returns: - * {String} The CSS property - */ - function domToCss(prefixedDom) { - if (!prefixedDom) { return null; } - return prefixedDom. - replace(/([A-Z])/g, function(c) { return "-" + c.toLowerCase(); }). - replace(/^ms-/, "-ms-"); - } - - /** - * APIMethod: css - * Detect which property is used for a CSS property - * - * Parameters: - * property - {String} The standard (unprefixed) CSS property name - * - * Returns: - * {String} The standard CSS property, prefixed property or null if not - * supported - */ - function css(property) { - if (cssCache[property] === undefined) { - var domProperty = property. - replace(/(-[\s\S])/g, function(c) { return c.charAt(1).toUpperCase(); }); - var prefixedDom = style(domProperty); - cssCache[property] = domToCss(prefixedDom); - } - return cssCache[property]; - } - - /** - * APIMethod: js - * Detect which property is used for a JS property/method - * - * Parameters: - * obj - {Object} The object to test on - * property - {String} The standard (unprefixed) JS property name - * - * Returns: - * {String} The standard JS property, prefixed property or null if not - * supported - */ - function js(obj, property) { - if (jsCache[property] === undefined) { - var tmpProp, - i = 0, - l = VENDOR_PREFIXES.length, - prefix, - isStyleObj = (typeof obj.cssText !== "undefined"); - - jsCache[property] = null; - for(; i<l; i++) { - prefix = VENDOR_PREFIXES[i]; - if(prefix) { - if (!isStyleObj) { - // js prefix should be lower-case, while style - // properties have upper case on first character - prefix = prefix.toLowerCase(); - } - tmpProp = prefix + property.charAt(0).toUpperCase() + property.slice(1); - } else { - tmpProp = property; - } - - if(obj[tmpProp] !== undefined) { - jsCache[property] = tmpProp; - break; - } - } - } - return jsCache[property]; - } - - /** - * APIMethod: style - * Detect which property is used for a DOM style property - * - * Parameters: - * property - {String} The standard (unprefixed) style property name - * - * Returns: - * {String} The standard style property, prefixed property or null if not - * supported - */ - function style(property) { - return js(divStyle, property); - } - - return { - css: css, - js: js, - style: style, - - // used for testing - cssCache: cssCache, - jsCache: jsCache - }; -}()); -/* ====================================================================== - OpenLayers/Animation.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @requires OpenLayers/SingleFile.js - * @requires OpenLayers/Util/vendorPrefix.js - */ - -/** - * Namespace: OpenLayers.Animation - * A collection of utility functions for executing methods that repaint a - * portion of the browser window. These methods take advantage of the - * browser's scheduled repaints where requestAnimationFrame is available. - */ -OpenLayers.Animation = (function(window) { - - /** - * Property: isNative - * {Boolean} true if a native requestAnimationFrame function is available - */ - var requestAnimationFrame = OpenLayers.Util.vendorPrefix.js(window, "requestAnimationFrame"); - var isNative = !!(requestAnimationFrame); - - /** - * Function: requestFrame - * Schedule a function to be called at the next available animation frame. - * Uses the native method where available. Where requestAnimationFrame is - * not available, setTimeout will be called with a 16ms delay. - * - * Parameters: - * callback - {Function} The function to be called at the next animation frame. - * element - {DOMElement} Optional element that visually bounds the animation. - */ - var requestFrame = (function() { - var request = window[requestAnimationFrame] || - function(callback, element) { - window.setTimeout(callback, 16); - }; - // bind to window to avoid illegal invocation of native function - return function(callback, element) { - request.apply(window, [callback, element]); - }; - })(); - - // private variables for animation loops - var counter = 0; - var loops = {}; - - /** - * Function: start - * Executes a method with <requestFrame> in series for some - * duration. - * - * Parameters: - * callback - {Function} The function to be called at the next animation frame. - * duration - {Number} Optional duration for the loop. If not provided, the - * animation loop will execute indefinitely. - * element - {DOMElement} Optional element that visually bounds the animation. - * - * Returns: - * {Number} Identifier for the animation loop. Used to stop animations with - * <stop>. - */ - function start(callback, duration, element) { - duration = duration > 0 ? duration : Number.POSITIVE_INFINITY; - var id = ++counter; - var start = +new Date; - loops[id] = function() { - if (loops[id] && +new Date - start <= duration) { - callback(); - if (loops[id]) { - requestFrame(loops[id], element); - } - } else { - delete loops[id]; - } - }; - requestFrame(loops[id], element); - return id; - } - - /** - * Function: stop - * Terminates an animation loop started with <start>. - * - * Parameters: - * id - {Number} Identifier returned from <start>. - */ - function stop(id) { - delete loops[id]; - } - - return { - isNative: isNative, - requestFrame: requestFrame, - start: start, - stop: stop - }; - -})(window); -/* ====================================================================== - OpenLayers/Tween.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @requires OpenLayers/BaseTypes/Class.js - * @requires OpenLayers/Animation.js - */ - -/** - * Namespace: OpenLayers.Tween - */ -OpenLayers.Tween = OpenLayers.Class({ - - /** - * APIProperty: easing - * {<OpenLayers.Easing>(Function)} Easing equation used for the animation - * Defaultly set to OpenLayers.Easing.Expo.easeOut - */ - easing: null, - - /** - * APIProperty: begin - * {Object} Values to start the animation with - */ - begin: null, - - /** - * APIProperty: finish - * {Object} Values to finish the animation with - */ - finish: null, - - /** - * APIProperty: duration - * {int} duration of the tween (number of steps) - */ - duration: null, - - /** - * APIProperty: callbacks - * {Object} An object with start, eachStep and done properties whose values - * are functions to be call during the animation. They are passed the - * current computed value as argument. - */ - callbacks: null, - - /** - * Property: time - * {int} Step counter - */ - time: null, - - /** - * APIProperty: minFrameRate - * {Number} The minimum framerate for animations in frames per second. After - * each step, the time spent in the animation is compared to the calculated - * time at this frame rate. If the animation runs longer than the calculated - * time, the next step is skipped. Default is 30. - */ - minFrameRate: null, - - /** - * Property: startTime - * {Number} The timestamp of the first execution step. Used for skipping - * frames - */ - startTime: null, - - /** - * Property: animationId - * {int} Loop id returned by OpenLayers.Animation.start - */ - animationId: null, - - /** - * Property: playing - * {Boolean} Tells if the easing is currently playing - */ - playing: false, - - /** - * Constructor: OpenLayers.Tween - * Creates a Tween. - * - * Parameters: - * easing - {<OpenLayers.Easing>(Function)} easing function method to use - */ - initialize: function(easing) { - this.easing = (easing) ? easing : OpenLayers.Easing.Expo.easeOut; - }, - - /** - * APIMethod: start - * Plays the Tween, and calls the callback method on each step - * - * Parameters: - * begin - {Object} values to start the animation with - * finish - {Object} values to finish the animation with - * duration - {int} duration of the tween (number of steps) - * options - {Object} hash of options (callbacks (start, eachStep, done), - * minFrameRate) - */ - start: function(begin, finish, duration, options) { - this.playing = true; - this.begin = begin; - this.finish = finish; - this.duration = duration; - this.callbacks = options.callbacks; - this.minFrameRate = options.minFrameRate || 30; - this.time = 0; - this.startTime = new Date().getTime(); - OpenLayers.Animation.stop(this.animationId); - this.animationId = null; - if (this.callbacks && this.callbacks.start) { - this.callbacks.start.call(this, this.begin); - } - this.animationId = OpenLayers.Animation.start( - OpenLayers.Function.bind(this.play, this) - ); - }, - - /** - * APIMethod: stop - * Stops the Tween, and calls the done callback - * Doesn't do anything if animation is already finished - */ - stop: function() { - if (!this.playing) { - return; - } - - if (this.callbacks && this.callbacks.done) { - this.callbacks.done.call(this, this.finish); - } - OpenLayers.Animation.stop(this.animationId); - this.animationId = null; - this.playing = false; - }, - - /** - * Method: play - * Calls the appropriate easing method - */ - play: function() { - var value = {}; - for (var i in this.begin) { - var b = this.begin[i]; - var f = this.finish[i]; - if (b == null || f == null || isNaN(b) || isNaN(f)) { - throw new TypeError('invalid value for Tween'); - } - - var c = f - b; - value[i] = this.easing.apply(this, [this.time, b, c, this.duration]); - } - this.time++; - - if (this.callbacks && this.callbacks.eachStep) { - // skip frames if frame rate drops below threshold - if ((new Date().getTime() - this.startTime) / this.time <= 1000 / this.minFrameRate) { - this.callbacks.eachStep.call(this, value); - } - } - - if (this.time > this.duration) { - this.stop(); - } - }, - - /** - * Create empty functions for all easing methods. - */ - CLASS_NAME: "OpenLayers.Tween" -}); - -/** - * Namespace: OpenLayers.Easing - * - * Credits: - * Easing Equations by Robert Penner, <http://www.robertpenner.com/easing/> - */ -OpenLayers.Easing = { - /** - * Create empty functions for all easing methods. - */ - CLASS_NAME: "OpenLayers.Easing" -}; - -/** - * Namespace: OpenLayers.Easing.Linear - */ -OpenLayers.Easing.Linear = { - - /** - * Function: easeIn - * - * Parameters: - * t - {Float} time - * b - {Float} beginning position - * c - {Float} total change - * d - {Float} duration of the transition - * - * Returns: - * {Float} - */ - easeIn: function(t, b, c, d) { - return c*t/d + b; - }, - - /** - * Function: easeOut - * - * Parameters: - * t - {Float} time - * b - {Float} beginning position - * c - {Float} total change - * d - {Float} duration of the transition - * - * Returns: - * {Float} - */ - easeOut: function(t, b, c, d) { - return c*t/d + b; - }, - - /** - * Function: easeInOut - * - * Parameters: - * t - {Float} time - * b - {Float} beginning position - * c - {Float} total change - * d - {Float} duration of the transition - * - * Returns: - * {Float} - */ - easeInOut: function(t, b, c, d) { - return c*t/d + b; - }, - - CLASS_NAME: "OpenLayers.Easing.Linear" -}; - -/** - * Namespace: OpenLayers.Easing.Expo - */ -OpenLayers.Easing.Expo = { - - /** - * Function: easeIn - * - * Parameters: - * t - {Float} time - * b - {Float} beginning position - * c - {Float} total change - * d - {Float} duration of the transition - * - * Returns: - * {Float} - */ - easeIn: function(t, b, c, d) { - return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b; - }, - - /** - * Function: easeOut - * - * Parameters: - * t - {Float} time - * b - {Float} beginning position - * c - {Float} total change - * d - {Float} duration of the transition - * - * Returns: - * {Float} - */ - easeOut: function(t, b, c, d) { - return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b; - }, - - /** - * Function: easeInOut - * - * Parameters: - * t - {Float} time - * b - {Float} beginning position - * c - {Float} total change - * d - {Float} duration of the transition - * - * Returns: - * {Float} - */ - 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" -}; - -/** - * Namespace: OpenLayers.Easing.Quad - */ -OpenLayers.Easing.Quad = { - - /** - * Function: easeIn - * - * Parameters: - * t - {Float} time - * b - {Float} beginning position - * c - {Float} total change - * d - {Float} duration of the transition - * - * Returns: - * {Float} - */ - easeIn: function(t, b, c, d) { - return c*(t/=d)*t + b; - }, - - /** - * Function: easeOut - * - * Parameters: - * t - {Float} time - * b - {Float} beginning position - * c - {Float} total change - * d - {Float} duration of the transition - * - * Returns: - * {Float} - */ - easeOut: function(t, b, c, d) { - return -c *(t/=d)*(t-2) + b; - }, - - /** - * Function: easeInOut - * - * Parameters: - * t - {Float} time - * b - {Float} beginning position - * c - {Float} total change - * d - {Float} duration of the transition - * - * Returns: - * {Float} - */ - 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/Projection.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @requires OpenLayers/BaseTypes/Class.js - * @requires OpenLayers/Util.js - */ - -/** - * Namespace: OpenLayers.Projection - * Methods for coordinate transforms between coordinate systems. By default, - * OpenLayers ships with the ability to transform coordinates between - * geographic (EPSG:4326) and web or spherical mercator (EPSG:900913 et al.) - * coordinate reference systems. See the <transform> method for details - * on usage. - * - * Additional transforms may be added by using the <proj4js at http://proj4js.org/> - * library. If the proj4js library is included, the <transform> method - * will work between any two coordinate reference systems with proj4js - * definitions. - * - * If the proj4js library is not included, or if you wish to allow transforms - * between arbitrary coordinate reference systems, use the <addTransform> - * method to register a custom transform method. - */ -OpenLayers.Projection = OpenLayers.Class({ - - /** - * Property: proj - * {Object} Proj4js.Proj instance. - */ - proj: null, - - /** - * Property: projCode - * {String} - */ - projCode: null, - - /** - * Property: titleRegEx - * {RegExp} regular expression to strip the title from a proj4js definition - */ - titleRegEx: /\+title=[^\+]*/, - - /** - * Constructor: OpenLayers.Projection - * This class offers several methods for interacting with a wrapped - * pro4js projection object. - * - * Parameters: - * projCode - {String} A string identifying the Well Known Identifier for - * the projection. - * options - {Object} An optional object to set additional properties - * on the projection. - * - * Returns: - * {<OpenLayers.Projection>} A projection object. - */ - initialize: function(projCode, options) { - OpenLayers.Util.extend(this, options); - this.projCode = projCode; - if (typeof Proj4js == "object") { - this.proj = new Proj4js.Proj(projCode); - } - }, - - /** - * APIMethod: getCode - * Get the string SRS code. - * - * Returns: - * {String} The SRS code. - */ - getCode: function() { - return this.proj ? this.proj.srsCode : this.projCode; - }, - - /** - * APIMethod: getUnits - * Get the units string for the projection -- returns null if - * proj4js is not available. - * - * Returns: - * {String} The units abbreviation. - */ - getUnits: function() { - return this.proj ? this.proj.units : null; - }, - - /** - * Method: toString - * Convert projection to string (getCode wrapper). - * - * Returns: - * {String} The projection code. - */ - toString: function() { - return this.getCode(); - }, - - /** - * Method: equals - * Test equality of two projection instances. Determines equality based - * soley on the projection code. - * - * Returns: - * {Boolean} The two projections are equivalent. - */ - equals: function(projection) { - var p = projection, equals = false; - if (p) { - if (!(p instanceof OpenLayers.Projection)) { - p = new OpenLayers.Projection(p); - } - if ((typeof Proj4js == "object") && this.proj.defData && p.proj.defData) { - equals = this.proj.defData.replace(this.titleRegEx, "") == - p.proj.defData.replace(this.titleRegEx, ""); - } else if (p.getCode) { - var source = this.getCode(), target = p.getCode(); - equals = source == target || - !!OpenLayers.Projection.transforms[source] && - OpenLayers.Projection.transforms[source][target] === - OpenLayers.Projection.nullTransform; - } - } - return equals; - }, - - /* Method: destroy - * Destroy projection object. - */ - destroy: function() { - delete this.proj; - delete this.projCode; - }, - - CLASS_NAME: "OpenLayers.Projection" -}); - -/** - * Property: transforms - * {Object} Transforms is an object, with from properties, each of which may - * have a to property. This allows you to define projections without - * requiring support for proj4js to be included. - * - * This object has keys which correspond to a 'source' projection object. The - * keys should be strings, corresponding to the projection.getCode() value. - * Each source projection object should have a set of destination projection - * keys included in the object. - * - * Each value in the destination object should be a transformation function, - * where the function is expected to be passed an object with a .x and a .y - * property. The function should return the object, with the .x and .y - * transformed according to the transformation function. - * - * Note - Properties on this object should not be set directly. To add a - * transform method to this object, use the <addTransform> method. For an - * example of usage, see the OpenLayers.Layer.SphericalMercator file. - */ -OpenLayers.Projection.transforms = {}; - -/** - * APIProperty: defaults - * {Object} Defaults for the SRS codes known to OpenLayers (currently - * EPSG:4326, CRS:84, urn:ogc:def:crs:EPSG:6.6:4326, EPSG:900913, EPSG:3857, - * EPSG:102113 and EPSG:102100). Keys are the SRS code, values are units, - * maxExtent (the validity extent for the SRS) and yx (true if this SRS is - * known to have a reverse axis order). - */ -OpenLayers.Projection.defaults = { - "EPSG:4326": { - units: "degrees", - maxExtent: [-180, -90, 180, 90], - yx: true - }, - "CRS:84": { - units: "degrees", - maxExtent: [-180, -90, 180, 90] - }, - "EPSG:900913": { - units: "m", - maxExtent: [-20037508.34, -20037508.34, 20037508.34, 20037508.34] - } -}; - -/** - * APIMethod: addTransform - * Set a custom transform method between two projections. Use this method in - * cases where the proj4js lib is not available or where custom projections - * need to be handled. - * - * Parameters: - * from - {String} The code for the source projection - * to - {String} the code for the destination projection - * method - {Function} A function that takes a point as an argument and - * transforms that point from the source to the destination projection - * in place. The original point should be modified. - */ -OpenLayers.Projection.addTransform = function(from, to, method) { - if (method === OpenLayers.Projection.nullTransform) { - var defaults = OpenLayers.Projection.defaults[from]; - if (defaults && !OpenLayers.Projection.defaults[to]) { - OpenLayers.Projection.defaults[to] = defaults; - } - } - if(!OpenLayers.Projection.transforms[from]) { - OpenLayers.Projection.transforms[from] = {}; - } - OpenLayers.Projection.transforms[from][to] = method; -}; - -/** - * APIMethod: transform - * Transform a point coordinate from one projection to another. Note that - * the input point is transformed in place. - * - * Parameters: - * point - {<OpenLayers.Geometry.Point> | Object} An object with x and y - * properties representing coordinates in those dimensions. - * source - {OpenLayers.Projection} Source map coordinate system - * dest - {OpenLayers.Projection} Destination map coordinate system - * - * Returns: - * point - {object} A transformed coordinate. The original point is modified. - */ -OpenLayers.Projection.transform = function(point, source, dest) { - if (source && dest) { - if (!(source instanceof OpenLayers.Projection)) { - source = new OpenLayers.Projection(source); - } - if (!(dest instanceof OpenLayers.Projection)) { - dest = new OpenLayers.Projection(dest); - } - if (source.proj && dest.proj) { - point = Proj4js.transform(source.proj, dest.proj, point); - } else { - var sourceCode = source.getCode(); - var destCode = dest.getCode(); - var transforms = OpenLayers.Projection.transforms; - if (transforms[sourceCode] && transforms[sourceCode][destCode]) { - transforms[sourceCode][destCode](point); - } - } - } - return point; -}; - -/** - * APIFunction: nullTransform - * A null transformation - useful for defining projection aliases when - * proj4js is not available: - * - * (code) - * OpenLayers.Projection.addTransform("EPSG:3857", "EPSG:900913", - * OpenLayers.Projection.nullTransform); - * OpenLayers.Projection.addTransform("EPSG:900913", "EPSG:3857", - * OpenLayers.Projection.nullTransform); - * (end) - */ -OpenLayers.Projection.nullTransform = function(point) { - return point; -}; - -/** - * Note: Transforms for web mercator <-> geographic - * OpenLayers recognizes EPSG:3857, EPSG:900913, EPSG:102113 and EPSG:102100. - * OpenLayers originally started referring to EPSG:900913 as web mercator. - * The EPSG has declared EPSG:3857 to be web mercator. - * ArcGIS 10 recognizes the EPSG:3857, EPSG:102113, and EPSG:102100 as - * equivalent. See http://blogs.esri.com/Dev/blogs/arcgisserver/archive/2009/11/20/ArcGIS-Online-moving-to-Google-_2F00_-Bing-tiling-scheme_3A00_-What-does-this-mean-for-you_3F00_.aspx#12084. - * For geographic, OpenLayers recognizes EPSG:4326, CRS:84 and - * urn:ogc:def:crs:EPSG:6.6:4326. OpenLayers also knows about the reverse axis - * order for EPSG:4326. - */ -(function() { - - var pole = 20037508.34; - - function inverseMercator(xy) { - xy.x = 180 * xy.x / pole; - xy.y = 180 / Math.PI * (2 * Math.atan(Math.exp((xy.y / pole) * Math.PI)) - Math.PI / 2); - return xy; - } - - function forwardMercator(xy) { - xy.x = xy.x * pole / 180; - var y = Math.log(Math.tan((90 + xy.y) * Math.PI / 360)) / Math.PI * pole; - xy.y = Math.max(-20037508.34, Math.min(y, 20037508.34)); - return xy; - } - - function map(base, codes) { - var add = OpenLayers.Projection.addTransform; - var same = OpenLayers.Projection.nullTransform; - var i, len, code, other, j; - for (i=0, len=codes.length; i<len; ++i) { - code = codes[i]; - add(base, code, forwardMercator); - add(code, base, inverseMercator); - for (j=i+1; j<len; ++j) { - other = codes[j]; - add(code, other, same); - add(other, code, same); - } - } - } - - // list of equivalent codes for web mercator - var mercator = ["EPSG:900913", "EPSG:3857", "EPSG:102113", "EPSG:102100"], - geographic = ["CRS:84", "urn:ogc:def:crs:EPSG:6.6:4326", "EPSG:4326"], - i; - for (i=mercator.length-1; i>=0; --i) { - map(mercator[i], geographic); - } - for (i=geographic.length-1; i>=0; --i) { - map(geographic[i], mercator); - } - -})(); -/* ====================================================================== - OpenLayers/Map.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @requires OpenLayers/BaseTypes/Class.js - * @requires OpenLayers/Util.js - * @requires OpenLayers/Util/vendorPrefix.js - * @requires OpenLayers/Events.js - * @requires OpenLayers/Tween.js - * @requires OpenLayers/Projection.js - */ - -/** - * Class: OpenLayers.Map - * Instances of OpenLayers.Map are interactive maps embedded in a web page. - * Create a new map with the <OpenLayers.Map> constructor. - * - * On their own maps do not provide much functionality. To extend a map - * it's necessary to add controls (<OpenLayers.Control>) and - * layers (<OpenLayers.Layer>) to the map. - */ -OpenLayers.Map = OpenLayers.Class({ - - /** - * Constant: Z_INDEX_BASE - * {Object} Base z-indexes for different classes of thing - */ - Z_INDEX_BASE: { - BaseLayer: 100, - Overlay: 325, - Feature: 725, - Popup: 750, - Control: 1000 - }, - - /** - * APIProperty: events - * {<OpenLayers.Events>} - * - * Register a listener for a particular event with the following syntax: - * (code) - * map.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 map.events.object. - * element - {DOMElement} A reference to map.events.element. - * - * Browser events have the following additional properties: - * xy - {<OpenLayers.Pixel>} The pixel location of the event (relative - * to the the map viewport). - * - * Supported map event types: - * preaddlayer - triggered before a layer has been added. The event - * object will include a *layer* property that references the layer - * to be added. When a listener returns "false" the adding will be - * aborted. - * addlayer - triggered after a layer has been added. The event object - * will include a *layer* property that references the added layer. - * preremovelayer - triggered before a layer has been removed. The event - * object will include a *layer* property that references the layer - * to be removed. When a listener returns "false" the removal will be - * aborted. - * removelayer - triggered after a layer has been removed. The event - * object will include a *layer* property that references the removed - * layer. - * changelayer - triggered after a layer name change, order change, - * opacity change, params change, visibility change (actual visibility, - * not the layer's visibility property) or attribution change (due to - * extent change). Listeners will receive an event object with *layer* - * and *property* properties. The *layer* property will be a reference - * to the changed layer. The *property* property will be a key to the - * changed property (name, order, opacity, params, visibility or - * attribution). - * movestart - triggered after the start of a drag, pan, or zoom. The event - * object may include a *zoomChanged* property that tells whether the - * zoom has changed. - * move - triggered after each drag, pan, or zoom - * moveend - triggered after a drag, pan, or zoom completes - * zoomend - triggered after a zoom completes - * mouseover - triggered after mouseover the map - * mouseout - triggered after mouseout the map - * mousemove - triggered after mousemove the map - * changebaselayer - triggered after the base layer changes - * updatesize - triggered after the <updateSize> method was executed - */ - - /** - * Property: id - * {String} Unique identifier for the map - */ - id: null, - - /** - * Property: fractionalZoom - * {Boolean} For a base layer that supports it, allow the map resolution - * to be set to a value between one of the values in the resolutions - * array. Default is false. - * - * When fractionalZoom is set to true, it is possible to zoom to - * an arbitrary extent. This requires a base layer from a source - * that supports requests for arbitrary extents (i.e. not cached - * tiles on a regular lattice). This means that fractionalZoom - * will not work with commercial layers (Google, Yahoo, VE), layers - * using TileCache, or any other pre-cached data sources. - * - * If you are using fractionalZoom, then you should also use - * <getResolutionForZoom> instead of layer.resolutions[zoom] as the - * former works for non-integer zoom levels. - */ - fractionalZoom: false, - - /** - * APIProperty: events - * {<OpenLayers.Events>} An events object that handles all - * events on the map - */ - events: null, - - /** - * APIProperty: allOverlays - * {Boolean} Allow the map to function with "overlays" only. Defaults to - * false. If true, the lowest layer in the draw order will act as - * the base layer. In addition, if set to true, all layers will - * have isBaseLayer set to false when they are added to the map. - * - * Note: - * If you set map.allOverlays to true, then you *cannot* use - * map.setBaseLayer or layer.setIsBaseLayer. With allOverlays true, - * the lowest layer in the draw layer is the base layer. So, to change - * the base layer, use <setLayerIndex> or <raiseLayer> to set the layer - * index to 0. - */ - allOverlays: false, - - /** - * APIProperty: div - * {DOMElement|String} The element that contains the map (or an id for - * that element). If the <OpenLayers.Map> constructor is called - * with two arguments, this should be provided as the first argument. - * Alternatively, the map constructor can be called with the options - * object as the only argument. In this case (one argument), a - * div property may or may not be provided. If the div property - * is not provided, the map can be rendered to a container later - * using the <render> method. - * - * Note: - * If you are calling <render> after map construction, do not use - * <maxResolution> auto. Instead, divide your <maxExtent> by your - * maximum expected dimension. - */ - div: null, - - /** - * Property: dragging - * {Boolean} The map is currently being dragged. - */ - dragging: false, - - /** - * Property: size - * {<OpenLayers.Size>} Size of the main div (this.div) - */ - size: null, - - /** - * Property: viewPortDiv - * {HTMLDivElement} The element that represents the map viewport - */ - viewPortDiv: null, - - /** - * Property: layerContainerOrigin - * {<OpenLayers.LonLat>} The lonlat at which the later container was - * re-initialized (on-zoom) - */ - layerContainerOrigin: null, - - /** - * Property: layerContainerDiv - * {HTMLDivElement} The element that contains the layers. - */ - layerContainerDiv: null, - - /** - * APIProperty: layers - * {Array(<OpenLayers.Layer>)} Ordered list of layers in the map - */ - layers: null, - - /** - * APIProperty: controls - * {Array(<OpenLayers.Control>)} List of controls associated with the map. - * - * If not provided in the map options at construction, the map will - * by default be given the following controls if present in the build: - * - <OpenLayers.Control.Navigation> or <OpenLayers.Control.TouchNavigation> - * - <OpenLayers.Control.Zoom> or <OpenLayers.Control.PanZoom> - * - <OpenLayers.Control.ArgParser> - * - <OpenLayers.Control.Attribution> - */ - controls: null, - - /** - * Property: popups - * {Array(<OpenLayers.Popup>)} List of popups associated with the map - */ - popups: null, - - /** - * APIProperty: baseLayer - * {<OpenLayers.Layer>} The currently selected base layer. This determines - * min/max zoom level, projection, etc. - */ - baseLayer: null, - - /** - * Property: center - * {<OpenLayers.LonLat>} The current center of the map - */ - center: null, - - /** - * Property: resolution - * {Float} The resolution of the map. - */ - resolution: null, - - /** - * Property: zoom - * {Integer} The current zoom level of the map - */ - zoom: 0, - - /** - * Property: panRatio - * {Float} The ratio of the current extent within - * which panning will tween. - */ - panRatio: 1.5, - - /** - * APIProperty: options - * {Object} The options object passed to the class constructor. Read-only. - */ - options: null, - - // Options - - /** - * APIProperty: tileSize - * {<OpenLayers.Size>} Set in the map options to override the default tile - * size for this map. - */ - tileSize: null, - - /** - * APIProperty: projection - * {String} Set in the map options to specify the default projection - * for layers added to this map. When using a projection other than EPSG:4326 - * (CRS:84, Geographic) or EPSG:3857 (EPSG:900913, Web Mercator), - * also set maxExtent, maxResolution or resolutions. Default is "EPSG:4326". - * Note that the projection of the map is usually determined - * by that of the current baseLayer (see <baseLayer> and <getProjectionObject>). - */ - projection: "EPSG:4326", - - /** - * APIProperty: units - * {String} The map units. Possible values are 'degrees' (or 'dd'), 'm', - * 'ft', 'km', 'mi', 'inches'. Normally taken from the projection. - * Only required if both map and layers do not define a projection, - * or if they define a projection which does not define units - */ - units: null, - - /** - * APIProperty: resolutions - * {Array(Float)} A list of map resolutions (map units per pixel) in - * descending order. If this is not set in the layer constructor, it - * will be set based on other resolution related properties - * (maxExtent, maxResolution, maxScale, etc.). - */ - resolutions: null, - - /** - * APIProperty: maxResolution - * {Float} Required if you are not displaying the whole world on a tile - * with the size specified in <tileSize>. - */ - maxResolution: null, - - /** - * APIProperty: minResolution - * {Float} - */ - minResolution: null, - - /** - * APIProperty: maxScale - * {Float} - */ - maxScale: null, - - /** - * APIProperty: minScale - * {Float} - */ - minScale: null, - - /** - * APIProperty: maxExtent - * {<OpenLayers.Bounds>|Array} If provided as an array, the array - * should consist of four values (left, bottom, right, top). - * The maximum extent for the map. - * Default depends on projection; if this is one of those defined in OpenLayers.Projection.defaults - * (EPSG:4326 or web mercator), maxExtent will be set to the value defined there; - * else, defaults to null. - * To restrict user panning and zooming of the map, use <restrictedExtent> instead. - * The value for <maxExtent> will change calculations for tile URLs. - */ - maxExtent: null, - - /** - * APIProperty: minExtent - * {<OpenLayers.Bounds>|Array} If provided as an array, the array - * should consist of four values (left, bottom, right, top). - * The minimum extent for the map. Defaults to null. - */ - minExtent: null, - - /** - * APIProperty: restrictedExtent - * {<OpenLayers.Bounds>|Array} If provided as an array, the array - * should consist of four values (left, bottom, right, top). - * Limit map navigation to this extent where possible. - * If a non-null restrictedExtent is set, panning will be restricted - * to the given bounds. In addition, zooming to a resolution that - * displays more than the restricted extent will center the map - * on the restricted extent. If you wish to limit the zoom level - * or resolution, use maxResolution. - */ - restrictedExtent: null, - - /** - * APIProperty: numZoomLevels - * {Integer} Number of zoom levels for the map. Defaults to 16. Set a - * different value in the map options if needed. - */ - numZoomLevels: 16, - - /** - * APIProperty: theme - * {String} Relative path to a CSS file from which to load theme styles. - * Specify null in the map options (e.g. {theme: null}) if you - * want to get cascading style declarations - by putting links to - * stylesheets or style declarations directly in your page. - */ - theme: null, - - /** - * APIProperty: displayProjection - * {<OpenLayers.Projection>} Requires proj4js support for projections other - * than EPSG:4326 or EPSG:900913/EPSG:3857. Projection used by - * several controls to display data to user. If this property is set, - * it will be set on any control which has a null displayProjection - * property at the time the control is added to the map. - */ - displayProjection: null, - - /** - * APIProperty: tileManager - * {<OpenLayers.TileManager>|Object} By default, and if the build contains - * TileManager.js, the map will use the TileManager to queue image requests - * and to cache tile image elements. To create a map without a TileManager - * configure the map with tileManager: null. To create a TileManager with - * non-default options, supply the options instead or alternatively supply - * an instance of {<OpenLayers.TileManager>}. - */ - - /** - * APIProperty: fallThrough - * {Boolean} Should OpenLayers allow events on the map to fall through to - * other elements on the page, or should it swallow them? (#457) - * Default is to swallow. - */ - fallThrough: false, - - /** - * APIProperty: autoUpdateSize - * {Boolean} Should OpenLayers automatically update the size of the map - * when the resize event is fired. Default is true. - */ - autoUpdateSize: true, - - /** - * APIProperty: eventListeners - * {Object} If set as an option at construction, the eventListeners - * object will be registered with <OpenLayers.Events.on>. Object - * structure must be a listeners object as shown in the example for - * the events.on method. - */ - eventListeners: null, - - /** - * Property: panTween - * {<OpenLayers.Tween>} Animated panning tween object, see panTo() - */ - panTween: null, - - /** - * APIProperty: panMethod - * {Function} The Easing function to be used for tweening. Default is - * OpenLayers.Easing.Expo.easeOut. Setting this to 'null' turns off - * animated panning. - */ - panMethod: OpenLayers.Easing.Expo.easeOut, - - /** - * Property: panDuration - * {Integer} The number of steps to be passed to the - * OpenLayers.Tween.start() method when the map is - * panned. - * Default is 50. - */ - panDuration: 50, - - /** - * Property: zoomTween - * {<OpenLayers.Tween>} Animated zooming tween object, see zoomTo() - */ - zoomTween: null, - - /** - * APIProperty: zoomMethod - * {Function} The Easing function to be used for tweening. Default is - * OpenLayers.Easing.Quad.easeOut. Setting this to 'null' turns off - * animated zooming. - */ - zoomMethod: OpenLayers.Easing.Quad.easeOut, - - /** - * Property: zoomDuration - * {Integer} The number of steps to be passed to the - * OpenLayers.Tween.start() method when the map is zoomed. - * Default is 20. - */ - zoomDuration: 20, - - /** - * Property: paddingForPopups - * {<OpenLayers.Bounds>} Outside margin of the popup. Used to prevent - * the popup from getting too close to the map border. - */ - paddingForPopups : null, - - /** - * Property: layerContainerOriginPx - * {Object} Cached object representing the layer container origin (in pixels). - */ - layerContainerOriginPx: null, - - /** - * Property: minPx - * {Object} An object with a 'x' and 'y' values that is the lower - * left of maxExtent in viewport pixel space. - * Used to verify in moveByPx that the new location we're moving to - * is valid. It is also used in the getLonLatFromViewPortPx function - * of Layer. - */ - minPx: null, - - /** - * Property: maxPx - * {Object} An object with a 'x' and 'y' values that is the top - * right of maxExtent in viewport pixel space. - * Used to verify in moveByPx that the new location we're moving to - * is valid. - */ - maxPx: null, - - /** - * Constructor: OpenLayers.Map - * Constructor for a new OpenLayers.Map instance. There are two possible - * ways to call the map constructor. See the examples below. - * - * Parameters: - * div - {DOMElement|String} The element or id of an element in your page - * that will contain the map. May be omitted if the <div> option is - * provided or if you intend to call the <render> method later. - * options - {Object} Optional object with properties to tag onto the map. - * - * Valid options (in addition to the listed API properties): - * center - {<OpenLayers.LonLat>|Array} The default initial center of the map. - * If provided as array, the first value is the x coordinate, - * and the 2nd value is the y coordinate. - * Only specify if <layers> is provided. - * Note that if an ArgParser/Permalink control is present, - * and the querystring contains coordinates, center will be set - * by that, and this option will be ignored. - * zoom - {Number} The initial zoom level for the map. Only specify if - * <layers> is provided. - * Note that if an ArgParser/Permalink control is present, - * and the querystring contains a zoom level, zoom will be set - * by that, and this option will be ignored. - * extent - {<OpenLayers.Bounds>|Array} The initial extent of the map. - * If provided as an array, the array should consist of - * four values (left, bottom, right, top). - * Only specify if <center> and <zoom> are not provided. - * - * Examples: - * (code) - * // create a map with default options in an element with the id "map1" - * var map = new OpenLayers.Map("map1"); - * - * // create a map with non-default options in an element with id "map2" - * var options = { - * projection: "EPSG:3857", - * maxExtent: new OpenLayers.Bounds(-200000, -200000, 200000, 200000), - * center: new OpenLayers.LonLat(-12356463.476333, 5621521.4854095) - * }; - * var map = new OpenLayers.Map("map2", options); - * - * // map with non-default options - same as above but with a single argument, - * // a restricted extent, and using arrays for bounds and center - * var map = new OpenLayers.Map({ - * div: "map_id", - * projection: "EPSG:3857", - * maxExtent: [-18924313.432222, -15538711.094146, 18924313.432222, 15538711.094146], - * restrictedExtent: [-13358338.893333, -9608371.5085962, 13358338.893333, 9608371.5085962], - * center: [-12356463.476333, 5621521.4854095] - * }); - * - * // create a map without a reference to a container - call render later - * var map = new OpenLayers.Map({ - * projection: "EPSG:3857", - * maxExtent: new OpenLayers.Bounds(-200000, -200000, 200000, 200000) - * }); - * (end) - */ - initialize: function (div, options) { - - // If only one argument is provided, check if it is an object. - if(arguments.length === 1 && typeof div === "object") { - options = div; - div = options && options.div; - } - - // Simple-type defaults are set in class definition. - // Now set complex-type defaults - this.tileSize = new OpenLayers.Size(OpenLayers.Map.TILE_WIDTH, - OpenLayers.Map.TILE_HEIGHT); - - this.paddingForPopups = new OpenLayers.Bounds(15, 15, 15, 15); - - this.theme = OpenLayers._getScriptLocation() + - 'theme/default/style.css'; - - // backup original options - this.options = OpenLayers.Util.extend({}, options); - - // now override default options - OpenLayers.Util.extend(this, options); - - var projCode = this.projection instanceof OpenLayers.Projection ? - this.projection.projCode : this.projection; - OpenLayers.Util.applyDefaults(this, OpenLayers.Projection.defaults[projCode]); - - // allow extents and center to be arrays - if (this.maxExtent && !(this.maxExtent instanceof OpenLayers.Bounds)) { - this.maxExtent = new OpenLayers.Bounds(this.maxExtent); - } - if (this.minExtent && !(this.minExtent instanceof OpenLayers.Bounds)) { - this.minExtent = new OpenLayers.Bounds(this.minExtent); - } - if (this.restrictedExtent && !(this.restrictedExtent instanceof OpenLayers.Bounds)) { - this.restrictedExtent = new OpenLayers.Bounds(this.restrictedExtent); - } - if (this.center && !(this.center instanceof OpenLayers.LonLat)) { - this.center = new OpenLayers.LonLat(this.center); - } - - // initialize layers array - this.layers = []; - - this.id = OpenLayers.Util.createUniqueID("OpenLayers.Map_"); - - this.div = OpenLayers.Util.getElement(div); - if(!this.div) { - this.div = document.createElement("div"); - this.div.style.height = "1px"; - this.div.style.width = "1px"; - } - - OpenLayers.Element.addClass(this.div, 'olMap'); - - // the viewPortDiv is the outermost div we modify - var id = this.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); - - this.events = new OpenLayers.Events( - this, this.viewPortDiv, null, this.fallThrough, - {includeXY: true} - ); - - if (OpenLayers.TileManager && this.tileManager !== null) { - if (!(this.tileManager instanceof OpenLayers.TileManager)) { - this.tileManager = new OpenLayers.TileManager(this.tileManager); - } - this.tileManager.addMap(this); - } - - // the layerContainerDiv is the one that holds all the layers - id = this.id + "_OpenLayers_Container"; - this.layerContainerDiv = OpenLayers.Util.createDiv(id); - this.layerContainerDiv.style.zIndex=this.Z_INDEX_BASE['Popup']-1; - this.layerContainerOriginPx = {x: 0, y: 0}; - this.applyTransform(); - - this.viewPortDiv.appendChild(this.layerContainerDiv); - - this.updateSize(); - if(this.eventListeners instanceof Object) { - this.events.on(this.eventListeners); - } - - if (this.autoUpdateSize === true) { - // updateSize on catching the window's resize - // Note that this is ok, as updateSize() does nothing if the - // map's size has not actually changed. - this.updateSizeDestroy = OpenLayers.Function.bind(this.updateSize, - this); - OpenLayers.Event.observe(window, 'resize', - this.updateSizeDestroy); - } - - // only append link stylesheet if the theme property is set - if(this.theme) { - // check existing links for equivalent url - 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; - } - } - // only add a new node if one with an equivalent url hasn't already - // been added - 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); - } - } - - if (this.controls == null) { // default controls - this.controls = []; - if (OpenLayers.Control != null) { // running full or lite? - // Navigation or TouchNavigation depending on what is in build - if (OpenLayers.Control.Navigation) { - this.controls.push(new OpenLayers.Control.Navigation()); - } else if (OpenLayers.Control.TouchNavigation) { - this.controls.push(new OpenLayers.Control.TouchNavigation()); - } - if (OpenLayers.Control.Zoom) { - this.controls.push(new OpenLayers.Control.Zoom()); - } else if (OpenLayers.Control.PanZoom) { - this.controls.push(new OpenLayers.Control.PanZoom()); - } - - if (OpenLayers.Control.ArgParser) { - this.controls.push(new OpenLayers.Control.ArgParser()); - } - if (OpenLayers.Control.Attribution) { - this.controls.push(new OpenLayers.Control.Attribution()); - } - } - } - - 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); - - - // always call map.destroy() - OpenLayers.Event.observe(window, 'unload', this.unloadDestroy); - - // add any initial layers - if (options && options.layers) { - /** - * If you have set options.center, the map center property will be - * set at this point. However, since setCenter has not been called, - * addLayers gets confused. So we delete the map center in this - * case. Because the check below uses options.center, it will - * be properly set below. - */ - delete this.center; - delete this.zoom; - this.addLayers(options.layers); - // set center (and optionally zoom) - if (options.center && !this.getCenter()) { - // zoom can be undefined here - this.setCenter(options.center, options.zoom); - } - } - - if (this.panMethod) { - this.panTween = new OpenLayers.Tween(this.panMethod); - } - if (this.zoomMethod && this.applyTransform.transform) { - this.zoomTween = new OpenLayers.Tween(this.zoomMethod); - } - }, - - /** - * APIMethod: getViewport - * Get the DOMElement representing the view port. - * - * Returns: - * {DOMElement} - */ - getViewport: function() { - return this.viewPortDiv; - }, - - /** - * APIMethod: render - * Render the map to a specified container. - * - * Parameters: - * div - {String|DOMElement} The container that the map should be rendered - * to. If different than the current container, the map viewport - * will be moved from the current to the new container. - */ - render: function(div) { - this.div = OpenLayers.Util.getElement(div); - OpenLayers.Element.addClass(this.div, 'olMap'); - this.viewPortDiv.parentNode.removeChild(this.viewPortDiv); - this.div.appendChild(this.viewPortDiv); - this.updateSize(); - }, - - /** - * Method: unloadDestroy - * Function that is called to destroy the map on page unload. stored here - * so that if map is manually destroyed, we can unregister this. - */ - unloadDestroy: null, - - /** - * Method: updateSizeDestroy - * When the map is destroyed, we need to stop listening to updateSize - * events: this method stores the function we need to unregister in - * non-IE browsers. - */ - updateSizeDestroy: null, - - /** - * APIMethod: destroy - * Destroy this map. - * Note that if you are using an application which removes a container - * of the map from the DOM, you need to ensure that you destroy the - * map *before* this happens; otherwise, the page unload handler - * will fail because the DOM elements that map.destroy() wants - * to clean up will be gone. (See - * http://trac.osgeo.org/openlayers/ticket/2277 for more information). - * This will apply to GeoExt and also to other applications which - * modify the DOM of the container of the OpenLayers Map. - */ - destroy:function() { - // if unloadDestroy is null, we've already been destroyed - if (!this.unloadDestroy) { - return false; - } - - // make sure panning doesn't continue after destruction - if(this.panTween) { - this.panTween.stop(); - this.panTween = null; - } - // make sure zooming doesn't continue after destruction - if(this.zoomTween) { - this.zoomTween.stop(); - this.zoomTween = null; - } - - // map has been destroyed. dont do it again! - OpenLayers.Event.stopObserving(window, 'unload', this.unloadDestroy); - this.unloadDestroy = null; - - if (this.updateSizeDestroy) { - OpenLayers.Event.stopObserving(window, 'resize', - this.updateSizeDestroy); - } - - this.paddingForPopups = null; - - if (this.controls != null) { - for (var i = this.controls.length - 1; i>=0; --i) { - this.controls[i].destroy(); - } - this.controls = null; - } - if (this.layers != null) { - for (var i = this.layers.length - 1; i>=0; --i) { - //pass 'false' to destroy so that map wont try to set a new - // baselayer after each baselayer is removed - this.layers[i].destroy(false); - } - this.layers = null; - } - if (this.viewPortDiv && this.viewPortDiv.parentNode) { - this.viewPortDiv.parentNode.removeChild(this.viewPortDiv); - } - this.viewPortDiv = null; - - if (this.tileManager) { - this.tileManager.removeMap(this); - this.tileManager = null; - } - - if(this.eventListeners) { - this.events.un(this.eventListeners); - this.eventListeners = null; - } - this.events.destroy(); - this.events = null; - - this.options = null; - }, - - /** - * APIMethod: setOptions - * Change the map options - * - * Parameters: - * options - {Object} Hashtable of options to tag to the map - */ - setOptions: function(options) { - var updatePxExtent = this.minPx && - options.restrictedExtent != this.restrictedExtent; - OpenLayers.Util.extend(this, options); - // force recalculation of minPx and maxPx - updatePxExtent && this.moveTo(this.getCachedCenter(), this.zoom, { - forceZoomChange: true - }); - }, - - /** - * APIMethod: getTileSize - * Get the tile size for the map - * - * Returns: - * {<OpenLayers.Size>} - */ - getTileSize: function() { - return this.tileSize; - }, - - - /** - * APIMethod: getBy - * Get a list of objects given a property and a match item. - * - * Parameters: - * array - {String} A property on the map whose value is an array. - * property - {String} A property on each item of the given array. - * 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(map[array][i][property]) evaluates to true, the item will - * be included in the array returned. If no items are found, an empty - * array is returned. - * - * Returns: - * {Array} An array of items where the given property matches the given - * criteria. - */ - 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; - }, - - /** - * APIMethod: getLayersBy - * Get a list of layers with properties matching the given criteria. - * - * Parameters: - * property - {String} A layer 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(layer[property]) evaluates to true, the layer will be - * included in the array returned. If no layers are found, an empty - * array is returned. - * - * Returns: - * {Array(<OpenLayers.Layer>)} A list of layers matching the given criteria. - * An empty array is returned if no matches are found. - */ - getLayersBy: function(property, match) { - return this.getBy("layers", property, match); - }, - - /** - * APIMethod: getLayersByName - * Get a list of layers with names matching the given name. - * - * Parameters: - * match - {String | Object} A layer 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(layer.name) evaluates to true, the layer will be included - * in the list of layers returned. If no layers are found, an empty - * array is returned. - * - * Returns: - * {Array(<OpenLayers.Layer>)} A list of layers matching the given name. - * An empty array is returned if no matches are found. - */ - getLayersByName: function(match) { - return this.getLayersBy("name", match); - }, - - /** - * APIMethod: getLayersByClass - * Get a list of layers of a given class (CLASS_NAME). - * - * Parameters: - * match - {String | Object} A layer class name. The 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 type.test(layer.CLASS_NAME) evaluates to true, the layer will - * be included in the list of layers returned. If no layers are - * found, an empty array is returned. - * - * Returns: - * {Array(<OpenLayers.Layer>)} A list of layers matching the given class. - * An empty array is returned if no matches are found. - */ - getLayersByClass: function(match) { - return this.getLayersBy("CLASS_NAME", match); - }, - - /** - * APIMethod: getControlsBy - * Get a list of controls with properties matching the given criteria. - * - * Parameters: - * 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(layer[property]) evaluates to true, the layer will be - * included in the array returned. If no layers 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) { - return this.getBy("controls", property, match); - }, - - /** - * APIMethod: getControlsByClass - * Get a list of controls of a given class (CLASS_NAME). - * - * Parameters: - * match - {String | Object} A control class name. The 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 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 class. - * An empty array is returned if no matches are found. - */ - getControlsByClass: function(match) { - return this.getControlsBy("CLASS_NAME", match); - }, - - /********************************************************/ - /* */ - /* Layer Functions */ - /* */ - /* The following functions deal with adding and */ - /* removing Layers to and from the Map */ - /* */ - /********************************************************/ - - /** - * APIMethod: getLayer - * Get a layer based on its id - * - * Parameters: - * id - {String} A layer id - * - * Returns: - * {<OpenLayers.Layer>} The Layer with the corresponding id from the map's - * layer collection, or null if not found. - */ - 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; - }, - - /** - * Method: setLayerZIndex - * - * Parameters: - * layer - {<OpenLayers.Layer>} - * zIdx - {int} - */ - setLayerZIndex: function (layer, zIdx) { - layer.setZIndex( - this.Z_INDEX_BASE[layer.isBaseLayer ? 'BaseLayer' : 'Overlay'] - + zIdx * 5 ); - }, - - /** - * Method: resetLayersZIndex - * Reset each layer's z-index based on layer's array index - */ - resetLayersZIndex: function() { - for (var i=0, len=this.layers.length; i<len; i++) { - var layer = this.layers[i]; - this.setLayerZIndex(layer, i); - } - }, - - /** - * APIMethod: addLayer - * - * Parameters: - * layer - {<OpenLayers.Layer>} - * - * Returns: - * {Boolean} True if the layer has been added to the map. - */ - addLayer: function (layer) { - for(var i = 0, len = this.layers.length; i < len; i++) { - if (this.layers[i] == layer) { - return false; - } - } - if (this.events.triggerEvent("preaddlayer", {layer: layer}) === false) { - return false; - } - if(this.allOverlays) { - layer.isBaseLayer = false; - } - - 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 || (this.allOverlays && !this.baseLayer)) { - if (this.baseLayer == null) { - // set the first baselaye we add as the baselayer - this.setBaseLayer(layer); - } else { - layer.setVisibility(false); - } - } else { - layer.redraw(); - } - - this.events.triggerEvent("addlayer", {layer: layer}); - layer.events.triggerEvent("added", {map: this, layer: layer}); - layer.afterAdd(); - - return true; - }, - - /** - * APIMethod: addLayers - * - * Parameters: - * layers - {Array(<OpenLayers.Layer>)} - */ - addLayers: function (layers) { - for (var i=0, len=layers.length; i<len; i++) { - this.addLayer(layers[i]); - } - }, - - /** - * APIMethod: removeLayer - * Removes a layer from the map by removing its visual element (the - * layer.div property), then removing it from the map's internal list - * of layers, setting the layer's map property to null. - * - * a "removelayer" event is triggered. - * - * very worthy of mention is that simply removing a layer from a map - * will not cause the removal of any popups which may have been created - * by the layer. this is due to the fact that it was decided at some - * point that popups would not belong to layers. thus there is no way - * for us to know here to which layer the popup belongs. - * - * A simple solution to this is simply to call destroy() on the layer. - * the default OpenLayers.Layer class's destroy() function - * automatically takes care to remove itself from whatever map it has - * been attached to. - * - * The correct solution is for the layer itself to register an - * event-handler on "removelayer" and when it is called, if it - * recognizes itself as the layer being removed, then it cycles through - * its own personal list of popups, removing them from the map. - * - * Parameters: - * layer - {<OpenLayers.Layer>} - * setNewBaseLayer - {Boolean} Default is true - */ - removeLayer: function(layer, setNewBaseLayer) { - if (this.events.triggerEvent("preremovelayer", {layer: layer}) === false) { - return; - } - 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 we removed the base layer, need to set a new one - 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.allOverlays) { - this.setBaseLayer(iLayer); - break; - } - } - } - } - - this.resetLayersZIndex(); - - this.events.triggerEvent("removelayer", {layer: layer}); - layer.events.triggerEvent("removed", {map: this, layer: layer}); - }, - - /** - * APIMethod: getNumLayers - * - * Returns: - * {Int} The number of layers attached to the map. - */ - getNumLayers: function () { - return this.layers.length; - }, - - /** - * APIMethod: getLayerIndex - * - * Parameters: - * layer - {<OpenLayers.Layer>} - * - * Returns: - * {Integer} The current (zero-based) index of the given layer in the map's - * layer stack. Returns -1 if the layer isn't on the map. - */ - getLayerIndex: function (layer) { - return OpenLayers.Util.indexOf(this.layers, layer); - }, - - /** - * APIMethod: setLayerIndex - * Move the given layer to the specified (zero-based) index in the layer - * list, changing its z-index in the map display. Use - * map.getLayerIndex() to find out the current index of a layer. Note - * that this cannot (or at least should not) be effectively used to - * raise base layers above overlays. - * - * Parameters: - * layer - {<OpenLayers.Layer>} - * idx - {int} - */ - 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, len=this.layers.length; i<len; i++) { - this.setLayerZIndex(this.layers[i], i); - } - this.events.triggerEvent("changelayer", { - layer: layer, property: "order" - }); - if(this.allOverlays) { - if(idx === 0) { - this.setBaseLayer(layer); - } else if(this.baseLayer !== this.layers[0]) { - this.setBaseLayer(this.layers[0]); - } - } - } - }, - - /** - * APIMethod: raiseLayer - * Change the index of the given layer by delta. If delta is positive, - * the layer is moved up the map's layer stack; if delta is negative, - * the layer is moved down. Again, note that this cannot (or at least - * should not) be effectively used to raise base layers above overlays. - * - * Paremeters: - * layer - {<OpenLayers.Layer>} - * delta - {int} - */ - raiseLayer: function (layer, delta) { - var idx = this.getLayerIndex(layer) + delta; - this.setLayerIndex(layer, idx); - }, - - /** - * APIMethod: setBaseLayer - * Allows user to specify one of the currently-loaded layers as the Map's - * new base layer. - * - * Parameters: - * newBaseLayer - {<OpenLayers.Layer>} - */ - setBaseLayer: function(newBaseLayer) { - - if (newBaseLayer != this.baseLayer) { - - // ensure newBaseLayer is already loaded - if (OpenLayers.Util.indexOf(this.layers, newBaseLayer) != -1) { - - // preserve center and scale when changing base layers - var center = this.getCachedCenter(); - var newResolution = OpenLayers.Util.getResolutionFromScale( - this.getScale(), newBaseLayer.units - ); - - // make the old base layer invisible - if (this.baseLayer != null && !this.allOverlays) { - this.baseLayer.setVisibility(false); - } - - // set new baselayer - this.baseLayer = newBaseLayer; - - if(!this.allOverlays || this.baseLayer.visibility) { - this.baseLayer.setVisibility(true); - // Layer may previously have been visible but not in range. - // In this case we need to redraw it to make it visible. - if (this.baseLayer.inRange === false) { - this.baseLayer.redraw(); - } - } - - // recenter the map - if (center != null) { - // new zoom level derived from old scale - var newZoom = this.getZoomForResolution( - newResolution || this.resolution, true - ); - // zoom and force zoom change - this.setCenter(center, newZoom, false, true); - } - - this.events.triggerEvent("changebaselayer", { - layer: this.baseLayer - }); - } - } - }, - - - /********************************************************/ - /* */ - /* Control Functions */ - /* */ - /* The following functions deal with adding and */ - /* removing Controls to and from the Map */ - /* */ - /********************************************************/ - - /** - * APIMethod: addControl - * Add the passed over control to the map. Optionally - * position the control at the given pixel. - * - * Parameters: - * control - {<OpenLayers.Control>} - * px - {<OpenLayers.Pixel>} - */ - addControl: function (control, px) { - this.controls.push(control); - this.addControlToMap(control, px); - }, - - /** - * APIMethod: addControls - * Add all of the passed over controls to the map. - * You can pass over an optional second array - * with pixel-objects to position the controls. - * The indices of the two arrays should match and - * you can add null as pixel for those controls - * you want to be autopositioned. - * - * Parameters: - * controls - {Array(<OpenLayers.Control>)} - * pixels - {Array(<OpenLayers.Pixel>)} - */ - addControls: function (controls, pixels) { - var pxs = (arguments.length === 1) ? [] : pixels; - for (var i=0, len=controls.length; i<len; i++) { - var ctrl = controls[i]; - var px = (pxs[i]) ? pxs[i] : null; - this.addControl( ctrl, px ); - } - }, - - /** - * Method: addControlToMap - * - * Parameters: - * - * control - {<OpenLayers.Control>} - * px - {<OpenLayers.Pixel>} - */ - addControlToMap: function (control, px) { - // If a control doesn't have a div at this point, it belongs in the - // viewport. - control.outsideViewport = (control.div != null); - - // If the map has a displayProjection, and the control doesn't, set - // the display projection. - 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 ); - } - } - if(control.autoActivate) { - control.activate(); - } - }, - - /** - * APIMethod: getControl - * - * Parameters: - * id - {String} ID of the control to return. - * - * Returns: - * {<OpenLayers.Control>} The control from the map's list of controls - * which has a matching 'id'. If none found, - * returns null. - */ - 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; - }, - - /** - * APIMethod: removeControl - * Remove a control from the map. Removes the control both from the map - * object's internal array of controls, as well as from the map's - * viewPort (assuming the control was not added outsideViewport) - * - * Parameters: - * control - {<OpenLayers.Control>} The control to remove. - */ - removeControl: function (control) { - //make sure control is non-null and actually part of our map - 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); - } - }, - - /********************************************************/ - /* */ - /* Popup Functions */ - /* */ - /* The following functions deal with adding and */ - /* removing Popups to and from the Map */ - /* */ - /********************************************************/ - - /** - * APIMethod: addPopup - * - * Parameters: - * popup - {<OpenLayers.Popup>} - * exclusive - {Boolean} If true, closes all other popups first - */ - addPopup: function(popup, exclusive) { - - if (exclusive) { - //remove all other popups from screen - 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); - } - }, - - /** - * APIMethod: removePopup - * - * Parameters: - * popup - {<OpenLayers.Popup>} - */ - removePopup: function(popup) { - OpenLayers.Util.removeItem(this.popups, popup); - if (popup.div) { - try { this.layerContainerDiv.removeChild(popup.div); } - catch (e) { } // Popups sometimes apparently get disconnected - // from the layerContainerDiv, and cause complaints. - } - popup.map = null; - }, - - /********************************************************/ - /* */ - /* Container Div Functions */ - /* */ - /* The following functions deal with the access to */ - /* and maintenance of the size of the container div */ - /* */ - /********************************************************/ - - /** - * APIMethod: getSize - * - * Returns: - * {<OpenLayers.Size>} An <OpenLayers.Size> object that represents the - * size, in pixels, of the div into which OpenLayers - * has been loaded. - * Note - A clone() of this locally cached variable is - * returned, so as not to allow users to modify it. - */ - getSize: function () { - var size = null; - if (this.size != null) { - size = this.size.clone(); - } - return size; - }, - - /** - * APIMethod: updateSize - * This function should be called by any external code which dynamically - * changes the size of the map div (because mozilla wont let us catch - * the "onresize" for an element) - */ - updateSize: function() { - // the div might have moved on the page, also - var newSize = this.getCurrentSize(); - if (newSize && !isNaN(newSize.h) && !isNaN(newSize.w)) { - this.events.clearMouseCache(); - var oldSize = this.getSize(); - if (oldSize == null) { - this.size = oldSize = newSize; - } - if (!newSize.equals(oldSize)) { - - // store the new size - this.size = newSize; - - //notify layers of mapresize - for(var i=0, len=this.layers.length; i<len; i++) { - this.layers[i].onMapResize(); - } - - var center = this.getCachedCenter(); - - if (this.baseLayer != null && center != null) { - var zoom = this.getZoom(); - this.zoom = null; - this.setCenter(center, zoom); - } - - } - } - this.events.triggerEvent("updatesize"); - }, - - /** - * Method: getCurrentSize - * - * Returns: - * {<OpenLayers.Size>} A new <OpenLayers.Size> object with the dimensions - * of the map div - */ - 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)) { - size.w = this.div.offsetWidth; - size.h = this.div.offsetHeight; - } - 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; - }, - - /** - * Method: calculateBounds - * - * Parameters: - * center - {<OpenLayers.LonLat>} Default is this.getCenter() - * resolution - {float} Default is this.getResolution() - * - * Returns: - * {<OpenLayers.Bounds>} A bounds based on resolution, center, and - * current mapsize. - */ - calculateBounds: function(center, resolution) { - - var extent = null; - - if (center == null) { - center = this.getCachedCenter(); - } - if (resolution == null) { - resolution = this.getResolution(); - } - - if ((center != null) && (resolution != null)) { - var halfWDeg = (this.size.w * resolution) / 2; - var halfHDeg = (this.size.h * resolution) / 2; - - extent = new OpenLayers.Bounds(center.lon - halfWDeg, - center.lat - halfHDeg, - center.lon + halfWDeg, - center.lat + halfHDeg); - } - - return extent; - }, - - - /********************************************************/ - /* */ - /* Zoom, Center, Pan Functions */ - /* */ - /* The following functions handle the validation, */ - /* getting and setting of the Zoom Level and Center */ - /* as well as the panning of the Map */ - /* */ - /********************************************************/ - /** - * APIMethod: getCenter - * - * Returns: - * {<OpenLayers.LonLat>} - */ - getCenter: function () { - var center = null; - var cachedCenter = this.getCachedCenter(); - if (cachedCenter) { - center = cachedCenter.clone(); - } - return center; - }, - - /** - * Method: getCachedCenter - * - * Returns: - * {<OpenLayers.LonLat>} - */ - getCachedCenter: function() { - if (!this.center && this.size) { - this.center = this.getLonLatFromViewPortPx({ - x: this.size.w / 2, - y: this.size.h / 2 - }); - } - return this.center; - }, - - /** - * APIMethod: getZoom - * - * Returns: - * {Integer} - */ - getZoom: function () { - return this.zoom; - }, - - /** - * APIMethod: pan - * Allows user to pan by a value of screen pixels - * - * Parameters: - * dx - {Integer} - * dy - {Integer} - * options - {Object} Options to configure panning: - * - *animate* {Boolean} Use panTo instead of setCenter. Default is true. - * - *dragging* {Boolean} Call setCenter with dragging true. Default is - * false. - */ - pan: function(dx, dy, options) { - options = OpenLayers.Util.applyDefaults(options, { - animate: true, - dragging: false - }); - if (options.dragging) { - if (dx != 0 || dy != 0) { - this.moveByPx(dx, dy); - } - } else { - // getCenter - var centerPx = this.getViewPortPxFromLonLat(this.getCachedCenter()); - - // adjust - var newCenterPx = centerPx.add(dx, dy); - - if (this.dragging || !newCenterPx.equals(centerPx)) { - var newCenterLonLat = this.getLonLatFromViewPortPx(newCenterPx); - if (options.animate) { - this.panTo(newCenterLonLat); - } else { - this.moveTo(newCenterLonLat); - if(this.dragging) { - this.dragging = false; - this.events.triggerEvent("moveend"); - } - } - } - } - - }, - - /** - * APIMethod: panTo - * Allows user to pan to a new lonlat - * If the new lonlat is in the current extent the map will slide smoothly - * - * Parameters: - * lonlat - {<OpenLayers.LonLat>} - */ - panTo: function(lonlat) { - if (this.panTween && this.getExtent().scale(this.panRatio).containsLonLat(lonlat)) { - var center = this.getCachedCenter(); - - // center will not change, don't do nothing - if (lonlat.equals(center)) { - return; - } - - var from = this.getPixelFromLonLat(center); - var to = this.getPixelFromLonLat(lonlat); - var vector = { x: to.x - from.x, y: to.y - from.y }; - var last = { x: 0, y: 0 }; - - this.panTween.start( { x: 0, y: 0 }, vector, this.panDuration, { - callbacks: { - eachStep: OpenLayers.Function.bind(function(px) { - var x = px.x - last.x, - y = px.y - last.y; - this.moveByPx(x, y); - last.x = Math.round(px.x); - last.y = Math.round(px.y); - }, this), - done: OpenLayers.Function.bind(function(px) { - this.moveTo(lonlat); - this.dragging = false; - this.events.triggerEvent("moveend"); - }, this) - } - }); - } else { - this.setCenter(lonlat); - } - }, - - /** - * APIMethod: setCenter - * Set the map center (and optionally, the zoom level). - * - * Parameters: - * lonlat - {<OpenLayers.LonLat>|Array} The new center location. - * If provided as array, the first value is the x coordinate, - * and the 2nd value is the y coordinate. - * zoom - {Integer} Optional zoom level. - * dragging - {Boolean} Specifies whether or not to trigger - * movestart/end events - * forceZoomChange - {Boolean} Specifies whether or not to trigger zoom - * change events (needed on baseLayer change) - * - * TBD: reconsider forceZoomChange in 3.0 - */ - setCenter: function(lonlat, zoom, dragging, forceZoomChange) { - if (this.panTween) { - this.panTween.stop(); - } - if (this.zoomTween) { - this.zoomTween.stop(); - } - this.moveTo(lonlat, zoom, { - 'dragging': dragging, - 'forceZoomChange': forceZoomChange - }); - }, - - /** - * Method: moveByPx - * Drag the map by pixels. - * - * Parameters: - * dx - {Number} - * dy - {Number} - */ - moveByPx: function(dx, dy) { - var hw = this.size.w / 2; - var hh = this.size.h / 2; - var x = hw + dx; - var y = hh + dy; - var wrapDateLine = this.baseLayer.wrapDateLine; - var xRestriction = 0; - var yRestriction = 0; - if (this.restrictedExtent) { - xRestriction = hw; - yRestriction = hh; - // wrapping the date line makes no sense for restricted extents - wrapDateLine = false; - } - dx = wrapDateLine || - x <= this.maxPx.x - xRestriction && - x >= this.minPx.x + xRestriction ? Math.round(dx) : 0; - dy = y <= this.maxPx.y - yRestriction && - y >= this.minPx.y + yRestriction ? Math.round(dy) : 0; - if (dx || dy) { - if (!this.dragging) { - this.dragging = true; - this.events.triggerEvent("movestart"); - } - this.center = null; - if (dx) { - this.layerContainerOriginPx.x -= dx; - this.minPx.x -= dx; - this.maxPx.x -= dx; - } - if (dy) { - this.layerContainerOriginPx.y -= dy; - this.minPx.y -= dy; - this.maxPx.y -= dy; - } - this.applyTransform(); - var layer, i, len; - for (i=0, len=this.layers.length; i<len; ++i) { - layer = this.layers[i]; - if (layer.visibility && - (layer === this.baseLayer || layer.inRange)) { - layer.moveByPx(dx, dy); - layer.events.triggerEvent("move"); - } - } - this.events.triggerEvent("move"); - } - }, - - /** - * Method: adjustZoom - * - * Parameters: - * zoom - {Number} The zoom level to adjust - * - * Returns: - * {Integer} Adjusted zoom level that shows a map not wider than its - * <baseLayer>'s maxExtent. - */ - adjustZoom: function(zoom) { - if (this.baseLayer && this.baseLayer.wrapDateLine) { - var resolution, resolutions = this.baseLayer.resolutions, - maxResolution = this.getMaxExtent().getWidth() / this.size.w; - if (this.getResolutionForZoom(zoom) > maxResolution) { - if (this.fractionalZoom) { - zoom = this.getZoomForResolution(maxResolution); - } else { - for (var i=zoom|0, ii=resolutions.length; i<ii; ++i) { - if (resolutions[i] <= maxResolution) { - zoom = i; - break; - } - } - } - } - } - return zoom; - }, - - /** - * APIMethod: getMinZoom - * Returns the minimum zoom level for the current map view. If the base - * layer is configured with <wrapDateLine> set to true, this will be the - * first zoom level that shows no more than one world width in the current - * map viewport. Components that rely on this value (e.g. zoom sliders) - * should also listen to the map's "updatesize" event and call this method - * in the "updatesize" listener. - * - * Returns: - * {Number} Minimum zoom level that shows a map not wider than its - * <baseLayer>'s maxExtent. This is an Integer value, unless the map is - * configured with <fractionalZoom> set to true. - */ - getMinZoom: function() { - return this.adjustZoom(0); - }, - - /** - * Method: moveTo - * - * Parameters: - * lonlat - {<OpenLayers.LonLat>} - * zoom - {Integer} - * options - {Object} - */ - moveTo: function(lonlat, zoom, options) { - if (lonlat != null && !(lonlat instanceof OpenLayers.LonLat)) { - lonlat = new OpenLayers.LonLat(lonlat); - } - if (!options) { - options = {}; - } - if (zoom != null) { - zoom = parseFloat(zoom); - if (!this.fractionalZoom) { - zoom = Math.round(zoom); - } - } - var requestedZoom = zoom; - zoom = this.adjustZoom(zoom); - if (zoom !== requestedZoom) { - // zoom was adjusted, so keep old lonlat to avoid panning - lonlat = this.getCenter(); - } - // dragging is false by default - var dragging = options.dragging || this.dragging; - // forceZoomChange is false by default - var forceZoomChange = options.forceZoomChange; - - if (!this.getCachedCenter() && !this.isValidLonLat(lonlat)) { - lonlat = this.maxExtent.getCenterLonLat(); - this.center = lonlat.clone(); - } - - if(this.restrictedExtent != null) { - // In 3.0, decide if we want to change interpretation of maxExtent. - if(lonlat == null) { - lonlat = this.center; - } - if(zoom == null) { - zoom = this.getZoom(); - } - var resolution = this.getResolutionForZoom(zoom); - var extent = this.calculateBounds(lonlat, resolution); - if(!this.restrictedExtent.containsBounds(extent)) { - var maxCenter = this.restrictedExtent.getCenterLonLat(); - if(extent.getWidth() > this.restrictedExtent.getWidth()) { - lonlat = new OpenLayers.LonLat(maxCenter.lon, lonlat.lat); - } else if(extent.left < this.restrictedExtent.left) { - lonlat = lonlat.add(this.restrictedExtent.left - - extent.left, 0); - } else if(extent.right > this.restrictedExtent.right) { - lonlat = lonlat.add(this.restrictedExtent.right - - extent.right, 0); - } - if(extent.getHeight() > this.restrictedExtent.getHeight()) { - lonlat = new OpenLayers.LonLat(lonlat.lon, maxCenter.lat); - } else if(extent.bottom < this.restrictedExtent.bottom) { - lonlat = lonlat.add(0, this.restrictedExtent.bottom - - extent.bottom); - } - else if(extent.top > this.restrictedExtent.top) { - lonlat = lonlat.add(0, this.restrictedExtent.top - - extent.top); - } - } - } - - var zoomChanged = forceZoomChange || ( - (this.isValidZoomLevel(zoom)) && - (zoom != this.getZoom()) ); - - var centerChanged = (this.isValidLonLat(lonlat)) && - (!lonlat.equals(this.center)); - - // if neither center nor zoom will change, no need to do anything - if (zoomChanged || centerChanged || dragging) { - dragging || this.events.triggerEvent("movestart", { - zoomChanged: zoomChanged - }); - - if (centerChanged) { - if (!zoomChanged && this.center) { - // if zoom hasnt changed, just slide layerContainer - // (must be done before setting this.center to new value) - this.centerLayerContainer(lonlat); - } - this.center = lonlat.clone(); - } - - var res = zoomChanged ? - this.getResolutionForZoom(zoom) : this.getResolution(); - // (re)set the layerContainerDiv's location - if (zoomChanged || this.layerContainerOrigin == null) { - this.layerContainerOrigin = this.getCachedCenter(); - this.layerContainerOriginPx.x = 0; - this.layerContainerOriginPx.y = 0; - this.applyTransform(); - var maxExtent = this.getMaxExtent({restricted: true}); - var maxExtentCenter = maxExtent.getCenterLonLat(); - var lonDelta = this.center.lon - maxExtentCenter.lon; - var latDelta = maxExtentCenter.lat - this.center.lat; - var extentWidth = Math.round(maxExtent.getWidth() / res); - var extentHeight = Math.round(maxExtent.getHeight() / res); - this.minPx = { - x: (this.size.w - extentWidth) / 2 - lonDelta / res, - y: (this.size.h - extentHeight) / 2 - latDelta / res - }; - this.maxPx = { - x: this.minPx.x + Math.round(maxExtent.getWidth() / res), - y: this.minPx.y + Math.round(maxExtent.getHeight() / res) - }; - } - - if (zoomChanged) { - this.zoom = zoom; - this.resolution = res; - } - - var bounds = this.getExtent(); - - //send the move call to the baselayer and all the overlays - - if(this.baseLayer.visibility) { - this.baseLayer.moveTo(bounds, zoomChanged, options.dragging); - options.dragging || this.baseLayer.events.triggerEvent( - "moveend", {zoomChanged: zoomChanged} - ); - } - - bounds = this.baseLayer.getExtent(); - - for (var i=this.layers.length-1; i>=0; --i) { - var layer = this.layers[i]; - if (layer !== this.baseLayer && !layer.isBaseLayer) { - var inRange = layer.calculateInRange(); - if (layer.inRange != inRange) { - // the inRange property has changed. If the layer is - // no longer in range, we turn it off right away. If - // the layer is no longer out of range, the moveTo - // call below will turn on the layer. - layer.inRange = inRange; - if (!inRange) { - layer.display(false); - } - this.events.triggerEvent("changelayer", { - layer: layer, property: "visibility" - }); - } - if (inRange && layer.visibility) { - layer.moveTo(bounds, zoomChanged, options.dragging); - options.dragging || layer.events.triggerEvent( - "moveend", {zoomChanged: zoomChanged} - ); - } - } - } - - this.events.triggerEvent("move"); - dragging || this.events.triggerEvent("moveend"); - - if (zoomChanged) { - //redraw popups - for (var i=0, len=this.popups.length; i<len; i++) { - this.popups[i].updatePosition(); - } - this.events.triggerEvent("zoomend"); - } - } - }, - - /** - * Method: centerLayerContainer - * This function takes care to recenter the layerContainerDiv. - * - * Parameters: - * lonlat - {<OpenLayers.LonLat>} - */ - centerLayerContainer: function (lonlat) { - var originPx = this.getViewPortPxFromLonLat(this.layerContainerOrigin); - var newPx = this.getViewPortPxFromLonLat(lonlat); - - if ((originPx != null) && (newPx != null)) { - var oldLeft = this.layerContainerOriginPx.x; - var oldTop = this.layerContainerOriginPx.y; - var newLeft = Math.round(originPx.x - newPx.x); - var newTop = Math.round(originPx.y - newPx.y); - this.applyTransform( - (this.layerContainerOriginPx.x = newLeft), - (this.layerContainerOriginPx.y = newTop)); - var dx = oldLeft - newLeft; - var dy = oldTop - newTop; - this.minPx.x -= dx; - this.maxPx.x -= dx; - this.minPx.y -= dy; - this.maxPx.y -= dy; - } - }, - - /** - * Method: isValidZoomLevel - * - * Parameters: - * zoomLevel - {Integer} - * - * Returns: - * {Boolean} Whether or not the zoom level passed in is non-null and - * within the min/max range of zoom levels. - */ - isValidZoomLevel: function(zoomLevel) { - return ( (zoomLevel != null) && - (zoomLevel >= 0) && - (zoomLevel < this.getNumZoomLevels()) ); - }, - - /** - * Method: isValidLonLat - * - * Parameters: - * lonlat - {<OpenLayers.LonLat>} - * - * Returns: - * {Boolean} Whether or not the lonlat passed in is non-null and within - * the maxExtent bounds - */ - isValidLonLat: function(lonlat) { - var valid = false; - if (lonlat != null) { - var maxExtent = this.getMaxExtent(); - var worldBounds = this.baseLayer.wrapDateLine && maxExtent; - valid = maxExtent.containsLonLat(lonlat, {worldBounds: worldBounds}); - } - return valid; - }, - - /********************************************************/ - /* */ - /* Layer Options */ - /* */ - /* Accessor functions to Layer Options parameters */ - /* */ - /********************************************************/ - - /** - * APIMethod: getProjection - * This method returns a string representing the projection. In - * the case of projection support, this will be the srsCode which - * is loaded -- otherwise it will simply be the string value that - * was passed to the projection at startup. - * - * FIXME: In 3.0, we will remove getProjectionObject, and instead - * return a Projection object from this function. - * - * Returns: - * {String} The Projection string from the base layer or null. - */ - getProjection: function() { - var projection = this.getProjectionObject(); - return projection ? projection.getCode() : null; - }, - - /** - * APIMethod: getProjectionObject - * Returns the projection obect from the baselayer. - * - * Returns: - * {<OpenLayers.Projection>} The Projection of the base layer. - */ - getProjectionObject: function() { - var projection = null; - if (this.baseLayer != null) { - projection = this.baseLayer.projection; - } - return projection; - }, - - /** - * APIMethod: getMaxResolution - * - * Returns: - * {String} The Map's Maximum Resolution - */ - getMaxResolution: function() { - var maxResolution = null; - if (this.baseLayer != null) { - maxResolution = this.baseLayer.maxResolution; - } - return maxResolution; - }, - - /** - * APIMethod: getMaxExtent - * - * Parameters: - * options - {Object} - * - * Allowed Options: - * restricted - {Boolean} If true, returns restricted extent (if it is - * available.) - * - * Returns: - * {<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 (options) { - var maxExtent = null; - if(options && options.restricted && this.restrictedExtent){ - maxExtent = this.restrictedExtent; - } else if (this.baseLayer != null) { - maxExtent = this.baseLayer.maxExtent; - } - return maxExtent; - }, - - /** - * APIMethod: getNumZoomLevels - * - * Returns: - * {Integer} The total number of zoom levels that can be displayed by the - * current baseLayer. - */ - getNumZoomLevels: function() { - var numZoomLevels = null; - if (this.baseLayer != null) { - numZoomLevels = this.baseLayer.numZoomLevels; - } - return numZoomLevels; - }, - - /********************************************************/ - /* */ - /* Baselayer Functions */ - /* */ - /* The following functions, all publicly exposed */ - /* in the API?, are all merely wrappers to the */ - /* the same calls on whatever layer is set as */ - /* the current base layer */ - /* */ - /********************************************************/ - - /** - * APIMethod: getExtent - * - * Returns: - * {<OpenLayers.Bounds>} A Bounds object which represents the lon/lat - * bounds of the current viewPort. - * If no baselayer is set, returns null. - */ - getExtent: function () { - var extent = null; - if (this.baseLayer != null) { - extent = this.baseLayer.getExtent(); - } - return extent; - }, - - /** - * APIMethod: getResolution - * - * Returns: - * {Float} The current resolution of the map. - * If no baselayer is set, returns null. - */ - getResolution: function () { - var resolution = null; - if (this.baseLayer != null) { - resolution = this.baseLayer.getResolution(); - } else if(this.allOverlays === true && this.layers.length > 0) { - // while adding the 1st layer to the map in allOverlays mode, - // this.baseLayer is not set yet when we need the resolution - // for calculateInRange. - resolution = this.layers[0].getResolution(); - } - 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 - * - * Returns: - * {Float} The current scale denominator of the map. - * If no baselayer is set, returns null. - */ - 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; - }, - - - /** - * APIMethod: getZoomForExtent - * - * 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. - * - * Returns: - * {Integer} A suitable zoom level for the specified bounds. - * If no baselayer is set, returns null. - */ - getZoomForExtent: function (bounds, closest) { - var zoom = null; - if (this.baseLayer != null) { - zoom = this.baseLayer.getZoomForExtent(bounds, closest); - } - return zoom; - }, - - /** - * APIMethod: getResolutionForZoom - * - * Parameters: - * zoom - {Float} - * - * Returns: - * {Float} A suitable resolution for the specified zoom. If no baselayer - * is set, returns null. - */ - getResolutionForZoom: function(zoom) { - var resolution = null; - if(this.baseLayer) { - resolution = this.baseLayer.getResolutionForZoom(zoom); - } - return resolution; - }, - - /** - * APIMethod: getZoomForResolution - * - * Parameters: - * resolution - {Float} - * closest - {Boolean} Find the zoom level that corresponds to the absolute - * closest resolution, which may result in a zoom whose corresponding - * resolution is actually smaller than we would have desired (if this - * is being called from a getZoomForExtent() call, then this means that - * the returned zoom index might not actually contain the entire - * extent specified... but it'll be close). - * Default is false. - * - * Returns: - * {Integer} A suitable zoom level for the specified resolution. - * If no baselayer is set, returns null. - */ - getZoomForResolution: function(resolution, closest) { - var zoom = null; - if (this.baseLayer != null) { - zoom = this.baseLayer.getZoomForResolution(resolution, closest); - } - return zoom; - }, - - /********************************************************/ - /* */ - /* Zooming Functions */ - /* */ - /* The following functions, all publicly exposed */ - /* in the API, are all merely wrappers to the */ - /* the setCenter() function */ - /* */ - /********************************************************/ - - /** - * APIMethod: zoomTo - * Zoom to a specific zoom level. Zooming will be animated unless the map - * is configured with {zoomMethod: null}. To zoom without animation, use - * <setCenter> without a lonlat argument. - * - * Parameters: - * zoom - {Integer} - */ - zoomTo: function(zoom, xy) { - // non-API arguments: - // xy - {<OpenLayers.Pixel>} optional zoom origin - - var map = this; - if (map.isValidZoomLevel(zoom)) { - if (map.baseLayer.wrapDateLine) { - zoom = map.adjustZoom(zoom); - } - if (map.zoomTween) { - var currentRes = map.getResolution(), - targetRes = map.getResolutionForZoom(zoom), - start = {scale: 1}, - end = {scale: currentRes / targetRes}; - if (map.zoomTween.playing && map.zoomTween.duration < 3 * map.zoomDuration) { - // update the end scale, and reuse the running zoomTween - map.zoomTween.finish = { - scale: map.zoomTween.finish.scale * end.scale - }; - } else { - if (!xy) { - var size = map.getSize(); - xy = {x: size.w / 2, y: size.h / 2}; - } - map.zoomTween.start(start, end, map.zoomDuration, { - minFrameRate: 50, // don't spend much time zooming - callbacks: { - eachStep: function(data) { - var containerOrigin = map.layerContainerOriginPx, - scale = data.scale, - dx = ((scale - 1) * (containerOrigin.x - xy.x)) | 0, - dy = ((scale - 1) * (containerOrigin.y - xy.y)) | 0; - map.applyTransform(containerOrigin.x + dx, containerOrigin.y + dy, scale); - }, - done: function(data) { - map.applyTransform(); - var resolution = map.getResolution() / data.scale, - zoom = map.getZoomForResolution(resolution, true) - map.moveTo(map.getZoomTargetCenter(xy, resolution), zoom, true); - } - } - }); - } - } else { - var center = xy ? - map.getZoomTargetCenter(xy, map.getResolutionForZoom(zoom)) : - null; - map.setCenter(center, zoom); - } - } - }, - - /** - * APIMethod: zoomIn - * - */ - zoomIn: function() { - this.zoomTo(this.getZoom() + 1); - }, - - /** - * APIMethod: zoomOut - * - */ - zoomOut: function() { - this.zoomTo(this.getZoom() - 1); - }, - - /** - * APIMethod: zoomToExtent - * Zoom to the passed in bounds, recenter - * - * Parameters: - * bounds - {<OpenLayers.Bounds>|Array} If provided as an array, the array - * should consist of four values (left, bottom, right, top). - * 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, closest) { - if (!(bounds instanceof OpenLayers.Bounds)) { - bounds = new OpenLayers.Bounds(bounds); - } - var center = bounds.getCenterLonLat(); - if (this.baseLayer.wrapDateLine) { - var maxExtent = this.getMaxExtent(); - - //fix straddling bounds (in the case of a bbox that straddles the - // dateline, it's left and right boundaries will appear backwards. - // we fix this by allowing a right value that is greater than the - // max value at the dateline -- this allows us to pass a valid - // bounds to calculate zoom) - // - bounds = bounds.clone(); - while (bounds.right < bounds.left) { - bounds.right += maxExtent.getWidth(); - } - //if the bounds was straddling (see above), then the center point - // we got from it was wrong. So we take our new bounds and ask it - // for the center. - // - center = bounds.getCenterLonLat().wrapDateLine(maxExtent); - } - this.setCenter(center, this.getZoomForExtent(bounds, closest)); - }, - - /** - * APIMethod: zoomToMaxExtent - * Zoom to the full extent and recenter. - * - * Parameters: - * options - {Object} - * - * Allowed Options: - * restricted - {Boolean} True to zoom to restricted extent if it is - * set. Defaults to true. - */ - zoomToMaxExtent: function(options) { - //restricted is true by default - var restricted = (options) ? options.restricted : true; - - var maxExtent = this.getMaxExtent({ - 'restricted': restricted - }); - this.zoomToExtent(maxExtent); - }, - - /** - * APIMethod: zoomToScale - * Zoom to a specified scale - * - * 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, closest) { - var res = OpenLayers.Util.getResolutionFromScale(scale, - this.baseLayer.units); - - var halfWDeg = (this.size.w * res) / 2; - var halfHDeg = (this.size.h * res) / 2; - var center = this.getCachedCenter(); - - var extent = new OpenLayers.Bounds(center.lon - halfWDeg, - center.lat - halfHDeg, - center.lon + halfWDeg, - center.lat + halfHDeg); - this.zoomToExtent(extent, closest); - }, - - /********************************************************/ - /* */ - /* Translation Functions */ - /* */ - /* The following functions translate between */ - /* LonLat, LayerPx, and ViewPortPx */ - /* */ - /********************************************************/ - - // - // TRANSLATION: LonLat <-> ViewPortPx - // - - /** - * Method: getLonLatFromViewPortPx - * - * Parameters: - * viewPortPx - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or - * an object with a 'x' - * and 'y' properties. - * - * Returns: - * {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in view - * port <OpenLayers.Pixel>, translated into lon/lat - * by the current base layer. - */ - getLonLatFromViewPortPx: function (viewPortPx) { - var lonlat = null; - if (this.baseLayer != null) { - lonlat = this.baseLayer.getLonLatFromViewPortPx(viewPortPx); - } - return lonlat; - }, - - /** - * APIMethod: getViewPortPxFromLonLat - * - * Parameters: - * lonlat - {<OpenLayers.LonLat>} - * - * Returns: - * {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in - * <OpenLayers.LonLat>, translated into view port - * pixels by the current base layer. - */ - getViewPortPxFromLonLat: function (lonlat) { - var px = null; - if (this.baseLayer != null) { - px = this.baseLayer.getViewPortPxFromLonLat(lonlat); - } - return px; - }, - - /** - * Method: getZoomTargetCenter - * - * Parameters: - * xy - {<OpenLayers.Pixel>} The zoom origin pixel location on the screen - * resolution - {Float} The resolution we want to get the center for - * - * Returns: - * {<OpenLayers.LonLat>} The location of the map center after the - * transformation described by the origin xy and the target resolution. - */ - getZoomTargetCenter: function (xy, resolution) { - var lonlat = null, - size = this.getSize(), - deltaX = size.w/2 - xy.x, - deltaY = xy.y - size.h/2, - zoomPoint = this.getLonLatFromPixel(xy); - if (zoomPoint) { - lonlat = new OpenLayers.LonLat( - zoomPoint.lon + deltaX * resolution, - zoomPoint.lat + deltaY * resolution - ); - } - return lonlat; - }, - - // - // CONVENIENCE TRANSLATION FUNCTIONS FOR API - // - - /** - * APIMethod: getLonLatFromPixel - * - * Parameters: - * px - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or an object with - * a 'x' and 'y' properties. - * - * Returns: - * {<OpenLayers.LonLat>} An OpenLayers.LonLat corresponding to the given - * OpenLayers.Pixel, translated into lon/lat by the - * current base layer - */ - getLonLatFromPixel: function (px) { - return this.getLonLatFromViewPortPx(px); - }, - - /** - * APIMethod: getPixelFromLonLat - * Returns a pixel location given a map location. The map location is - * translated to an integer pixel location (in viewport pixel - * coordinates) by the current base layer. - * - * Parameters: - * lonlat - {<OpenLayers.LonLat>} A map location. - * - * Returns: - * {<OpenLayers.Pixel>} An OpenLayers.Pixel corresponding to the - * <OpenLayers.LonLat> translated into view port pixels by the current - * base layer. - */ - getPixelFromLonLat: function (lonlat) { - var px = this.getViewPortPxFromLonLat(lonlat); - px.x = Math.round(px.x); - px.y = Math.round(px.y); - return px; - }, - - /** - * Method: getGeodesicPixelSize - * - * Parameters: - * px - {<OpenLayers.Pixel>} The pixel to get the geodesic length for. If - * not provided, the center pixel of the map viewport will be used. - * - * Returns: - * {<OpenLayers.Size>} The geodesic size of the pixel in kilometers. - */ - getGeodesicPixelSize: function(px) { - var lonlat = px ? this.getLonLatFromPixel(px) : ( - this.getCachedCenter() || new OpenLayers.LonLat(0, 0)); - var res = this.getResolution(); - var left = lonlat.add(-res / 2, 0); - var right = lonlat.add(res / 2, 0); - var bottom = lonlat.add(0, -res / 2); - var top = lonlat.add(0, res / 2); - var dest = new OpenLayers.Projection("EPSG:4326"); - var source = this.getProjectionObject() || dest; - if(!source.equals(dest)) { - left.transform(source, dest); - right.transform(source, dest); - bottom.transform(source, dest); - top.transform(source, dest); - } - - return new OpenLayers.Size( - OpenLayers.Util.distVincenty(left, right), - OpenLayers.Util.distVincenty(bottom, top) - ); - }, - - - - // - // TRANSLATION: ViewPortPx <-> LayerPx - // - - /** - * APIMethod: getViewPortPxFromLayerPx - * - * Parameters: - * layerPx - {<OpenLayers.Pixel>} - * - * Returns: - * {<OpenLayers.Pixel>} Layer Pixel translated into ViewPort Pixel - * coordinates - */ - getViewPortPxFromLayerPx:function(layerPx) { - var viewPortPx = null; - if (layerPx != null) { - var dX = this.layerContainerOriginPx.x; - var dY = this.layerContainerOriginPx.y; - viewPortPx = layerPx.add(dX, dY); - } - return viewPortPx; - }, - - /** - * APIMethod: getLayerPxFromViewPortPx - * - * Parameters: - * viewPortPx - {<OpenLayers.Pixel>} - * - * Returns: - * {<OpenLayers.Pixel>} ViewPort Pixel translated into Layer Pixel - * coordinates - */ - getLayerPxFromViewPortPx:function(viewPortPx) { - var layerPx = null; - if (viewPortPx != null) { - var dX = -this.layerContainerOriginPx.x; - var dY = -this.layerContainerOriginPx.y; - layerPx = viewPortPx.add(dX, dY); - if (isNaN(layerPx.x) || isNaN(layerPx.y)) { - layerPx = null; - } - } - return layerPx; - }, - - // - // TRANSLATION: LonLat <-> LayerPx - // - - /** - * Method: getLonLatFromLayerPx - * - * Parameters: - * px - {<OpenLayers.Pixel>} - * - * Returns: - * {<OpenLayers.LonLat>} - */ - getLonLatFromLayerPx: function (px) { - //adjust for displacement of layerContainerDiv - px = this.getViewPortPxFromLayerPx(px); - return this.getLonLatFromViewPortPx(px); - }, - - /** - * APIMethod: getLayerPxFromLonLat - * - * Parameters: - * lonlat - {<OpenLayers.LonLat>} lonlat - * - * Returns: - * {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in - * <OpenLayers.LonLat>, translated into layer pixels - * by the current base layer - */ - getLayerPxFromLonLat: function (lonlat) { - //adjust for displacement of layerContainerDiv - var px = this.getPixelFromLonLat(lonlat); - return this.getLayerPxFromViewPortPx(px); - }, - - /** - * Method: applyTransform - * Applies the given transform to the <layerContainerDiv>. This method has - * a 2-stage fallback from translate3d/scale3d via translate/scale to plain - * style.left/style.top, in which case no scaling is supported. - * - * Parameters: - * x - {Number} x parameter for the translation. Defaults to the x value of - * the map's <layerContainerOriginPx> - * y - {Number} y parameter for the translation. Defaults to the y value of - * the map's <layerContainerOriginPx> - * scale - {Number} scale. Defaults to 1 if not provided. - */ - applyTransform: function(x, y, scale) { - scale = scale || 1; - var origin = this.layerContainerOriginPx, - needTransform = scale !== 1; - x = x || origin.x; - y = y || origin.y; - - var style = this.layerContainerDiv.style, - transform = this.applyTransform.transform, - template = this.applyTransform.template; - - if (transform === undefined) { - transform = OpenLayers.Util.vendorPrefix.style('transform'); - this.applyTransform.transform = transform; - if (transform) { - // Try translate3d, but only if the viewPortDiv has a transform - // defined in a stylesheet - var computedStyle = OpenLayers.Element.getStyle(this.viewPortDiv, - OpenLayers.Util.vendorPrefix.css('transform')); - if (!computedStyle || computedStyle !== 'none') { - template = ['translate3d(', ',0) ', 'scale3d(', ',1)']; - style[transform] = [template[0], '0,0', template[1]].join(''); - } - // If no transform is defined in the stylesheet or translate3d - // does not stick, use translate and scale - if (!template || !~style[transform].indexOf(template[0])) { - template = ['translate(', ') ', 'scale(', ')']; - } - this.applyTransform.template = template; - } - } - - // If we do 3d transforms, we always want to use them. If we do 2d - // transforms, we only use them when we need to. - if (transform !== null && (template[0] === 'translate3d(' || needTransform === true)) { - // Our 2d transforms are combined with style.left and style.top, so - // adjust x and y values and set the origin as left and top - if (needTransform === true && template[0] === 'translate(') { - x -= origin.x; - y -= origin.y; - style.left = origin.x + 'px'; - style.top = origin.y + 'px'; - } - style[transform] = [ - template[0], x, 'px,', y, 'px', template[1], - template[2], scale, ',', scale, template[3] - ].join(''); - } else { - style.left = x + 'px'; - style.top = y + 'px'; - // We previously might have had needTransform, so remove transform - if (transform !== null) { - style[transform] = ''; - } - } - }, - - CLASS_NAME: "OpenLayers.Map" -}); - -/** - * Constant: TILE_WIDTH - * {Integer} 256 Default tile width (unless otherwise specified) - */ -OpenLayers.Map.TILE_WIDTH = 256; -/** - * Constant: TILE_HEIGHT - * {Integer} 256 Default tile height (unless otherwise specified) - */ -OpenLayers.Map.TILE_HEIGHT = 256; -/* ====================================================================== - OpenLayers/Layer.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - - -/** - * @requires OpenLayers/BaseTypes/Class.js - * @requires OpenLayers/Map.js - * @requires OpenLayers/Projection.js - */ - -/** - * Class: OpenLayers.Layer - */ -OpenLayers.Layer = OpenLayers.Class({ - - /** - * APIProperty: id - * {String} - */ - id: null, - - /** - * APIProperty: name - * {String} - */ - name: null, - - /** - * APIProperty: div - * {DOMElement} - */ - div: null, - - /** - * APIProperty: opacity - * {Float} The layer's opacity. Float number between 0.0 and 1.0. Default - * is 1. - */ - opacity: 1, - - /** - * 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: RESOLUTION_PROPERTIES - * {Array} The properties that are used for calculating resolutions - * information. - */ - RESOLUTION_PROPERTIES: [ - 'scales', 'resolutions', - 'maxScale', 'minScale', - 'maxResolution', 'minResolution', - 'numZoomLevels', 'maxZoomLevel' - ], - - /** - * APIProperty: events - * {<OpenLayers.Events>} - * - * 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: - * loadstart - Triggered when layer loading starts. When using a Vector - * layer with a Fixed or BBOX strategy, the event object includes - * a *filter* property holding the OpenLayers.Filter used when - * calling read on the protocol. - * loadend - Triggered when layer loading ends. When using a Vector layer - * with a Fixed or BBOX strategy, the event object includes a - * *response* property holding an OpenLayers.Protocol.Response object. - * visibilitychanged - Triggered when the layer's visibility property is - * changed, e.g. by turning the layer on or off in the layer switcher. - * Note that the actual visibility of the layer can also change if it - * gets out of range (see <calculateInRange>). If you also want to catch - * these cases, register for the map's 'changelayer' event instead. - * move - Triggered when layer moves (triggered with every mousemove - * during a drag). - * moveend - Triggered when layer is done moving, object passed as - * argument has a zoomChanged boolean property which tells that the - * zoom has changed. - * added - Triggered after the layer is added to a map. Listeners will - * receive an object with a *map* property referencing the map and a - * *layer* property referencing the layer. - * removed - Triggered after the layer is removed from the map. Listeners - * will receive an object with a *map* property referencing the map and - * a *layer* property referencing the layer. - */ - events: null, - - /** - * APIProperty: map - * {<OpenLayers.Map>} This variable is set when the layer is added to - * the map, via the accessor function setMap(). - */ - map: null, - - /** - * APIProperty: isBaseLayer - * {Boolean} Whether or not the layer is a base layer. This should be set - * individually by all subclasses. Default is false - */ - isBaseLayer: false, - - /** - * Property: alpha - * {Boolean} The layer's images have an alpha channel. Default is false. - */ - alpha: false, - - /** - * APIProperty: displayInLayerSwitcher - * {Boolean} Display the layer's name in the layer switcher. Default is - * true. - */ - displayInLayerSwitcher: true, - - /** - * APIProperty: visibility - * {Boolean} The layer should be displayed in the map. Default is true. - */ - visibility: true, - - /** - * APIProperty: attribution - * {String} Attribution string, displayed when an - * <OpenLayers.Control.Attribution> has been added to the map. - */ - attribution: null, - - /** - * Property: inRange - * {Boolean} The current map resolution is within the layer's min/max - * range. This is set in <OpenLayers.Map.setCenter> whenever the zoom - * changes. - */ - inRange: false, - - /** - * Propery: imageSize - * {<OpenLayers.Size>} For layers with a gutter, the image is larger than - * the tile by twice the gutter in each dimension. - */ - imageSize: null, - - // OPTIONS - - /** - * Property: options - * {Object} An optional object whose properties will be set on the layer. - * Any of the layer properties can be set as a property of the options - * object and sent to the constructor when the layer is created. - */ - options: null, - - /** - * APIProperty: eventListeners - * {Object} If set as an option at construction, the eventListeners - * object will be registered with <OpenLayers.Events.on>. Object - * structure must be a listeners object as shown in the example for - * the events.on method. - */ - eventListeners: null, - - /** - * APIProperty: gutter - * {Integer} Determines the width (in pixels) of the gutter around image - * tiles to ignore. By setting this property to a non-zero value, - * images will be requested that are wider and taller than the tile - * size by a value of 2 x gutter. This allows artifacts of rendering - * at tile edges to be ignored. Set a gutter value that is equal to - * half the size of the widest symbol that needs to be displayed. - * Defaults to zero. Non-tiled layers always have zero gutter. - */ - gutter: 0, - - /** - * APIProperty: projection - * {<OpenLayers.Projection>} or {<String>} Specifies the projection of the layer. - * Can be set in the layer options. If not specified in the layer options, - * it is set to the default projection specified in the map, - * when the layer is added to the map. - * Projection along with default maxExtent and resolutions - * are set automatically with commercial baselayers in EPSG:3857, - * such as Google, Bing and OpenStreetMap, and do not need to be specified. - * Otherwise, if specifying projection, also set maxExtent, - * maxResolution or resolutions as appropriate. - * When using vector layers with strategies, layer projection should be set - * to the projection of the source data if that is different from the map default. - * - * Can be either a string or an <OpenLayers.Projection> object; - * if a string is passed, will be converted to an object when - * the layer is added to the map. - * - */ - projection: null, - - /** - * APIProperty: units - * {String} The layer map units. Defaults to null. Possible values - * are 'degrees' (or 'dd'), 'm', 'ft', 'km', 'mi', 'inches'. - * Normally taken from the projection. - * Only required if both map and layers do not define a projection, - * or if they define a projection which does not define units. - */ - units: null, - - /** - * APIProperty: scales - * {Array} An array of map scales in descending order. The values in the - * array correspond to the map scale denominator. Note that these - * values only make sense if the display (monitor) resolution of the - * client is correctly guessed by whomever is configuring the - * application. In addition, the units property must also be set. - * Use <resolutions> instead wherever possible. - */ - scales: null, - - /** - * APIProperty: resolutions - * {Array} A list of map resolutions (map units per pixel) in descending - * order. If this is not set in the layer constructor, it will be set - * based on other resolution related properties (maxExtent, - * maxResolution, maxScale, etc.). - */ - resolutions: null, - - /** - * APIProperty: maxExtent - * {<OpenLayers.Bounds>|Array} If provided as an array, the array - * should consist of four values (left, bottom, right, top). - * The maximum extent for the layer. Defaults to null. - * - * The center of these bounds will not stray outside - * of the viewport extent during panning. In addition, if - * <displayOutsideMaxExtent> is set to false, data will not be - * requested that falls completely outside of these bounds. - */ - maxExtent: null, - - /** - * APIProperty: minExtent - * {<OpenLayers.Bounds>|Array} If provided as an array, the array - * should consist of four values (left, bottom, right, top). - * The minimum extent for the layer. Defaults to null. - */ - minExtent: null, - - /** - * APIProperty: maxResolution - * {Float} Default max is 360 deg / 256 px, which corresponds to - * zoom level 0 on gmaps. Specify a different value in the layer - * options if you are not using the default <OpenLayers.Map.tileSize> - * and displaying the whole world. - */ - maxResolution: null, - - /** - * APIProperty: minResolution - * {Float} - */ - minResolution: null, - - /** - * APIProperty: numZoomLevels - * {Integer} - */ - numZoomLevels: null, - - /** - * APIProperty: minScale - * {Float} - */ - minScale: null, - - /** - * APIProperty: maxScale - * {Float} - */ - maxScale: null, - - /** - * APIProperty: displayOutsideMaxExtent - * {Boolean} Request map tiles that are completely outside of the max - * extent for this layer. Defaults to false. - */ - displayOutsideMaxExtent: false, - - /** - * APIProperty: wrapDateLine - * {Boolean} Wraps the world at the international dateline, so the map can - * be panned infinitely in longitudinal direction. Only use this on the - * base layer, and only if the layer's maxExtent equals the world bounds. - * #487 for more info. - */ - wrapDateLine: false, - - /** - * Property: metadata - * {Object} This object can be used to store additional information on a - * layer object. - */ - metadata: null, - - /** - * Constructor: OpenLayers.Layer - * - * Parameters: - * name - {String} The layer name - * options - {Object} Hashtable of extra options to tag onto the layer - */ - initialize: function(name, options) { - - this.metadata = {}; - - options = OpenLayers.Util.extend({}, options); - // make sure we respect alwaysInRange if set on the prototype - if (this.alwaysInRange != null) { - options.alwaysInRange = this.alwaysInRange; - } - 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.div.dir = "ltr"; - - this.events = new OpenLayers.Events(this, this.div); - if(this.eventListeners instanceof Object) { - this.events.on(this.eventListeners); - } - - } - }, - - /** - * Method: destroy - * Destroy is a destructor: this is to alleviate cyclic references which - * the Javascript garbage cleaner can not take care of on its own. - * - * Parameters: - * setNewBaseLayer - {Boolean} Set a new base layer when this layer has - * been destroyed. Default is 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); - } - this.events.destroy(); - } - this.eventListeners = null; - this.events = null; - }, - - /** - * Method: clone - * - * Parameters: - * obj - {<OpenLayers.Layer>} The layer to be cloned - * - * Returns: - * {<OpenLayers.Layer>} An exact clone of this <OpenLayers.Layer> - */ - clone: function (obj) { - - if (obj == null) { - obj = new OpenLayers.Layer(this.name, this.getOptions()); - } - - // catch any randomly tagged-on properties - OpenLayers.Util.applyDefaults(obj, this); - - // a cloned layer should never have its map property set - // because it has not been added to a map yet. - obj.map = null; - - return obj; - }, - - /** - * Method: getOptions - * Extracts an object from the layer with the properties that were set as - * options, but updates them with the values currently set on the - * instance. - * - * Returns: - * {Object} the <options> of the layer, representing the current state. - */ - getOptions: function() { - var options = {}; - for(var o in this.options) { - options[o] = this[o]; - } - return options; - }, - - /** - * APIMethod: setName - * Sets the new layer name for this layer. Can trigger a changelayer event - * on the map. - * - * Parameters: - * newName - {String} The new name. - */ - setName: function(newName) { - if (newName != this.name) { - this.name = newName; - if (this.map != null) { - this.map.events.triggerEvent("changelayer", { - layer: this, - property: "name" - }); - } - } - }, - - /** - * APIMethod: addOptions - * - * Parameters: - * newOptions - {Object} - * reinitialize - {Boolean} If set to true, and if resolution options of the - * current baseLayer were changed, the map will be recentered to make - * sure that it is displayed with a valid resolution, and a - * changebaselayer event will be triggered. - */ - addOptions: function (newOptions, reinitialize) { - - if (this.options == null) { - this.options = {}; - } - - if (newOptions) { - // make sure this.projection references a projection object - if(typeof newOptions.projection == "string") { - newOptions.projection = new OpenLayers.Projection(newOptions.projection); - } - if (newOptions.projection) { - // get maxResolution, units and maxExtent from projection defaults if - // they are not defined already - OpenLayers.Util.applyDefaults(newOptions, - OpenLayers.Projection.defaults[newOptions.projection.getCode()]); - } - // allow array for extents - if (newOptions.maxExtent && !(newOptions.maxExtent instanceof OpenLayers.Bounds)) { - newOptions.maxExtent = new OpenLayers.Bounds(newOptions.maxExtent); - } - if (newOptions.minExtent && !(newOptions.minExtent instanceof OpenLayers.Bounds)) { - newOptions.minExtent = new OpenLayers.Bounds(newOptions.minExtent); - } - } - - // update our copy for clone - OpenLayers.Util.extend(this.options, newOptions); - - // add new options to this - OpenLayers.Util.extend(this, newOptions); - - // get the units from the projection, if we have a projection - // and it it has units - if(this.projection && this.projection.getUnits()) { - this.units = this.projection.getUnits(); - } - - // re-initialize resolutions if necessary, i.e. if any of the - // properties of the "properties" array defined below is set - // in the new options - if(this.map) { - // store current resolution so we can try to restore it later - var resolution = this.map.getResolution(); - var properties = this.RESOLUTION_PROPERTIES.concat( - ["projection", "units", "minExtent", "maxExtent"] - ); - for(var o in newOptions) { - if(newOptions.hasOwnProperty(o) && - OpenLayers.Util.indexOf(properties, o) >= 0) { - - this.initResolutions(); - if (reinitialize && this.map.baseLayer === this) { - // update map position, and restore previous resolution - this.map.setCenter(this.map.getCenter(), - this.map.getZoomForResolution(resolution), - false, true - ); - // trigger a changebaselayer event to make sure that - // all controls (especially - // OpenLayers.Control.PanZoomBar) get notified of the - // new options - this.map.events.triggerEvent("changebaselayer", { - layer: this - }); - } - break; - } - } - } - }, - - /** - * APIMethod: onMapResize - * This function can be implemented by subclasses - */ - onMapResize: function() { - //this function can be implemented by subclasses - }, - - /** - * APIMethod: redraw - * Redraws the layer. Returns true if the layer was redrawn, false if not. - * - * Returns: - * {Boolean} The layer was redrawn. - */ - redraw: function() { - var redrawn = false; - if (this.map) { - - // min/max Range may have changed - this.inRange = this.calculateInRange(); - - // map's center might not yet be set - var extent = this.getExtent(); - - if (extent && this.inRange && this.visibility) { - var zoomChanged = true; - this.moveTo(extent, zoomChanged, false); - this.events.triggerEvent("moveend", - {"zoomChanged": zoomChanged}); - redrawn = true; - } - } - return redrawn; - }, - - /** - * Method: moveTo - * - * Parameters: - * bounds - {<OpenLayers.Bounds>} - * zoomChanged - {Boolean} Tells when zoom has changed, as layers have to - * do some init work in that case. - * dragging - {Boolean} - */ - moveTo:function(bounds, zoomChanged, dragging) { - var display = this.visibility; - if (!this.isBaseLayer) { - display = display && this.inRange; - } - this.display(display); - }, - - /** - * Method: moveByPx - * Move the layer based on pixel vector. To be implemented by subclasses. - * - * Parameters: - * dx - {Number} The x coord of the displacement vector. - * dy - {Number} The y coord of the displacement vector. - */ - moveByPx: function(dx, dy) { - }, - - /** - * 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. - * - * Here we take care to bring over any of the necessary default - * properties from the map. - * - * Parameters: - * map - {<OpenLayers.Map>} - */ - setMap: function(map) { - if (this.map == null) { - - this.map = map; - - // grab some essential layer data from the map if it hasn't already - // been set - this.maxExtent = this.maxExtent || this.map.maxExtent; - this.minExtent = this.minExtent || this.map.minExtent; - - this.projection = this.projection || this.map.projection; - if (typeof this.projection == "string") { - this.projection = new OpenLayers.Projection(this.projection); - } - - // Check the projection to see if we can get units -- if not, refer - // to properties. - 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"; - } - - // deal with gutters - this.setTileSize(); - } - }, - - /** - * Method: afterAdd - * Called at the end of the map.addLayer sequence. At this point, the map - * will have a base layer. To be overridden by subclasses. - */ - afterAdd: function() { - }, - - /** - * APIMethod: removeMap - * Just as setMap() allows each layer the possibility to take a - * personalized action on being added to the map, removeMap() allows - * each layer to take a personalized action on being removed from it. - * For now, this will be mostly unused, except for the EventPane layer, - * which needs this hook so that it can remove the special invisible - * pane. - * - * Parameters: - * map - {<OpenLayers.Map>} - */ - removeMap: function(map) { - //to be overridden by subclasses - }, - - /** - * APIMethod: getImageSize - * - * Parameters: - * bounds - {<OpenLayers.Bounds>} optional tile bounds, can be used - * by subclasses that have to deal with different tile sizes at the - * layer extent edges (e.g. Zoomify) - * - * Returns: - * {<OpenLayers.Size>} The size that the image should be, taking into - * account gutters. - */ - getImageSize: function(bounds) { - return (this.imageSize || this.tileSize); - }, - - /** - * APIMethod: setTileSize - * Set the tile size based on the map size. This also sets layer.imageSize - * or use by Tile.Image. - * - * Parameters: - * size - {<OpenLayers.Size>} - */ - setTileSize: function(size) { - var tileSize = (size) ? size : - ((this.tileSize) ? this.tileSize : - this.map.getTileSize()); - this.tileSize = tileSize; - if(this.gutter) { - // layers with gutters need non-null tile sizes - //if(tileSize == null) { - // OpenLayers.console.error("Error in layer.setMap() for " + - // this.name + ": layers with " + - // "gutters need non-null tile sizes"); - //} - this.imageSize = new OpenLayers.Size(tileSize.w + (2*this.gutter), - tileSize.h + (2*this.gutter)); - } - }, - - /** - * APIMethod: getVisibility - * - * Returns: - * {Boolean} The layer should be displayed (if in range). - */ - getVisibility: function() { - return this.visibility; - }, - - /** - * APIMethod: setVisibility - * Set the visibility flag for the layer and hide/show & redraw - * accordingly. Fire event unless otherwise specified - * - * Note that visibility is no longer simply whether or not the layer's - * style.display is set to "block". Now we store a 'visibility' state - * property on the layer class, this allows us to remember whether or - * not we *desire* for a layer to be visible. In the case where the - * map's resolution is out of the layer's range, this desire may be - * subverted. - * - * Parameters: - * visibility - {Boolean} Whether or not to display the layer (if in range) - */ - 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"); - } - }, - - /** - * APIMethod: display - * Hide or show the Layer. This is designed to be used internally, and - * is not generally the way to enable or disable the layer. For that, - * use the setVisibility function instead.. - * - * Parameters: - * display - {Boolean} - */ - display: function(display) { - if (display != (this.div.style.display != "none")) { - this.div.style.display = (display && this.calculateInRange()) ? "block" : "none"; - } - }, - - /** - * APIMethod: calculateInRange - * - * Returns: - * {Boolean} The layer is displayable at the current map's current - * resolution. Note that if 'alwaysInRange' is true for the layer, - * this function will always return true. - */ - 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; - }, - - /** - * APIMethod: setIsBaseLayer - * - * Parameters: - * isBaseLayer - {Boolean} - */ - setIsBaseLayer: function(isBaseLayer) { - if (isBaseLayer != this.isBaseLayer) { - this.isBaseLayer = isBaseLayer; - if (this.map != null) { - this.map.events.triggerEvent("changebaselayer", { - layer: this - }); - } - } - }, - - /********************************************************/ - /* */ - /* Baselayer Functions */ - /* */ - /********************************************************/ - - /** - * Method: initResolutions - * This method's responsibility is to set up the 'resolutions' array - * for the layer -- this array is what the layer will use to interface - * between the zoom levels of the map and the resolution display - * of the layer. - * - * The user has several options that determine how the array is set up. - * - * For a detailed explanation, see the following wiki from the - * openlayers.org homepage: - * http://trac.openlayers.org/wiki/SettingZoomLevels - */ - initResolutions: function() { - - // ok we want resolutions, here's our strategy: - // - // 1. if resolutions are defined in the layer config, use them - // 2. else, if scales are defined in the layer config then derive - // resolutions from these scales - // 3. else, attempt to calculate resolutions from maxResolution, - // minResolution, numZoomLevels, maxZoomLevel set in the - // layer config - // 4. if we still don't have resolutions, and if resolutions - // are defined in the same, use them - // 5. else, if scales are defined in the map then derive - // resolutions from these scales - // 6. else, attempt to calculate resolutions from maxResolution, - // minResolution, numZoomLevels, maxZoomLevel set in the - // map - // 7. hope for the best! - - var i, len, p; - var props = {}, alwaysInRange = true; - - // get resolution data from layer config - // (we also set alwaysInRange in the layer as appropriate) - for(i=0, len=this.RESOLUTION_PROPERTIES.length; i<len; i++) { - p = this.RESOLUTION_PROPERTIES[i]; - props[p] = this.options[p]; - if(alwaysInRange && this.options[p]) { - alwaysInRange = false; - } - } - if(this.options.alwaysInRange == null) { - this.alwaysInRange = alwaysInRange; - } - - // if we don't have resolutions then attempt to derive them from scales - if(props.resolutions == null) { - props.resolutions = this.resolutionsFromScales(props.scales); - } - - // if we still don't have resolutions then attempt to calculate them - if(props.resolutions == null) { - props.resolutions = this.calculateResolutions(props); - } - - // if we couldn't calculate resolutions then we look at we have - // in the map - if(props.resolutions == null) { - for(i=0, len=this.RESOLUTION_PROPERTIES.length; i<len; i++) { - p = this.RESOLUTION_PROPERTIES[i]; - props[p] = this.options[p] != null ? - this.options[p] : this.map[p]; - } - if(props.resolutions == null) { - props.resolutions = this.resolutionsFromScales(props.scales); - } - if(props.resolutions == null) { - props.resolutions = this.calculateResolutions(props); - } - } - - // ok, we new need to set properties in the instance - - // get maxResolution from the config if it's defined there - var maxResolution; - if(this.options.maxResolution && - this.options.maxResolution !== "auto") { - maxResolution = this.options.maxResolution; - } - if(this.options.minScale) { - maxResolution = OpenLayers.Util.getResolutionFromScale( - this.options.minScale, this.units); - } - - // get minResolution from the config if it's defined there - var minResolution; - if(this.options.minResolution && - this.options.minResolution !== "auto") { - minResolution = this.options.minResolution; - } - if(this.options.maxScale) { - minResolution = OpenLayers.Util.getResolutionFromScale( - this.options.maxScale, this.units); - } - - if(props.resolutions) { - - //sort resolutions array descendingly - props.resolutions.sort(function(a, b) { - return (b - a); - }); - - // if we still don't have a maxResolution get it from the - // resolutions array - if(!maxResolution) { - maxResolution = props.resolutions[0]; - } - - // if we still don't have a minResolution get it from the - // resolutions array - if(!minResolution) { - var lastIdx = props.resolutions.length - 1; - minResolution = props.resolutions[lastIdx]; - } - } - - this.resolutions = props.resolutions; - if(this.resolutions) { - len = this.resolutions.length; - this.scales = new Array(len); - for(i=0; i<len; i++) { - this.scales[i] = OpenLayers.Util.getScaleFromResolution( - this.resolutions[i], this.units); - } - this.numZoomLevels = len; - } - this.minResolution = minResolution; - if(minResolution) { - this.maxScale = OpenLayers.Util.getScaleFromResolution( - minResolution, this.units); - } - this.maxResolution = maxResolution; - if(maxResolution) { - this.minScale = OpenLayers.Util.getScaleFromResolution( - maxResolution, this.units); - } - }, - - /** - * Method: resolutionsFromScales - * Derive resolutions from scales. - * - * Parameters: - * scales - {Array(Number)} Scales - * - * Returns - * {Array(Number)} Resolutions - */ - resolutionsFromScales: function(scales) { - if(scales == null) { - return; - } - var resolutions, i, len; - len = scales.length; - resolutions = new Array(len); - for(i=0; i<len; i++) { - resolutions[i] = OpenLayers.Util.getResolutionFromScale( - scales[i], this.units); - } - return resolutions; - }, - - /** - * Method: calculateResolutions - * Calculate resolutions based on the provided properties. - * - * Parameters: - * props - {Object} Properties - * - * Returns: - * {Array({Number})} Array of resolutions. - */ - calculateResolutions: function(props) { - - var viewSize, wRes, hRes; - - // determine maxResolution - var maxResolution = props.maxResolution; - if(props.minScale != null) { - maxResolution = - OpenLayers.Util.getResolutionFromScale(props.minScale, - this.units); - } else if(maxResolution == "auto" && this.maxExtent != null) { - viewSize = this.map.getSize(); - wRes = this.maxExtent.getWidth() / viewSize.w; - hRes = this.maxExtent.getHeight() / viewSize.h; - maxResolution = Math.max(wRes, hRes); - } - - // determine minResolution - var minResolution = props.minResolution; - if(props.maxScale != null) { - minResolution = - OpenLayers.Util.getResolutionFromScale(props.maxScale, - this.units); - } else if(props.minResolution == "auto" && this.minExtent != null) { - viewSize = this.map.getSize(); - wRes = this.minExtent.getWidth() / viewSize.w; - hRes = this.minExtent.getHeight()/ viewSize.h; - minResolution = Math.max(wRes, hRes); - } - - if(typeof maxResolution !== "number" && - typeof minResolution !== "number" && - this.maxExtent != null) { - // maxResolution for default grid sets assumes that at zoom - // level zero, the whole world fits on one tile. - var tileSize = this.map.getTileSize(); - maxResolution = Math.max( - this.maxExtent.getWidth() / tileSize.w, - this.maxExtent.getHeight() / tileSize.h - ); - } - - // determine numZoomLevels - var maxZoomLevel = props.maxZoomLevel; - var numZoomLevels = props.numZoomLevels; - if(typeof minResolution === "number" && - typeof maxResolution === "number" && numZoomLevels === undefined) { - var ratio = maxResolution / minResolution; - numZoomLevels = Math.floor(Math.log(ratio) / Math.log(2)) + 1; - } else if(numZoomLevels === undefined && maxZoomLevel != null) { - numZoomLevels = maxZoomLevel + 1; - } - - // are we able to calculate resolutions? - if(typeof numZoomLevels !== "number" || numZoomLevels <= 0 || - (typeof maxResolution !== "number" && - typeof minResolution !== "number")) { - return; - } - - // now we have numZoomLevels and at least one of maxResolution - // or minResolution, we can populate the resolutions array - - var resolutions = new Array(numZoomLevels); - var base = 2; - if(typeof minResolution == "number" && - typeof maxResolution == "number") { - // if maxResolution and minResolution are set, we calculate - // the base for exponential scaling that starts at - // maxResolution and ends at minResolution in numZoomLevels - // steps. - base = Math.pow( - (maxResolution / minResolution), - (1 / (numZoomLevels - 1)) - ); - } - - var i; - if(typeof maxResolution === "number") { - for(i=0; i<numZoomLevels; i++) { - resolutions[i] = maxResolution / Math.pow(base, i); - } - } else { - for(i=0; i<numZoomLevels; i++) { - resolutions[numZoomLevels - 1 - i] = - minResolution * Math.pow(base, i); - } - } - - return resolutions; - }, - - /** - * APIMethod: getResolution - * - * Returns: - * {Float} The currently selected resolution of the map, taken from the - * resolutions array, indexed by current zoom level. - */ - getResolution: function() { - var zoom = this.map.getZoom(); - return this.getResolutionForZoom(zoom); - }, - - /** - * APIMethod: getExtent - * - * Returns: - * {<OpenLayers.Bounds>} A Bounds object which represents the lon/lat - * bounds of the current viewPort. - */ - getExtent: function() { - // just use stock map calculateBounds function -- passing no arguments - // means it will user map's current center & resolution - // - return this.map.calculateBounds(); - }, - - /** - * APIMethod: getZoomForExtent - * - * Parameters: - * extent - {<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. - * - * Returns: - * {Integer} The index of the zoomLevel (entry in the resolutions array) - * for the passed-in extent. We do this by calculating the ideal - * resolution for the given extent (based on the map size) and then - * calling getZoomForResolution(), passing along the 'closest' - * parameter. - */ - 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); - }, - - /** - * Method: getDataExtent - * Calculates the max extent which includes all of the data for the layer. - * This function is to be implemented by subclasses. - * - * Returns: - * {<OpenLayers.Bounds>} - */ - getDataExtent: function () { - //to be implemented by subclasses - }, - - /** - * APIMethod: getResolutionForZoom - * - * Parameters: - * zoom - {Float} - * - * Returns: - * {Float} A suitable resolution for the specified zoom. - */ - 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[low] - - ((zoom-low) * (this.resolutions[low]-this.resolutions[high])); - } else { - resolution = this.resolutions[Math.round(zoom)]; - } - return resolution; - }, - - /** - * APIMethod: getZoomForResolution - * - * Parameters: - * resolution - {Float} - * closest - {Boolean} Find the zoom level that corresponds to the absolute - * closest resolution, which may result in a zoom whose corresponding - * resolution is actually smaller than we would have desired (if this - * is being called from a getZoomForExtent() call, then this means that - * the returned zoom index might not actually contain the entire - * extent specified... but it'll be close). - * Default is false. - * - * Returns: - * {Integer} The index of the zoomLevel (entry in the resolutions array) - * that corresponds to the best fit resolution given the passed in - * value and the 'closest' specification. - */ - getZoomForResolution: function(resolution, closest) { - var zoom, i, len; - 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(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 + ((highRes - resolution) / dRes); - } else { - zoom = lowZoom; - } - } else { - var diff; - var minDiff = Number.POSITIVE_INFINITY; - for(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; - }, - - /** - * APIMethod: getLonLatFromViewPortPx - * - * Parameters: - * viewPortPx - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or - * an object with a 'x' - * and 'y' properties. - * - * Returns: - * {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in - * view port <OpenLayers.Pixel>, translated into lon/lat by the layer. - */ - getLonLatFromViewPortPx: function (viewPortPx) { - var lonlat = null; - var map = this.map; - if (viewPortPx != null && map.minPx) { - var res = map.getResolution(); - var maxExtent = map.getMaxExtent({restricted: true}); - var lon = (viewPortPx.x - map.minPx.x) * res + maxExtent.left; - var lat = (map.minPx.y - viewPortPx.y) * res + maxExtent.top; - lonlat = new OpenLayers.LonLat(lon, lat); - - if (this.wrapDateLine) { - lonlat = lonlat.wrapDateLine(this.maxExtent); - } - } - return lonlat; - }, - - /** - * APIMethod: getViewPortPxFromLonLat - * Returns a pixel location given a map location. This method will return - * fractional pixel values. - * - * Parameters: - * lonlat - {<OpenLayers.LonLat>|Object} An OpenLayers.LonLat or - * an object with a 'lon' - * and 'lat' properties. - * - * Returns: - * {<OpenLayers.Pixel>} An <OpenLayers.Pixel> which is the passed-in - * lonlat translated into view port pixels. - */ - getViewPortPxFromLonLat: function (lonlat, resolution) { - var px = null; - if (lonlat != null) { - resolution = resolution || this.map.getResolution(); - var extent = this.map.calculateBounds(null, resolution); - px = new OpenLayers.Pixel( - (1/resolution * (lonlat.lon - extent.left)), - (1/resolution * (extent.top - lonlat.lat)) - ); - } - return px; - }, - - /** - * APIMethod: setOpacity - * Sets the opacity for the entire layer (all images) - * - * Parameters: - * opacity - {Float} - */ - setOpacity: function(opacity) { - if (opacity != this.opacity) { - this.opacity = opacity; - var childNodes = this.div.childNodes; - for(var i = 0, len = childNodes.length; i < len; ++i) { - var element = childNodes[i].firstChild || childNodes[i]; - var lastChild = childNodes[i].lastChild; - //TODO de-uglify this - if (lastChild && lastChild.nodeName.toLowerCase() === "iframe") { - element = lastChild.parentNode; - } - OpenLayers.Util.modifyDOMElement(element, null, null, null, - null, null, null, opacity); - } - if (this.map != null) { - this.map.events.triggerEvent("changelayer", { - layer: this, - property: "opacity" - }); - } - } - }, - - /** - * Method: getZIndex - * - * Returns: - * {Integer} the z-index of this layer - */ - getZIndex: function () { - return this.div.style.zIndex; - }, - - /** - * Method: setZIndex - * - * Parameters: - * zIndex - {Integer} - */ - setZIndex: function (zIndex) { - this.div.style.zIndex = zIndex; - }, - - /** - * Method: adjustBounds - * This function will take a bounds, and if wrapDateLine option is set - * on the layer, it will return a bounds which is wrapped around the - * world. We do not wrap for bounds which *cross* the - * maxExtent.left/right, only bounds which are entirely to the left - * or entirely to the right. - * - * Parameters: - * bounds - {<OpenLayers.Bounds>} - */ - adjustBounds: function (bounds) { - - if (this.gutter) { - // Adjust the extent of a bounds in map units by the - // layer's gutter in pixels. - 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) { - // wrap around the date line, within the limits of rounding error - var wrappingOptions = { - 'rightTolerance':this.getResolution(), - 'leftTolerance':this.getResolution() - }; - bounds = bounds.wrapDateLine(this.maxExtent, wrappingOptions); - - } - return bounds; - }, - - CLASS_NAME: "OpenLayers.Layer" -}); -/* ====================================================================== - OpenLayers/Layer/SphericalMercator.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @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: 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) { - return OpenLayers.Layer.prototype.getLonLatFromViewPortPx.apply(this, arguments); - }, - - /** - * 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) { - return OpenLayers.Layer.prototype.getViewPortPxFromLonLat.apply(this, arguments); - }, - - /** - * 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.03390625; - for(var zoom=0; zoom<=this.MAX_ZOOM_LEVEL; ++zoom) { - this.RESOLUTIONS[zoom] = maxResolution / Math.pow(2, zoom); - } - this.units = "m"; - this.projection = 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() { - var gg = new OpenLayers.Projection("EPSG:4326"); - var sm = new OpenLayers.Projection("EPSG:900913"); - return function(lon, lat) { - var point = OpenLayers.Projection.transform({x: lon, y: lat}, gg, sm); - return new OpenLayers.LonLat(point.x, point.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() { - var gg = new OpenLayers.Projection("EPSG:4326"); - var sm = new OpenLayers.Projection("EPSG:900913"); - return function(x, y) { - var point = OpenLayers.Projection.transform({x: x, y: y}, sm, gg); - return new OpenLayers.LonLat(point.x, point.y); - }; - })() - -}; -/* ====================================================================== - OpenLayers/Layer/EventPane.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository 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, providing a DOM element which isolates - * the 3rd-party layer from mouse events. - * Only used by Google layers. - * - * Automatically instantiated by the Google constructor, and not usually instantiated directly. - * - * 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; - this.pane = 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.BROWSER_NAME == "msie") { - this.pane.style.background = - "url(" + OpenLayers.Util.getImageLocation("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); - } - 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: moveByPx - * Move the layer based on pixel vector. To be implemented by subclasses. - * - * Parameters: - * dx - {Number} The x coord of the displacement vector. - * dy - {Number} The y coord of the displacement vector. - */ - moveByPx: function(dx, dy) { - OpenLayers.Layer.prototype.moveByPx.apply(this, arguments); - - if (this.dragPanMapObject) { - this.dragPanMapObject(dx, -dy); - } else { - this.moveTo(this.map.getCachedCenter()); - } - }, - - /** - * 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 (!zoomChanged && oldCenter && 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-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository 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 = ['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; - } - - // - // At this point, we know what the minimum desired zoom level is, and - // we must calculate the total number of zoom levels. - // - // Because we allow for the setting of either the 'numZoomLevels' - // or the 'maxZoomLevel' properties... on either the layer or the - // map, we have to define some rules to see which we take into - // account first in this calculation. - // - // The following is the precedence list for these properties: - // - // (1) numZoomLevels set on layer - // (2) maxZoomLevel set on layer - // (3) numZoomLevels set on map - // (4) maxZoomLevel set on map* - // (5) none of the above* - // - // *Note that options (4) and (5) are only possible if the user - // _explicitly_ sets the 'numZoomLevels' property on the map to - // null, since it is set by default to 16. - // - - // - // Note to future: In 3.0, I think we should remove the default - // value of 16 for map.numZoomLevels. Rather, I think that value - // should be set as a default on the Layer.WMS class. If someone - // creates a 3rd party layer and does not specify any 'minZoomLevel', - // 'maxZoomLevel', or 'numZoomLevels', and has not explicitly - // specified any of those on the map object either.. then I think - // it is fair to say that s/he wants all the zoom levels available. - // - // By making map.numZoomLevels *null* by default, that will be the - // case. As it is, I don't feel comfortable changing that right now - // as it would be a glaring API change and actually would probably - // break many peoples' codes. - // - - //the number of zoom levels we'd like to have. - var desiredZoomLevels; - - //this is the maximum number of zoom levels the layer will allow, - // given the specified starting minimum zoom level. - var limitZoomLevels = this.MAX_ZOOM_LEVEL - this.minZoomLevel + 1; - - if ( ((this.options.numZoomLevels == null) && - (this.options.maxZoomLevel != null)) // (2) - || - ((this.numZoomLevels == null) && - (this.maxZoomLevel != null)) // (4) - ) { - //calculate based on specified maxZoomLevel (on layer or map) - desiredZoomLevels = this.maxZoomLevel - this.minZoomLevel + 1; - } else { - //calculate based on specified numZoomLevels (on layer or map) - // this covers cases (1) and (3) - desiredZoomLevels = this.numZoomLevels; - } - - if (desiredZoomLevels != null) { - //Now that we know what we would *like* the number of zoom levels - // to be, based on layer or map options, we have to make sure that - // it does not conflict with the actual limit, as specified by - // the constants on the layer itself (and calculated into the - // 'limitZoomLevels' variable). - this.numZoomLevels = Math.min(desiredZoomLevels, limitZoomLevels); - } else { - // case (5) -- neither 'numZoomLevels' not 'maxZoomLevel' was - // set on either the layer or the map. So we just use the - // maximum limit as calculated by the layer's constants. - this.numZoomLevels = limitZoomLevels; - } - - //now that the 'numZoomLevels' is appropriately, safely set, - // we go back and re-calculate the 'maxZoomLevel'. - 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 size = this.map.getSize(); - var tl = this.getLonLatFromViewPortPx({ - x: 0, y: 0 - }); - var br = this.getLonLatFromViewPortPx({ - x: size.w, y: size.h - }); - - if ((tl != null) && (br != null)) { - return new OpenLayers.Bounds(tl.lon, br.lat, br.lon, tl.lat); - } else { - return null; - } - }, - - /** - * 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; - if (this.map.baseLayer !== this) { - zoom = this.map.baseLayer.getZoomForResolution( - this.getResolutionForZoom(zoom) - ); - } - } - 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; - if (this.map.baseLayer !== this) { - zoom = this.getZoomForResolution( - this.map.baseLayer.getResolutionForZoom(zoom) - ); - } - } - return zoom; - }, - - CLASS_NAME: "OpenLayers.Layer.FixedZoomLevels" -}); - -/* ====================================================================== - OpenLayers/Layer/Google.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - - -/** - * @requires OpenLayers/Layer/SphericalMercator.js - * @requires OpenLayers/Layer/EventPane.js - * @requires OpenLayers/Layer/FixedZoomLevels.js - * @requires OpenLayers/Lang.js - */ - -/** - * Class: OpenLayers.Layer.Google - * - * Provides a wrapper for Google's Maps API - * Normally the Terms of Use for this API do not allow wrapping, but Google - * have provided written consent to OpenLayers for this - see email in - * http://osgeo-org.1560.n6.nabble.com/Google-Maps-API-Terms-of-Use-changes-tp4910013p4911981.html - * - * 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} 21 - */ - MAX_ZOOM_LEVEL: 21, - - /** - * 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, - 0.0000013411045074462891, - 0.00000067055225372314453 - ], - - /** - * APIProperty: type - * {GMapType} - */ - type: null, - - /** - * APIProperty: wrapDateLine - * {Boolean} Allow user to pan forever east/west. Default is true. - * Setting this to false only restricts panning if - * <sphericalMercator> is true. - */ - wrapDateLine: true, - - /** - * 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: version - * {Number} The version of the Google Maps API - */ - version: 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) { - options = options || {}; - if(!options.version) { - options.version = typeof GMap2 === "function" ? "2" : "3"; - } - var mixin = OpenLayers.Layer.Google["v" + - options.version.replace(/\./g, "_")]; - if (mixin) { - OpenLayers.Util.applyDefaults(options, mixin); - } else { - throw "Unsupported Google Maps API version: " + options.version; - } - - OpenLayers.Util.applyDefaults(options, mixin.DEFAULTS); - if (options.maxExtent) { - options.maxExtent = options.maxExtent.clone(); - } - - OpenLayers.Layer.EventPane.prototype.initialize.apply(this, - [name, options]); - OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this, - [name, options]); - - if (this.sphericalMercator) { - OpenLayers.Util.extend(this, OpenLayers.Layer.SphericalMercator); - this.initMercatorParameters(); - } - }, - - /** - * Method: clone - * Create a clone of this layer - * - * Returns: - * {<OpenLayers.Layer.Google>} An exact clone of this layer - */ - clone: function() { - /** - * This method isn't intended to be called by a subclass and it - * doesn't call the same method on the superclass. We don't call - * the super's clone because we don't want properties that are set - * on this layer after initialize (i.e. this.mapObject etc.). - */ - return new OpenLayers.Layer.Google( - this.name, this.getOptions() - ); - }, - - /** - * APIMethod: setVisibility - * Set the visibility flag for the layer and hide/show & redraw - * accordingly. Fire event unless otherwise specified - * - * Note that visibility is no longer simply whether or not the layer's - * style.display is set to "block". Now we store a 'visibility' state - * property on the layer class, this allows us to remember whether or - * not we *desire* for a layer to be visible. In the case where the - * map's resolution is out of the layer's range, this desire may be - * subverted. - * - * Parameters: - * visible - {Boolean} Display the layer (if in range) - */ - setVisibility: function(visible) { - // sharing a map container, opacity has to be set per layer - var opacity = this.opacity == null ? 1 : this.opacity; - OpenLayers.Layer.EventPane.prototype.setVisibility.apply(this, arguments); - this.setOpacity(opacity); - }, - - /** - * APIMethod: display - * Hide or show the Layer - * - * Parameters: - * visible - {Boolean} - */ - display: function(visible) { - if (!this._dragging) { - this.setGMapVisibility(visible); - } - OpenLayers.Layer.EventPane.prototype.display.apply(this, arguments); - }, - - /** - * Method: moveTo - * - * Parameters: - * bounds - {<OpenLayers.Bounds>} - * zoomChanged - {Boolean} Tells when zoom has changed, as layers have to - * do some init work in that case. - * dragging - {Boolean} - */ - moveTo: function(bounds, zoomChanged, dragging) { - this._dragging = dragging; - OpenLayers.Layer.EventPane.prototype.moveTo.apply(this, arguments); - delete this._dragging; - }, - - /** - * APIMethod: setOpacity - * Sets the opacity for the entire layer (all images) - * - * Parameters: - * opacity - {Float} - */ - setOpacity: function(opacity) { - if (opacity !== this.opacity) { - if (this.map != null) { - this.map.events.triggerEvent("changelayer", { - layer: this, - property: "opacity" - }); - } - this.opacity = opacity; - } - // Though this layer's opacity may not change, we're sharing a container - // and need to update the opacity for the entire container. - if (this.getVisibility()) { - var container = this.getMapContainer(); - OpenLayers.Util.modifyDOMElement( - container, null, null, null, null, null, null, opacity - ); - } - }, - - /** - * APIMethod: destroy - * Clean up this layer. - */ - destroy: function() { - /** - * We have to override this method because the event pane destroy - * deletes the mapObject reference before removing this layer from - * the map. - */ - if (this.map) { - this.setGMapVisibility(false); - var cache = OpenLayers.Layer.Google.cache[this.map.id]; - if (cache && cache.count <= 1) { - this.removeGMapElements(); - } - } - OpenLayers.Layer.EventPane.prototype.destroy.apply(this, arguments); - }, - - /** - * Method: removeGMapElements - * Remove all elements added to the dom. This should only be called if - * this is the last of the Google layers for the given map. - */ - removeGMapElements: function() { - var cache = OpenLayers.Layer.Google.cache[this.map.id]; - if (cache) { - // remove shared elements from dom - var container = this.mapObject && this.getMapContainer(); - if (container && container.parentNode) { - container.parentNode.removeChild(container); - } - var termsOfUse = cache.termsOfUse; - if (termsOfUse && termsOfUse.parentNode) { - termsOfUse.parentNode.removeChild(termsOfUse); - } - var poweredBy = cache.poweredBy; - if (poweredBy && poweredBy.parentNode) { - poweredBy.parentNode.removeChild(poweredBy); - } - if (this.mapObject && window.google && google.maps && - google.maps.event && google.maps.event.clearListeners) { - google.maps.event.clearListeners(this.mapObject, 'tilesloaded'); - } - } - }, - - /** - * APIMethod: removeMap - * On being removed from the map, also remove termsOfUse and poweredBy divs - * - * Parameters: - * map - {<OpenLayers.Map>} - */ - removeMap: function(map) { - // hide layer before removing - if (this.visibility && this.mapObject) { - this.setGMapVisibility(false); - } - // check to see if last Google layer in this map - var cache = OpenLayers.Layer.Google.cache[map.id]; - if (cache) { - if (cache.count <= 1) { - this.removeGMapElements(); - delete OpenLayers.Layer.Google.cache[map.id]; - } else { - // decrement the layer count - --cache.count; - } - } - // remove references to gmap elements - delete this.termsOfUse; - delete this.poweredBy; - delete this.mapObject; - delete this.dragObject; - OpenLayers.Layer.EventPane.prototype.removeMap.apply(this, arguments); - }, - - // - // 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: 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: 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(); - }, - - - /************************************ - * * - * 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; - }, - - // 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; - }, - - CLASS_NAME: "OpenLayers.Layer.Google" -}); - -/** - * Property: OpenLayers.Layer.Google.cache - * {Object} Cache for elements that should only be created once per map. - */ -OpenLayers.Layer.Google.cache = {}; - - -/** - * Constant: OpenLayers.Layer.Google.v2 - * - * Mixin providing functionality specific to the Google Maps API v2. - * - * This API has been deprecated by Google. - * Developers are encouraged to migrate to v3 of the API; support for this - * is provided by <OpenLayers.Layer.Google.v3> - */ -OpenLayers.Layer.Google.v2 = { - - /** - * Property: termsOfUse - * {DOMElement} Div for Google's copyright and terms of use link - */ - termsOfUse: null, - - /** - * Property: poweredBy - * {DOMElement} Div for Google's powered by logo and link - */ - poweredBy: null, - - /** - * 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, - - /** - * Method: loadMapObject - * Load the GMap and register appropriate event listeners. If we can't - * load GMap2, then display a warning message. - */ - loadMapObject:function() { - if (!this.type) { - this.type = G_NORMAL_MAP; - } - var mapObject, termsOfUse, poweredBy; - var cache = OpenLayers.Layer.Google.cache[this.map.id]; - if (cache) { - // there are already Google layers added to this map - mapObject = cache.mapObject; - termsOfUse = cache.termsOfUse; - poweredBy = cache.poweredBy; - // increment the layer count - ++cache.count; - } else { - // this is the first Google layer for this map - - var container = this.map.viewPortDiv; - var div = document.createElement("div"); - div.id = this.map.id + "_GMap2Container"; - div.style.position = "absolute"; - div.style.width = "100%"; - div.style.height = "100%"; - container.appendChild(div); - - // create GMap and shuffle elements - try { - mapObject = new GMap2(div); - - // move the ToS and branding stuff up to the container div - termsOfUse = div.lastChild; - container.appendChild(termsOfUse); - termsOfUse.style.zIndex = "1100"; - termsOfUse.style.right = ""; - termsOfUse.style.bottom = ""; - termsOfUse.className = "olLayerGoogleCopyright"; - - poweredBy = div.lastChild; - container.appendChild(poweredBy); - poweredBy.style.zIndex = "1100"; - poweredBy.style.right = ""; - poweredBy.style.bottom = ""; - poweredBy.className = "olLayerGooglePoweredBy gmnoprint"; - - } catch (e) { - throw(e); - } - // cache elements for use by any other google layers added to - // this same map - OpenLayers.Layer.Google.cache[this.map.id] = { - mapObject: mapObject, - termsOfUse: termsOfUse, - poweredBy: poweredBy, - count: 1 - }; - } - - this.mapObject = mapObject; - this.termsOfUse = termsOfUse; - this.poweredBy = poweredBy; - - // ensure this layer type is one of the mapObject types - if (OpenLayers.Util.indexOf(this.mapObject.getMapTypes(), - this.type) === -1) { - this.mapObject.addMapType(this.type); - } - - //since v 2.93 getDragObject is now available. - if(typeof mapObject.getDragObject == "function") { - this.dragObject = mapObject.getDragObject(); - } else { - this.dragPanMapObject = null; - } - - if(this.isBaseLayer === false) { - this.setGMapVisibility(this.div.style.display !== "none"); - } - - }, - - /** - * APIMethod: onMapResize - */ - onMapResize: function() { - // workaround for resizing of invisible or not yet fully loaded layers - // where GMap2.checkResize() does not work. We need to load the GMap - // for the old div size, then checkResize(), and then call - // layer.moveTo() to trigger GMap.setCenter() (which will finish - // the GMap initialization). - if(this.visibility && this.mapObject.isLoaded()) { - this.mapObject.checkResize(); - } else { - if(!this._resized) { - var layer = this; - var handle = GEvent.addListener(this.mapObject, "load", function() { - GEvent.removeListener(handle); - delete layer._resized; - layer.mapObject.checkResize(); - layer.moveTo(layer.map.getCenter(), layer.map.getZoom()); - }); - } - this._resized = true; - } - }, - - /** - * Method: setGMapVisibility - * Display the GMap container and associated elements. - * - * Parameters: - * visible - {Boolean} Display the GMap elements. - */ - setGMapVisibility: function(visible) { - var cache = OpenLayers.Layer.Google.cache[this.map.id]; - if (cache) { - var container = this.mapObject.getContainer(); - if (visible === true) { - this.mapObject.setMapType(this.type); - container.style.display = ""; - this.termsOfUse.style.left = ""; - this.termsOfUse.style.display = ""; - this.poweredBy.style.display = ""; - cache.displayed = this.id; - } else { - if (cache.displayed === this.id) { - delete cache.displayed; - } - if (!cache.displayed) { - container.style.display = "none"; - this.termsOfUse.style.display = "none"; - // move ToU far to the left in addition to setting display - // to "none", because at the end of the GMap2 load - // sequence, display: none will be unset and ToU would be - // visible after loading a map with a google layer that is - // initially hidden. - this.termsOfUse.style.left = "-9999px"; - this.poweredBy.style.display = "none"; - } - } - } - }, - - /** - * Method: getMapContainer - * - * Returns: - * {DOMElement} the GMap container's div - */ - getMapContainer: function() { - return this.mapObject.getContainer(); - }, - - // - // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds - // - - /** - * 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; - }, - - - /************************************ - * * - * 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)); - }, - - - // 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: 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: getMapObjectPixelFromXY - * - * Parameters: - * x - {Integer} - * y - {Integer} - * - * Returns: - * {Object} MapObject Pixel from x and y parameters - */ - getMapObjectPixelFromXY: function(x, y) { - return new GPoint(x, y); - } - -}; -/* ====================================================================== - OpenLayers/Geometry.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @requires OpenLayers/BaseTypes/Class.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. - * - * Note that if you use the <OpenLayers.Geometry.fromWKT> method, you must - * explicitly include the OpenLayers.Format.WKT in your build. - */ -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(); - }, - - /** - * Method: setBounds - * Set the bounds for this Geometry. - * - * Parameters: - * bounds - {<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. - // - }, - - /** - * APIMethod: distanceTo - * Calculate the closest distance between two geometries (on the x-y plane). - * - * Parameters: - * geometry - {<OpenLayers.Geometry>} The target geometry. - * options - {Object} Optional properties for configuring the distance - * calculation. - * - * Valid options depend on the specific geometry type. - * - * Returns: - * {Number | Object} The distance between this geometry and the target. - * If details is true, the return will be an object with distance, - * x0, y0, x1, and x2 properties. The x0 and y0 properties represent - * the coordinates of the closest point on this geometry. The x1 and y1 - * properties represent the coordinates of the closest point on the - * target geometry. - */ - distanceTo: function(geometry, options) { - }, - - /** - * APIMethod: getVertices - * Return a list of all points in this geometry. - * - * Parameters: - * nodes - {Boolean} For lines, only return vertices that are - * endpoints. If false, for lines, only vertices that are not - * endpoints will be returned. If not provided, all vertices will - * be returned. - * - * Returns: - * {Array} A list of all vertices in the geometry. - */ - getVertices: function(nodes) { - }, - - /** - * Method: atPoint - * Note - This is only an approximation based on the bounds of the - * geometry. - * - * Parameters: - * lonlat - {<OpenLayers.LonLat>|Object} OpenLayers.LonLat or an - * object with a 'lon' and 'lat' properties. - * 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; - }, - - /** - * APIMethod: getCentroid - * Calculate the centroid of this geometry. This method is defined in subclasses. - * - * Returns: - * {<OpenLayers.Geometry.Point>} The centroid of the collection - */ - getCentroid: function() { - return null; - }, - - /** - * Method: toString - * Returns a text representation of the geometry. If the WKT format is - * included in a build, this will be the Well-Known Text - * representation. - * - * Returns: - * {String} String representation of this geometry. - */ - toString: function() { - var string; - if (OpenLayers.Format && OpenLayers.Format.WKT) { - string = OpenLayers.Format.WKT.prototype.write( - new OpenLayers.Feature.Vector(this) - ); - } else { - string = Object.prototype.toString.call(this); - } - return string; - }, - - CLASS_NAME: "OpenLayers.Geometry" -}); - -/** - * Function: OpenLayers.Geometry.fromWKT - * Generate a geometry given a Well-Known Text string. For this method to - * work, you must include the OpenLayers.Format.WKT in your build - * explicitly. - * - * Parameters: - * wkt - {String} A string representing the geometry in Well-Known Text. - * - * Returns: - * {<OpenLayers.Geometry>} A geometry of the appropriate class. - */ -OpenLayers.Geometry.fromWKT = function(wkt) { - var geom; - if (OpenLayers.Format && OpenLayers.Format.WKT) { - var format = OpenLayers.Geometry.fromWKT.format; - if (!format) { - format = new OpenLayers.Format.WKT(); - OpenLayers.Geometry.fromWKT.format = format; - } - var result = format.read(wkt); - if (result instanceof OpenLayers.Feature.Vector) { - geom = result.geometry; - } else if (OpenLayers.Util.isArray(result)) { - var len = result.length; - var components = new Array(len); - for (var i=0; i<len; ++i) { - components[i] = result[i].geometry; - } - geom = new OpenLayers.Geometry.Collection(components); - } - } - return geom; -}; - -/** - * 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. - * options - {Object} Optional properties for calculating the intersection. - * - * Valid options: - * 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. - * tolerance - {Number} If a non-null value is provided, if the segments are - * within the tolerance distance, this will be considered an intersection. - * In addition, if the point option is true and the calculated intersection - * is within the tolerance distance of an end point, the endpoint will be - * returned instead of the calculated intersection. Further, if the - * intersection is within the tolerance of endpoints on both segments, or - * if two segment endpoints are within the tolerance distance of eachother - * (but no intersection is otherwise calculated), an endpoint on the - * first segment provided 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, options) { - var point = options && options.point; - var tolerance = options && options.tolerance; - 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); - } - } - } - if(tolerance) { - var dist; - if(intersection) { - if(point) { - var segs = [seg1, seg2]; - var seg, x, y; - // check segment endpoints for proximity to intersection - // set intersection to first endpoint within the tolerance - outer: for(var i=0; i<2; ++i) { - seg = segs[i]; - for(var j=1; j<3; ++j) { - x = seg["x" + j]; - y = seg["y" + j]; - dist = Math.sqrt( - Math.pow(x - intersection.x, 2) + - Math.pow(y - intersection.y, 2) - ); - if(dist < tolerance) { - intersection.x = x; - intersection.y = y; - break outer; - } - } - } - - } - } else { - // no calculated intersection, but segments could be within - // the tolerance of one another - var segs = [seg1, seg2]; - var source, target, x, y, p, result; - // check segment endpoints for proximity to intersection - // set intersection to first endpoint within the tolerance - outer: for(var i=0; i<2; ++i) { - source = segs[i]; - target = segs[(i+1)%2]; - for(var j=1; j<3; ++j) { - p = {x: source["x"+j], y: source["y"+j]}; - result = OpenLayers.Geometry.distanceToSegment(p, target); - if(result.distance < tolerance) { - if(point) { - intersection = new OpenLayers.Geometry.Point(p.x, p.y); - } else { - intersection = true; - } - break outer; - } - } - } - } - } - return intersection; -}; - -/** - * Function: OpenLayers.Geometry.distanceToSegment - * - * Parameters: - * point - {Object} An object with x and y properties representing the - * point coordinates. - * segment - {Object} An object with x1, y1, x2, and y2 properties - * representing endpoint coordinates. - * - * Returns: - * {Object} An object with distance, along, x, and y properties. The distance - * will be the shortest distance between the input point and segment. - * The x and y properties represent the coordinates along the segment - * where the shortest distance meets the segment. The along attribute - * describes how far between the two segment points the given point is. - */ -OpenLayers.Geometry.distanceToSegment = function(point, segment) { - var result = OpenLayers.Geometry.distanceSquaredToSegment(point, segment); - result.distance = Math.sqrt(result.distance); - return result; -}; - -/** - * Function: OpenLayers.Geometry.distanceSquaredToSegment - * - * Usually the distanceToSegment function should be used. This variant however - * can be used for comparisons where the exact distance is not important. - * - * Parameters: - * point - {Object} An object with x and y properties representing the - * point coordinates. - * segment - {Object} An object with x1, y1, x2, and y2 properties - * representing endpoint coordinates. - * - * Returns: - * {Object} An object with squared distance, along, x, and y properties. - * The distance will be the shortest distance between the input point and - * segment. The x and y properties represent the coordinates along the - * segment where the shortest distance meets the segment. The along - * attribute describes how far between the two segment points the given - * point is. - */ -OpenLayers.Geometry.distanceSquaredToSegment = function(point, segment) { - var x0 = point.x; - var y0 = point.y; - var x1 = segment.x1; - var y1 = segment.y1; - var x2 = segment.x2; - var y2 = segment.y2; - var dx = x2 - x1; - var dy = y2 - y1; - var along = ((dx * (x0 - x1)) + (dy * (y0 - y1))) / - (Math.pow(dx, 2) + Math.pow(dy, 2)); - var x, y; - if(along <= 0.0) { - x = x1; - y = y1; - } else if(along >= 1.0) { - x = x2; - y = y2; - } else { - x = x1 + along * dx; - y = y1 + along * dy; - } - return { - distance: Math.pow(x - x0, 2) + Math.pow(y - y0, 2), - x: x, y: y, - along: along - }; -}; -/* ====================================================================== - OpenLayers/Geometry/Collection.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository 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. - * - * Inherits 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; - OpenLayers.Geometry.prototype.destroy.apply(this, arguments); - }, - - /** - * 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; - var bounds = new OpenLayers.Bounds(); - var components = this.components; - if (components) { - for (var i=0, len=components.length; i<len; i++) { - bounds.extend(components[i].getBounds()); - } - } - // to preserve old behavior, we only set bounds if non-null - // in the future, we could add bounds.isEmpty() - if (bounds.left != null && bounds.bottom != null && - bounds.right != null && bounds.top != null) { - this.setBounds(bounds); - } - }, - - /** - * APIMethod: addComponents - * Add components to this geometry. - * - * Parameters: - * components - {Array(<OpenLayers.Geometry>)} An array of geometries to add - */ - addComponents: function(components){ - if(!(OpenLayers.Util.isArray(components))) { - 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 - * - * Returns: - * {Boolean} A component was removed. - */ - removeComponents: function(components) { - var removed = false; - - if(!(OpenLayers.Util.isArray(components))) { - components = [components]; - } - for(var i=components.length-1; i>=0; --i) { - removed = this.removeComponent(components[i]) || removed; - } - return removed; - }, - - /** - * Method: removeComponent - * Remove a component from this geometry. - * - * Parameters: - * component - {<OpenLayers.Geometry>} - * - * Returns: - * {Boolean} The component was removed. - */ - removeComponent: function(component) { - - OpenLayers.Util.removeItem(this.components, component); - - // clearBounds() so that it gets recalculated on the next call - // to this.getBounds(); - this.clearBounds(); - return true; - }, - - /** - * 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: getGeodesicArea - * Calculate the approximate area of the polygon were it projected onto - * the earth. - * - * Parameters: - * projection - {<OpenLayers.Projection>} The spatial reference system - * for the geometry coordinates. If not provided, Geographic/WGS84 is - * assumed. - * - * Reference: - * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for - * Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion - * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409 - * - * Returns: - * {float} The approximate geodesic area of the geometry in square meters. - */ - getGeodesicArea: function(projection) { - var area = 0.0; - for(var i=0, len=this.components.length; i<len; i++) { - area += this.components[i].getGeodesicArea(projection); - } - return area; - }, - - /** - * APIMethod: getCentroid - * - * Compute the centroid for this geometry collection. - * - * Parameters: - * weighted - {Boolean} Perform the getCentroid computation recursively, - * returning an area weighted average of all geometries in this collection. - * - * Returns: - * {<OpenLayers.Geometry.Point>} The centroid of the collection - */ - getCentroid: function(weighted) { - if (!weighted) { - return this.components.length && this.components[0].getCentroid(); - } - var len = this.components.length; - if (!len) { - return false; - } - - var areas = []; - var centroids = []; - var areaSum = 0; - var minArea = Number.MAX_VALUE; - var component; - for (var i=0; i<len; ++i) { - component = this.components[i]; - var area = component.getArea(); - var centroid = component.getCentroid(true); - if (isNaN(area) || isNaN(centroid.x) || isNaN(centroid.y)) { - continue; - } - areas.push(area); - areaSum += area; - minArea = (area < minArea && area > 0) ? area : minArea; - centroids.push(centroid); - } - len = areas.length; - if (areaSum === 0) { - // all the components in this collection have 0 area - // probably a collection of points -- weight all the points the same - for (var i=0; i<len; ++i) { - areas[i] = 1; - } - areaSum = areas.length; - } else { - // normalize all the areas where the smallest area will get - // a value of 1 - for (var i=0; i<len; ++i) { - areas[i] /= minArea; - } - areaSum /= minArea; - } - - var xSum = 0, ySum = 0, centroid, area; - for (var i=0; i<len; ++i) { - centroid = centroids[i]; - area = areas[i]; - xSum += centroid.x * area; - ySum += centroid.y * area; - } - - return new OpenLayers.Geometry.Point(xSum/areaSum, ySum/areaSum); - }, - - /** - * APIMethod: getGeodesicLength - * Calculate the approximate length of the geometry were it projected onto - * the earth. - * - * projection - {<OpenLayers.Projection>} The spatial reference system - * for the geometry coordinates. If not provided, Geographic/WGS84 is - * assumed. - * - * Returns: - * {Float} The appoximate geodesic length of the geometry in meters. - */ - getGeodesicLength: function(projection) { - var length = 0.0; - for(var i=0, len=this.components.length; i<len; i++) { - length += this.components[i].getGeodesicLength(projection); - } - return length; - }, - - /** - * APIMethod: move - * Moves a geometry by the given displacement along positive x and y axes. - * This modifies the position of the geometry and clears the cached - * bounds. - * - * Parameters: - * x - {Float} Distance to move geometry in positive x direction. - * y - {Float} Distance to move geometry in positive y direction. - */ - 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. - * - * Returns: - * {<OpenLayers.Geometry>} - The current geometry. - */ - resize: function(scale, origin, ratio) { - for(var i=0; i<this.components.length; ++i) { - this.components[i].resize(scale, origin, ratio); - } - return this; - }, - - /** - * APIMethod: distanceTo - * Calculate the closest distance between two geometries (on the x-y plane). - * - * Parameters: - * geometry - {<OpenLayers.Geometry>} The target geometry. - * options - {Object} Optional properties for configuring the distance - * calculation. - * - * Valid options: - * details - {Boolean} Return details from the distance calculation. - * Default is false. - * edge - {Boolean} Calculate the distance from this geometry to the - * nearest edge of the target geometry. Default is true. If true, - * calling distanceTo from a geometry that is wholly contained within - * the target will result in a non-zero distance. If false, whenever - * geometries intersect, calling distanceTo will return 0. If false, - * details cannot be returned. - * - * Returns: - * {Number | Object} The distance between this geometry and the target. - * If details is true, the return will be an object with distance, - * x0, y0, x1, and y1 properties. The x0 and y0 properties represent - * the coordinates of the closest point on this geometry. The x1 and y1 - * properties represent the coordinates of the closest point on the - * target geometry. - */ - distanceTo: function(geometry, options) { - var edge = !(options && options.edge === false); - var details = edge && options && options.details; - var result, best, distance; - var min = Number.POSITIVE_INFINITY; - for(var i=0, len=this.components.length; i<len; ++i) { - result = this.components[i].distanceTo(geometry, options); - distance = details ? result.distance : result; - if(distance < min) { - min = distance; - best = result; - if(min == 0) { - break; - } - } - } - return best; - }, - - /** - * APIMethod: equals - * Determine whether another geometry is equivalent to this one. Geometries - * are considered equivalent if all components have the same coordinates. - * - * Parameters: - * geometry - {<OpenLayers.Geometry>} The geometry to test. - * - * Returns: - * {Boolean} The supplied geometry is equivalent to this geometry. - */ - equals: function(geometry) { - var equivalent = true; - if(!geometry || !geometry.CLASS_NAME || - (this.CLASS_NAME != geometry.CLASS_NAME)) { - equivalent = false; - } else if(!(OpenLayers.Util.isArray(geometry.components)) || - (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; - }, - - /** - * APIMethod: getVertices - * Return a list of all points in this geometry. - * - * Parameters: - * nodes - {Boolean} For lines, only return vertices that are - * endpoints. If false, for lines, only vertices that are not - * endpoints will be returned. If not provided, all vertices will - * be returned. - * - * Returns: - * {Array} A list of all vertices in the geometry. - */ - getVertices: function(nodes) { - var vertices = []; - for(var i=0, len=this.components.length; i<len; ++i) { - Array.prototype.push.apply( - vertices, this.components[i].getVertices(nodes) - ); - } - return vertices; - }, - - - CLASS_NAME: "OpenLayers.Geometry.Collection" -}); -/* ====================================================================== - OpenLayers/Geometry/Point.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository 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 - * Calculate the closest distance between two geometries (on the x-y plane). - * - * Parameters: - * geometry - {<OpenLayers.Geometry>} The target geometry. - * options - {Object} Optional properties for configuring the distance - * calculation. - * - * Valid options: - * details - {Boolean} Return details from the distance calculation. - * Default is false. - * edge - {Boolean} Calculate the distance from this geometry to the - * nearest edge of the target geometry. Default is true. If true, - * calling distanceTo from a geometry that is wholly contained within - * the target will result in a non-zero distance. If false, whenever - * geometries intersect, calling distanceTo will return 0. If false, - * details cannot be returned. - * - * Returns: - * {Number | Object} The distance between this geometry and the target. - * If details is true, the return will be an object with distance, - * x0, y0, x1, and x2 properties. The x0 and y0 properties represent - * the coordinates of the closest point on this geometry. The x1 and y1 - * properties represent the coordinates of the closest point on the - * target geometry. - */ - distanceTo: function(geometry, options) { - var edge = !(options && options.edge === false); - var details = edge && options && options.details; - var distance, x0, y0, x1, y1, result; - if(geometry instanceof OpenLayers.Geometry.Point) { - x0 = this.x; - y0 = this.y; - x1 = geometry.x; - y1 = geometry.y; - distance = Math.sqrt(Math.pow(x0 - x1, 2) + Math.pow(y0 - y1, 2)); - result = !details ? - distance : {x0: x0, y0: y0, x1: x1, y1: y1, distance: distance}; - } else { - result = geometry.distanceTo(this, options); - if(details) { - // switch coord order since this geom is target - result = { - x0: result.x1, y0: result.y1, - x1: result.x0, y1: result.y0, - distance: result.distance - }; - } - } - return result; - }, - - /** - * APIMethod: equals - * Determine whether another geometry is equivalent to this one. Geometries - * are considered equivalent if all components have the same coordinates. - * - * Parameters: - * geom - {<OpenLayers.Geometry.Point>} The geometry to test. - * - * Returns: - * {Boolean} The supplied geometry is equivalent to this geometry. - */ - 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 geometry by the given displacement along positive x and y axes. - * This modifies the position of the geometry and clears the cached - * bounds. - * - * Parameters: - * x - {Float} Distance to move geometry in positive x direction. - * y - {Float} Distance to move geometry in positive y direction. - */ - 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: getCentroid - * - * Returns: - * {<OpenLayers.Geometry.Point>} The centroid of the collection - */ - getCentroid: function() { - return new OpenLayers.Geometry.Point(this.x, this.y); - }, - - /** - * 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. - * - * Returns: - * {<OpenLayers.Geometry>} - The current geometry. - */ - 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(); - 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; - 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; - }, - - /** - * APIMethod: getVertices - * Return a list of all points in this geometry. - * - * Parameters: - * nodes - {Boolean} For lines, only return vertices that are - * endpoints. If false, for lines, only vertices that are not - * endpoints will be returned. If not provided, all vertices will - * be returned. - * - * Returns: - * {Array} A list of all vertices in the geometry. - */ - getVertices: function(nodes) { - return [this]; - }, - - CLASS_NAME: "OpenLayers.Geometry.Point" -}); -/* ====================================================================== - OpenLayers/Geometry/MultiPoint.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @requires OpenLayers/Geometry/Collection.js - * @requires OpenLayers/Geometry/Point.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>} - */ - - /** - * 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/Curve.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository 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>)} - */ - - /** - * 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; - }, - - /** - * APIMethod: getGeodesicLength - * Calculate the approximate length of the geometry were it projected onto - * the earth. - * - * projection - {<OpenLayers.Projection>} The spatial reference system - * for the geometry coordinates. If not provided, Geographic/WGS84 is - * assumed. - * - * Returns: - * {Float} The appoximate geodesic length of the geometry in meters. - */ - getGeodesicLength: function(projection) { - var geom = this; // so we can work with a clone if needed - if(projection) { - var gg = new OpenLayers.Projection("EPSG:4326"); - if(!gg.equals(projection)) { - geom = this.clone().transform(projection, gg); - } - } - var length = 0.0; - if(geom.components && (geom.components.length > 1)) { - var p1, p2; - for(var i=1, len=geom.components.length; i<len; i++) { - p1 = geom.components[i-1]; - p2 = geom.components[i]; - // this returns km and requires lon/lat properties - length += OpenLayers.Util.distVincenty( - {lon: p1.x, lat: p1.y}, {lon: p2.x, lat: p2.y} - ); - } - } - // convert to m - return length * 1000; - }, - - CLASS_NAME: "OpenLayers.Geometry.Curve" -}); -/* ====================================================================== - OpenLayers/Geometry/LineString.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository 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 - * - */ - - /** - * 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 - * - * Returns: - * {Boolean} The component was removed. - */ - removeComponent: function(point) { - var removed = this.components && (this.components.length > 2); - if (removed) { - OpenLayers.Geometry.Collection.prototype.removeComponent.apply(this, - arguments); - } - return removed; - }, - - /** - * 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), point1, point2; - 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); - }, - - /** - * Method: splitWithSegment - * Split this geometry with the given segment. - * - * Parameters: - * seg - {Object} An object with x1, y1, x2, and y2 properties referencing - * segment endpoint coordinates. - * options - {Object} Properties of this object will be used to determine - * how the split is conducted. - * - * Valid options: - * edge - {Boolean} Allow splitting when only edges intersect. Default is - * true. If false, a vertex on the source segment must be within the - * tolerance distance of the intersection to be considered a split. - * tolerance - {Number} If a non-null value is provided, intersections - * within the tolerance distance of one of the source segment's - * endpoints will be assumed to occur at the endpoint. - * - * Returns: - * {Object} An object with *lines* and *points* properties. If the given - * segment intersects this linestring, the lines array will reference - * geometries that result from the split. The points array will contain - * all intersection points. Intersection points are sorted along the - * segment (in order from x1,y1 to x2,y2). - */ - splitWithSegment: function(seg, options) { - var edge = !(options && options.edge === false); - var tolerance = options && options.tolerance; - var lines = []; - var verts = this.getVertices(); - var points = []; - var intersections = []; - var split = false; - var vert1, vert2, point; - var node, vertex, target; - var interOptions = {point: true, tolerance: tolerance}; - var result = null; - for(var i=0, stop=verts.length-2; i<=stop; ++i) { - vert1 = verts[i]; - points.push(vert1.clone()); - vert2 = verts[i+1]; - target = {x1: vert1.x, y1: vert1.y, x2: vert2.x, y2: vert2.y}; - point = OpenLayers.Geometry.segmentsIntersect( - seg, target, interOptions - ); - if(point instanceof OpenLayers.Geometry.Point) { - if((point.x === seg.x1 && point.y === seg.y1) || - (point.x === seg.x2 && point.y === seg.y2) || - point.equals(vert1) || point.equals(vert2)) { - vertex = true; - } else { - vertex = false; - } - if(vertex || edge) { - // push intersections different than the previous - if(!point.equals(intersections[intersections.length-1])) { - intersections.push(point.clone()); - } - if(i === 0) { - if(point.equals(vert1)) { - continue; - } - } - if(point.equals(vert2)) { - continue; - } - split = true; - if(!point.equals(vert1)) { - points.push(point); - } - lines.push(new OpenLayers.Geometry.LineString(points)); - points = [point.clone()]; - } - } - } - if(split) { - points.push(vert2.clone()); - lines.push(new OpenLayers.Geometry.LineString(points)); - } - if(intersections.length > 0) { - // sort intersections along segment - var xDir = seg.x1 < seg.x2 ? 1 : -1; - var yDir = seg.y1 < seg.y2 ? 1 : -1; - result = { - lines: lines, - points: intersections.sort(function(p1, p2) { - return (xDir * p1.x - xDir * p2.x) || (yDir * p1.y - yDir * p2.y); - }) - }; - } - return result; - }, - - /** - * Method: split - * Use this geometry (the source) to attempt to split a target geometry. - * - * Parameters: - * target - {<OpenLayers.Geometry>} The target geometry. - * options - {Object} Properties of this object will be used to determine - * how the split is conducted. - * - * Valid options: - * mutual - {Boolean} Split the source geometry in addition to the target - * geometry. Default is false. - * edge - {Boolean} Allow splitting when only edges intersect. Default is - * true. If false, a vertex on the source must be within the tolerance - * distance of the intersection to be considered a split. - * tolerance - {Number} If a non-null value is provided, intersections - * within the tolerance distance of an existing vertex on the source - * will be assumed to occur at the vertex. - * - * Returns: - * {Array} A list of geometries (of this same type as the target) that - * result from splitting the target with the source geometry. The - * source and target geometry will remain unmodified. If no split - * results, null will be returned. If mutual is true and a split - * results, return will be an array of two arrays - the first will be - * all geometries that result from splitting the source geometry and - * the second will be all geometries that result from splitting the - * target geometry. - */ - split: function(target, options) { - var results = null; - var mutual = options && options.mutual; - var sourceSplit, targetSplit, sourceParts, targetParts; - if(target instanceof OpenLayers.Geometry.LineString) { - var verts = this.getVertices(); - var vert1, vert2, seg, splits, lines, point; - var points = []; - sourceParts = []; - for(var i=0, stop=verts.length-2; i<=stop; ++i) { - vert1 = verts[i]; - vert2 = verts[i+1]; - seg = { - x1: vert1.x, y1: vert1.y, - x2: vert2.x, y2: vert2.y - }; - targetParts = targetParts || [target]; - if(mutual) { - points.push(vert1.clone()); - } - for(var j=0; j<targetParts.length; ++j) { - splits = targetParts[j].splitWithSegment(seg, options); - if(splits) { - // splice in new features - lines = splits.lines; - if(lines.length > 0) { - lines.unshift(j, 1); - Array.prototype.splice.apply(targetParts, lines); - j += lines.length - 2; - } - if(mutual) { - for(var k=0, len=splits.points.length; k<len; ++k) { - point = splits.points[k]; - if(!point.equals(vert1)) { - points.push(point); - sourceParts.push(new OpenLayers.Geometry.LineString(points)); - if(point.equals(vert2)) { - points = []; - } else { - points = [point.clone()]; - } - } - } - } - } - } - } - if(mutual && sourceParts.length > 0 && points.length > 0) { - points.push(vert2.clone()); - sourceParts.push(new OpenLayers.Geometry.LineString(points)); - } - } else { - results = target.splitWith(this, options); - } - if(targetParts && targetParts.length > 1) { - targetSplit = true; - } else { - targetParts = []; - } - if(sourceParts && sourceParts.length > 1) { - sourceSplit = true; - } else { - sourceParts = []; - } - if(targetSplit || sourceSplit) { - if(mutual) { - results = [sourceParts, targetParts]; - } else { - results = targetParts; - } - } - return results; - }, - - /** - * Method: splitWith - * Split this geometry (the target) with the given geometry (the source). - * - * Parameters: - * geometry - {<OpenLayers.Geometry>} A geometry used to split this - * geometry (the source). - * options - {Object} Properties of this object will be used to determine - * how the split is conducted. - * - * Valid options: - * mutual - {Boolean} Split the source geometry in addition to the target - * geometry. Default is false. - * edge - {Boolean} Allow splitting when only edges intersect. Default is - * true. If false, a vertex on the source must be within the tolerance - * distance of the intersection to be considered a split. - * tolerance - {Number} If a non-null value is provided, intersections - * within the tolerance distance of an existing vertex on the source - * will be assumed to occur at the vertex. - * - * Returns: - * {Array} A list of geometries (of this same type as the target) that - * result from splitting the target with the source geometry. The - * source and target geometry will remain unmodified. If no split - * results, null will be returned. If mutual is true and a split - * results, return will be an array of two arrays - the first will be - * all geometries that result from splitting the source geometry and - * the second will be all geometries that result from splitting the - * target geometry. - */ - splitWith: function(geometry, options) { - return geometry.split(this, options); - - }, - - /** - * APIMethod: getVertices - * Return a list of all points in this geometry. - * - * Parameters: - * nodes - {Boolean} For lines, only return vertices that are - * endpoints. If false, for lines, only vertices that are not - * endpoints will be returned. If not provided, all vertices will - * be returned. - * - * Returns: - * {Array} A list of all vertices in the geometry. - */ - getVertices: function(nodes) { - var vertices; - if(nodes === true) { - vertices = [ - this.components[0], - this.components[this.components.length-1] - ]; - } else if (nodes === false) { - vertices = this.components.slice(1, this.components.length-1); - } else { - vertices = this.components.slice(); - } - return vertices; - }, - - /** - * APIMethod: distanceTo - * Calculate the closest distance between two geometries (on the x-y plane). - * - * Parameters: - * geometry - {<OpenLayers.Geometry>} The target geometry. - * options - {Object} Optional properties for configuring the distance - * calculation. - * - * Valid options: - * details - {Boolean} Return details from the distance calculation. - * Default is false. - * edge - {Boolean} Calculate the distance from this geometry to the - * nearest edge of the target geometry. Default is true. If true, - * calling distanceTo from a geometry that is wholly contained within - * the target will result in a non-zero distance. If false, whenever - * geometries intersect, calling distanceTo will return 0. If false, - * details cannot be returned. - * - * Returns: - * {Number | Object} The distance between this geometry and the target. - * If details is true, the return will be an object with distance, - * x0, y0, x1, and x2 properties. The x0 and y0 properties represent - * the coordinates of the closest point on this geometry. The x1 and y1 - * properties represent the coordinates of the closest point on the - * target geometry. - */ - distanceTo: function(geometry, options) { - var edge = !(options && options.edge === false); - var details = edge && options && options.details; - var result, best = {}; - var min = Number.POSITIVE_INFINITY; - if(geometry instanceof OpenLayers.Geometry.Point) { - var segs = this.getSortedSegments(); - var x = geometry.x; - var y = geometry.y; - var seg; - for(var i=0, len=segs.length; i<len; ++i) { - seg = segs[i]; - result = OpenLayers.Geometry.distanceToSegment(geometry, seg); - if(result.distance < min) { - min = result.distance; - best = result; - if(min === 0) { - break; - } - } else { - // if distance increases and we cross y0 to the right of x0, no need to keep looking. - if(seg.x2 > x && ((y > seg.y1 && y < seg.y2) || (y < seg.y1 && y > seg.y2))) { - break; - } - } - } - if(details) { - best = { - distance: best.distance, - x0: best.x, y0: best.y, - x1: x, y1: y - }; - } else { - best = best.distance; - } - } else if(geometry instanceof OpenLayers.Geometry.LineString) { - var segs0 = this.getSortedSegments(); - var segs1 = geometry.getSortedSegments(); - var seg0, seg1, intersection, x0, y0; - var len1 = segs1.length; - var interOptions = {point: true}; - outer: for(var i=0, len=segs0.length; i<len; ++i) { - seg0 = segs0[i]; - x0 = seg0.x1; - y0 = seg0.y1; - for(var j=0; j<len1; ++j) { - seg1 = segs1[j]; - intersection = OpenLayers.Geometry.segmentsIntersect(seg0, seg1, interOptions); - if(intersection) { - min = 0; - best = { - distance: 0, - x0: intersection.x, y0: intersection.y, - x1: intersection.x, y1: intersection.y - }; - break outer; - } else { - result = OpenLayers.Geometry.distanceToSegment({x: x0, y: y0}, seg1); - if(result.distance < min) { - min = result.distance; - best = { - distance: min, - x0: x0, y0: y0, - x1: result.x, y1: result.y - }; - } - } - } - } - if(!details) { - best = best.distance; - } - if(min !== 0) { - // check the final vertex in this line's sorted segments - if(seg0) { - result = geometry.distanceTo( - new OpenLayers.Geometry.Point(seg0.x2, seg0.y2), - options - ); - var dist = details ? result.distance : result; - if(dist < min) { - if(details) { - best = { - distance: min, - x0: result.x1, y0: result.y1, - x1: result.x0, y1: result.y0 - }; - } else { - best = dist; - } - } - } - } - } else { - best = geometry.distanceTo(this, options); - // swap since target comes from this line - if(details) { - best = { - distance: best.distance, - x0: best.x1, y0: best.y1, - x1: best.x0, y1: best.y0 - }; - } - } - return best; - }, - - /** - * APIMethod: simplify - * This function will return a simplified LineString. - * Simplification is based on the Douglas-Peucker algorithm. - * - * - * Parameters: - * tolerance - {number} threshhold for simplification in map units - * - * Returns: - * {OpenLayers.Geometry.LineString} the simplified LineString - */ - simplify: function(tolerance){ - if (this && this !== null) { - var points = this.getVertices(); - if (points.length < 3) { - return this; - } - - var compareNumbers = function(a, b){ - return (a-b); - }; - - /** - * Private function doing the Douglas-Peucker reduction - */ - var douglasPeuckerReduction = function(points, firstPoint, lastPoint, tolerance){ - var maxDistance = 0; - var indexFarthest = 0; - - for (var index = firstPoint, distance; index < lastPoint; index++) { - distance = perpendicularDistance(points[firstPoint], points[lastPoint], points[index]); - if (distance > maxDistance) { - maxDistance = distance; - indexFarthest = index; - } - } - - if (maxDistance > tolerance && indexFarthest != firstPoint) { - //Add the largest point that exceeds the tolerance - pointIndexsToKeep.push(indexFarthest); - douglasPeuckerReduction(points, firstPoint, indexFarthest, tolerance); - douglasPeuckerReduction(points, indexFarthest, lastPoint, tolerance); - } - }; - - /** - * Private function calculating the perpendicular distance - * TODO: check whether OpenLayers.Geometry.LineString::distanceTo() is faster or slower - */ - var perpendicularDistance = function(point1, point2, point){ - //Area = |(1/2)(x1y2 + x2y3 + x3y1 - x2y1 - x3y2 - x1y3)| *Area of triangle - //Base = v((x1-x2)²+(x1-x2)²) *Base of Triangle* - //Area = .5*Base*H *Solve for height - //Height = Area/.5/Base - - var area = Math.abs(0.5 * (point1.x * point2.y + point2.x * point.y + point.x * point1.y - point2.x * point1.y - point.x * point2.y - point1.x * point.y)); - var bottom = Math.sqrt(Math.pow(point1.x - point2.x, 2) + Math.pow(point1.y - point2.y, 2)); - var height = area / bottom * 2; - - return height; - }; - - var firstPoint = 0; - var lastPoint = points.length - 1; - var pointIndexsToKeep = []; - - //Add the first and last index to the keepers - pointIndexsToKeep.push(firstPoint); - pointIndexsToKeep.push(lastPoint); - - //The first and the last point cannot be the same - while (points[firstPoint].equals(points[lastPoint])) { - lastPoint--; - //Addition: the first point not equal to first point in the LineString is kept as well - pointIndexsToKeep.push(lastPoint); - } - - douglasPeuckerReduction(points, firstPoint, lastPoint, tolerance); - var returnPoints = []; - pointIndexsToKeep.sort(compareNumbers); - for (var index = 0; index < pointIndexsToKeep.length; index++) { - returnPoints.push(points[pointIndexsToKeep[index]]); - } - return new OpenLayers.Geometry.LineString(returnPoints); - - } - else { - return this; - } - }, - - CLASS_NAME: "OpenLayers.Geometry.LineString" -}); -/* ====================================================================== - OpenLayers/Geometry/LinearRing.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository 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 - */ - - /** - * 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. - * - * Parameters: - * 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>} - * - * Returns: - * {Boolean} The component was removed. - */ - removeComponent: function(point) { - var removed = this.components && (this.components.length > 3); - if (removed) { - //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]); - } - return removed; - }, - - /** - * APIMethod: move - * Moves a geometry by the given displacement along positive x and y axes. - * This modifies the position of the geometry and clears the cached - * bounds. - * - * Parameters: - * x - {Float} Distance to move geometry in positive x direction. - * y - {Float} Distance to move geometry in positive y direction. - */ - 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. - * - * Returns: - * {<OpenLayers.Geometry>} - The current geometry. - */ - resize: function(scale, origin, ratio) { - for(var i=0, len=this.components.length; i<len - 1; ++i) { - this.components[i].resize(scale, origin, ratio); - } - return this; - }, - - /** - * 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: getCentroid - * - * Returns: - * {<OpenLayers.Geometry.Point>} The centroid of the collection - */ - getCentroid: function() { - if (this.components) { - var len = this.components.length; - if (len > 0 && len <= 2) { - return this.components[0].clone(); - } else if (len > 2) { - var sumX = 0.0; - var sumY = 0.0; - var x0 = this.components[0].x; - var y0 = this.components[0].y; - var area = -1 * this.getArea(); - if (area != 0) { - for (var i = 0; i < len - 1; i++) { - var b = this.components[i]; - var c = this.components[i+1]; - sumX += (b.x + c.x - 2 * x0) * ((b.x - x0) * (c.y - y0) - (c.x - x0) * (b.y - y0)); - sumY += (b.y + c.y - 2 * y0) * ((b.x - x0) * (c.y - y0) - (c.x - x0) * (b.y - y0)); - } - var x = x0 + sumX / (6 * area); - var y = y0 + sumY / (6 * area); - } else { - for (var i = 0; i < len - 1; i++) { - sumX += this.components[i].x; - sumY += this.components[i].y; - } - var x = sumX / (len - 1); - var y = sumY / (len - 1); - } - return new OpenLayers.Geometry.Point(x, y); - } else { - return null; - } - } - }, - - /** - * 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; - }, - - /** - * APIMethod: getGeodesicArea - * Calculate the approximate area of the polygon were it projected onto - * the earth. Note that this area will be positive if ring is oriented - * clockwise, otherwise it will be negative. - * - * Parameters: - * projection - {<OpenLayers.Projection>} The spatial reference system - * for the geometry coordinates. If not provided, Geographic/WGS84 is - * assumed. - * - * Reference: - * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for - * Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion - * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409 - * - * Returns: - * {float} The approximate signed geodesic area of the polygon in square - * meters. - */ - getGeodesicArea: function(projection) { - var ring = this; // so we can work with a clone if needed - if(projection) { - var gg = new OpenLayers.Projection("EPSG:4326"); - if(!gg.equals(projection)) { - ring = this.clone().transform(projection, gg); - } - } - var area = 0.0; - var len = ring.components && ring.components.length; - if(len > 2) { - var p1, p2; - for(var i=0; i<len-1; i++) { - p1 = ring.components[i]; - p2 = ring.components[i+1]; - area += OpenLayers.Util.rad(p2.x - p1.x) * - (2 + Math.sin(OpenLayers.Util.rad(p1.y)) + - Math.sin(OpenLayers.Util.rad(p2.y))); - } - area = area * 6378137.0 * 6378137.0 / 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 (y - y2) * ((x2 - x1) / (y2 - y1)) + x2; - } - 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; - }, - - /** - * APIMethod: getVertices - * Return a list of all points in this geometry. - * - * Parameters: - * nodes - {Boolean} For lines, only return vertices that are - * endpoints. If false, for lines, only vertices that are not - * endpoints will be returned. If not provided, all vertices will - * be returned. - * - * Returns: - * {Array} A list of all vertices in the geometry. - */ - getVertices: function(nodes) { - return (nodes === true) ? [] : this.components.slice(0, this.components.length-1); - }, - - CLASS_NAME: "OpenLayers.Geometry.LinearRing" -}); -/* ====================================================================== - OpenLayers/Layer/HTTPRequest.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - - -/** - * @requires OpenLayers/Layer.js - */ - -/** - * Class: OpenLayers.Layer.HTTPRequest - * - * Inherits from: - * - <OpenLayers.Layer> - */ -OpenLayers.Layer.HTTPRequest = OpenLayers.Class(OpenLayers.Layer, { - - /** - * Constant: URL_HASH_FACTOR - * {Float} Used to hash URL param strings for multi-WMS server selection. - * Set to the Golden Ratio per Knuth's recommendation. - */ - URL_HASH_FACTOR: (Math.sqrt(5) - 1) / 2, - - /** - * Property: url - * {Array(String) or String} This is either an array of url strings or - * a single url string. - */ - url: null, - - /** - * Property: params - * {Object} Hashtable of key/value parameters - */ - params: null, - - /** - * APIProperty: reproject - * *Deprecated*. See http://docs.openlayers.org/library/spherical_mercator.html - * for information on the replacement for this functionality. - * {Boolean} Whether layer should reproject itself based on base layer - * locations. This allows reprojection onto commercial layers. - * Default is false: Most layers can't reproject, but layers - * which can create non-square geographic pixels can, like WMS. - * - */ - reproject: false, - - /** - * Constructor: OpenLayers.Layer.HTTPRequest - * - * Parameters: - * name - {String} - * url - {Array(String) or String} - * params - {Object} - * options - {Object} Hashtable of extra options to tag onto the layer - */ - initialize: function(name, url, params, options) { - OpenLayers.Layer.prototype.initialize.apply(this, [name, options]); - this.url = url; - if (!this.params) { - this.params = OpenLayers.Util.extend({}, params); - } - }, - - /** - * APIMethod: destroy - */ - destroy: function() { - this.url = null; - this.params = null; - OpenLayers.Layer.prototype.destroy.apply(this, arguments); - }, - - /** - * APIMethod: clone - * - * Parameters: - * obj - {Object} - * - * Returns: - * {<OpenLayers.Layer.HTTPRequest>} An exact clone of this - * <OpenLayers.Layer.HTTPRequest> - */ - clone: function (obj) { - - if (obj == null) { - obj = new OpenLayers.Layer.HTTPRequest(this.name, - this.url, - this.params, - this.getOptions()); - } - - //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: setUrl - * - * Parameters: - * newUrl - {String} - */ - setUrl: function(newUrl) { - this.url = newUrl; - }, - - /** - * APIMethod: mergeNewParams - * - * Parameters: - * newParams - {Object} - * - * Returns: - * redrawn: {Boolean} whether the layer was actually redrawn. - */ - mergeNewParams:function(newParams) { - this.params = OpenLayers.Util.extend(this.params, newParams); - var ret = this.redraw(); - if(this.map != null) { - this.map.events.triggerEvent("changelayer", { - layer: this, - property: "params" - }); - } - return ret; - }, - - /** - * APIMethod: redraw - * Redraws the layer. Returns true if the layer was redrawn, false if not. - * - * Parameters: - * force - {Boolean} Force redraw by adding random parameter. - * - * Returns: - * {Boolean} The layer was redrawn. - */ - redraw: function(force) { - if (force) { - return this.mergeNewParams({"_olSalt": Math.random()}); - } else { - return OpenLayers.Layer.prototype.redraw.apply(this, []); - } - }, - - /** - * Method: selectUrl - * selectUrl() implements the standard floating-point multiplicative - * hash function described by Knuth, and hashes the contents of the - * given param string into a float between 0 and 1. This float is then - * scaled to the size of the provided urls array, and used to select - * a URL. - * - * Parameters: - * paramString - {String} - * urls - {Array(String)} - * - * Returns: - * {String} An entry from the urls array, deterministically selected based - * on the paramString. - */ - 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)]; - }, - - /** - * Method: getFullRequestString - * Combine url with layer's params and these newParams. - * - * does checking on the serverPath variable, allowing for cases when it - * is supplied with trailing ? or &, as well as cases where not. - * - * return in formatted string like this: - * "server?key1=value1&key2=value2&key3=value3" - * - * WARNING: The altUrl parameter is deprecated and will be removed in 3.0. - * - * Parameters: - * newParams - {Object} - * altUrl - {String} Use this as the url instead of the layer's url - * - * Returns: - * {String} - */ - getFullRequestString:function(newParams, altUrl) { - - // if not altUrl passed in, use layer's url - var url = altUrl || this.url; - - // create a new params hashtable with all the layer params and the - // new params together. then convert to string - var allParams = OpenLayers.Util.extend({}, this.params); - allParams = OpenLayers.Util.extend(allParams, newParams); - var paramsString = OpenLayers.Util.getParameterString(allParams); - - // if url is not a string, it should be an array of strings, - // in which case we will deterministically select one of them in - // order to evenly distribute requests to different urls. - // - if (OpenLayers.Util.isArray(url)) { - url = this.selectUrl(paramsString, url); - } - - // ignore parameters that are already in the url search string - 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); - - return OpenLayers.Util.urlAppend(url, paramsString); - }, - - CLASS_NAME: "OpenLayers.Layer.HTTPRequest" -}); -/* ====================================================================== - OpenLayers/Tile.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - - -/** - * @requires OpenLayers/BaseTypes/Class.js - * @requires OpenLayers/Util.js - */ - -/** - * Class: OpenLayers.Tile - * This is a class designed to designate a single tile, however - * it is explicitly designed to do relatively little. Tiles store - * information about themselves -- such as the URL that they are related - * to, and their size - but do not add themselves to the layer div - * automatically, for example. Create a new tile with the - * <OpenLayers.Tile> constructor, or a subclass. - * - * TBD 3.0 - remove reference to url in above paragraph - * - */ -OpenLayers.Tile = OpenLayers.Class({ - - /** - * APIProperty: events - * {<OpenLayers.Events>} An events object that handles all - * events on the tile. - * - * Register a listener for a particular event with the following syntax: - * (code) - * tile.events.register(type, obj, listener); - * (end) - * - * Supported event types: - * beforedraw - Triggered before the tile is drawn. Used to defer - * drawing to an animation queue. To defer drawing, listeners need - * to return false, which will abort drawing. The queue handler needs - * to call <draw>(true) to actually draw the tile. - * loadstart - Triggered when tile loading starts. - * loadend - Triggered when tile loading ends. - * loaderror - Triggered before the loadend event (i.e. when the tile is - * still hidden) if the tile could not be loaded. - * reload - Triggered when an already loading tile is reloaded. - * unload - Triggered before a tile is unloaded. - */ - events: null, - - /** - * APIProperty: eventListeners - * {Object} If set as an option at construction, the eventListeners - * object will be registered with <OpenLayers.Events.on>. Object - * structure must be a listeners object as shown in the example for - * the events.on method. - * - * This options can be set in the ``tileOptions`` option from - * <OpenLayers.Layer.Grid>. For example, to be notified of the - * ``loadend`` event of each tiles: - * (code) - * new OpenLayers.Layer.OSM('osm', 'http://tile.openstreetmap.org/${z}/${x}/${y}.png', { - * tileOptions: { - * eventListeners: { - * 'loadend': function(evt) { - * // do something on loadend - * } - * } - * } - * }); - * (end) - */ - eventListeners: null, - - /** - * Property: id - * {String} null - */ - id: null, - - /** - * Property: layer - * {<OpenLayers.Layer>} layer the tile is attached to - */ - layer: null, - - /** - * Property: url - * {String} url of the request. - * - * TBD 3.0 - * Deprecated. The base tile class does not need an url. This should be - * handled in subclasses. Does not belong here. - */ - url: null, - - /** - * APIProperty: bounds - * {<OpenLayers.Bounds>} null - */ - bounds: null, - - /** - * Property: size - * {<OpenLayers.Size>} null - */ - size: null, - - /** - * Property: position - * {<OpenLayers.Pixel>} Top Left pixel of the tile - */ - position: null, - - /** - * Property: isLoading - * {Boolean} Is the tile loading? - */ - isLoading: false, - - /** 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. - */ - - /** - * Constructor: OpenLayers.Tile - * Constructor for a new <OpenLayers.Tile> instance. - * - * Parameters: - * layer - {<OpenLayers.Layer>} layer that the tile will go in. - * position - {<OpenLayers.Pixel>} - * bounds - {<OpenLayers.Bounds>} - * url - {<String>} - * size - {<OpenLayers.Size>} - * options - {Object} - */ - initialize: function(layer, position, bounds, url, size, options) { - this.layer = layer; - this.position = position.clone(); - this.setBounds(bounds); - this.url = url; - if (size) { - this.size = size.clone(); - } - - //give the tile a unique id based on its BBOX. - this.id = OpenLayers.Util.createUniqueID("Tile_"); - - OpenLayers.Util.extend(this, options); - - this.events = new OpenLayers.Events(this); - if (this.eventListeners instanceof Object) { - this.events.on(this.eventListeners); - } - }, - - /** - * Method: unload - * Call immediately before destroying if you are listening to tile - * events, so that counters are properly handled if tile is still - * loading at destroy-time. Will only fire an event if the tile is - * still loading. - */ - unload: function() { - if (this.isLoading) { - this.isLoading = false; - this.events.triggerEvent("unload"); - } - }, - - /** - * APIMethod: destroy - * Nullify references to prevent circular references and memory leaks. - */ - destroy:function() { - this.layer = null; - this.bounds = null; - this.size = null; - this.position = null; - - if (this.eventListeners) { - this.events.un(this.eventListeners); - } - this.events.destroy(); - this.eventListeners = null; - this.events = null; - }, - - /** - * Method: draw - * Clear whatever is currently in the tile, then return whether or not - * it should actually be re-drawn. This is an example implementation - * that can be overridden by subclasses. The minimum thing to do here - * is to call <clear> and return the result from <shouldDraw>. - * - * Parameters: - * force - {Boolean} If true, the tile will not be cleared and no beforedraw - * event will be fired. This is used for drawing tiles asynchronously - * after drawing has been cancelled by returning false from a beforedraw - * listener. - * - * Returns: - * {Boolean} Whether or not the tile should actually be drawn. Returns null - * if a beforedraw listener returned false. - */ - draw: function(force) { - if (!force) { - //clear tile's contents and mark as not drawn - this.clear(); - } - var draw = this.shouldDraw(); - if (draw && !force && this.events.triggerEvent("beforedraw") === false) { - draw = null; - } - return draw; - }, - - /** - * Method: shouldDraw - * Return whether or not the tile should actually be (re-)drawn. The only - * case where we *wouldn't* want to draw the tile is if the tile is outside - * its layer's maxExtent - * - * Returns: - * {Boolean} Whether or not the tile should actually be drawn. - */ - shouldDraw: function() { - var withinMaxExtent = false, - maxExtent = this.layer.maxExtent; - if (maxExtent) { - var map = this.layer.map; - var worldBounds = map.baseLayer.wrapDateLine && map.getMaxExtent(); - if (this.bounds.intersectsBounds(maxExtent, {inclusive: false, worldBounds: worldBounds})) { - withinMaxExtent = true; - } - } - - return withinMaxExtent || this.layer.displayOutsideMaxExtent; - }, - - /** - * Method: setBounds - * Sets the bounds on this instance - * - * Parameters: - * bounds {<OpenLayers.Bounds>} - */ - setBounds: function(bounds) { - bounds = bounds.clone(); - if (this.layer.map.baseLayer.wrapDateLine) { - var worldExtent = this.layer.map.getMaxExtent(), - tolerance = this.layer.map.getResolution(); - bounds = bounds.wrapDateLine(worldExtent, { - leftTolerance: tolerance, - rightTolerance: tolerance - }); - } - this.bounds = bounds; - }, - - /** - * Method: moveTo - * Reposition the tile. - * - * Parameters: - * bounds - {<OpenLayers.Bounds>} - * position - {<OpenLayers.Pixel>} - * redraw - {Boolean} Call draw method on tile after moving. - * Default is true - */ - moveTo: function (bounds, position, redraw) { - if (redraw == null) { - redraw = true; - } - - this.setBounds(bounds); - this.position = position.clone(); - if (redraw) { - this.draw(); - } - }, - - /** - * Method: clear - * Clear the tile of any bounds/position-related data so that it can - * be reused in a new location. - */ - clear: function(draw) { - // to be extended by subclasses - }, - - CLASS_NAME: "OpenLayers.Tile" -}); -/* ====================================================================== - OpenLayers/Tile/Image.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - - -/** - * @requires OpenLayers/Tile.js - * @requires OpenLayers/Animation.js - * @requires OpenLayers/Util.js - */ - -/** - * Class: OpenLayers.Tile.Image - * Instances of OpenLayers.Tile.Image are used to manage the image tiles - * used by various layers. Create a new image tile with the - * <OpenLayers.Tile.Image> constructor. - * - * Inherits from: - * - <OpenLayers.Tile> - */ -OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile, { - - /** - * APIProperty: events - * {<OpenLayers.Events>} An events object that handles all - * events on the tile. - * - * Register a listener for a particular event with the following syntax: - * (code) - * tile.events.register(type, obj, listener); - * (end) - * - * Supported event types (in addition to the <OpenLayers.Tile> events): - * beforeload - Triggered before an image is prepared for loading, when the - * url for the image is known already. Listeners may call <setImage> on - * the tile instance. If they do so, that image will be used and no new - * one will be created. - */ - - /** - * APIProperty: url - * {String} The URL of the image being requested. No default. Filled in by - * layer.getURL() function. May be modified by loadstart listeners. - */ - url: null, - - /** - * Property: imgDiv - * {HTMLImageElement} The image for this tile. - */ - imgDiv: null, - - /** - * Property: frame - * {DOMElement} The image element is appended to the frame. Any gutter on - * the image will be hidden behind the frame. If no gutter is set, - * this will be null. - */ - frame: null, - - /** - * Property: imageReloadAttempts - * {Integer} Attempts to load the image. - */ - imageReloadAttempts: null, - - /** - * Property: layerAlphaHack - * {Boolean} True if the png alpha hack needs to be applied on the layer's div. - */ - layerAlphaHack: null, - - /** - * Property: asyncRequestId - * {Integer} ID of an request to see if request is still valid. This is a - * number which increments by 1 for each asynchronous request. - */ - asyncRequestId: null, - - /** - * APIProperty: maxGetUrlLength - * {Number} If set, requests that would result in GET urls with more - * characters than the number provided will be made using form-encoded - * HTTP POST. It is good practice to avoid urls that are longer than 2048 - * characters. - * - * Caution: - * Older versions of Gecko based browsers (e.g. Firefox < 3.5) and most - * Opera versions do not fully support this option. On all browsers, - * transition effects are not supported if POST requests are used. - */ - maxGetUrlLength: null, - - /** - * Property: canvasContext - * {CanvasRenderingContext2D} A canvas context associated with - * the tile image. - */ - canvasContext: null, - - /** - * APIProperty: crossOriginKeyword - * The value of the crossorigin keyword to use when loading images. This is - * only relevant when using <getCanvasContext> for tiles from remote - * origins and should be set to either 'anonymous' or 'use-credentials' - * for servers that send Access-Control-Allow-Origin headers with their - * tiles. - */ - crossOriginKeyword: 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. - */ - - /** - * Constructor: OpenLayers.Tile.Image - * Constructor for a new <OpenLayers.Tile.Image> instance. - * - * Parameters: - * layer - {<OpenLayers.Layer>} layer that the tile will go in. - * position - {<OpenLayers.Pixel>} - * bounds - {<OpenLayers.Bounds>} - * url - {<String>} Deprecated. Remove me in 3.0. - * size - {<OpenLayers.Size>} - * options - {Object} - */ - initialize: function(layer, position, bounds, url, size, options) { - OpenLayers.Tile.prototype.initialize.apply(this, arguments); - - this.url = url; //deprecated remove me - - this.layerAlphaHack = this.layer.alpha && OpenLayers.Util.alphaHack(); - - if (this.maxGetUrlLength != null || this.layer.gutter || this.layerAlphaHack) { - // only create frame if it's needed - this.frame = document.createElement("div"); - this.frame.style.position = "absolute"; - this.frame.style.overflow = "hidden"; - } - if (this.maxGetUrlLength != null) { - OpenLayers.Util.extend(this, OpenLayers.Tile.Image.IFrame); - } - }, - - /** - * APIMethod: destroy - * nullify references to prevent circular references and memory leaks - */ - destroy: function() { - if (this.imgDiv) { - this.clear(); - this.imgDiv = null; - this.frame = null; - } - // don't handle async requests any more - this.asyncRequestId = null; - OpenLayers.Tile.prototype.destroy.apply(this, arguments); - }, - - /** - * Method: draw - * Check that a tile should be drawn, and draw it. - * - * Returns: - * {Boolean} Was a tile drawn? Or null if a beforedraw listener returned - * false. - */ - draw: function() { - var shouldDraw = OpenLayers.Tile.prototype.draw.apply(this, arguments); - if (shouldDraw) { - // The layer's reproject option is deprecated. - if (this.layer != this.layer.map.baseLayer && this.layer.reproject) { - // getBoundsFromBaseLayer is defined in deprecated.js. - this.bounds = this.getBoundsFromBaseLayer(this.position); - } - if (this.isLoading) { - //if we're already loading, send 'reload' instead of 'loadstart'. - this._loadEvent = "reload"; - } else { - this.isLoading = true; - this._loadEvent = "loadstart"; - } - this.renderTile(); - this.positionTile(); - } else if (shouldDraw === false) { - this.unload(); - } - return shouldDraw; - }, - - /** - * Method: renderTile - * Internal function to actually initialize the image tile, - * position it correctly, and set its url. - */ - renderTile: function() { - if (this.layer.async) { - // Asynchronous image requests call the asynchronous getURL method - // on the layer to fetch an image that covers 'this.bounds'. - var id = this.asyncRequestId = (this.asyncRequestId || 0) + 1; - this.layer.getURLasync(this.bounds, function(url) { - if (id == this.asyncRequestId) { - this.url = url; - this.initImage(); - } - }, this); - } else { - // synchronous image requests get the url immediately. - this.url = this.layer.getURL(this.bounds); - this.initImage(); - } - }, - - /** - * Method: positionTile - * Using the properties currenty set on the layer, position the tile correctly. - * This method is used both by the async and non-async versions of the Tile.Image - * code. - */ - positionTile: function() { - var style = this.getTile().style, - size = this.frame ? this.size : - this.layer.getImageSize(this.bounds), - ratio = 1; - if (this.layer instanceof OpenLayers.Layer.Grid) { - ratio = this.layer.getServerResolution() / this.layer.map.getResolution(); - } - style.left = this.position.x + "px"; - style.top = this.position.y + "px"; - style.width = Math.round(ratio * size.w) + "px"; - style.height = Math.round(ratio * size.h) + "px"; - }, - - /** - * Method: clear - * Remove the tile from the DOM, clear it of any image related data so that - * it can be reused in a new location. - */ - clear: function() { - OpenLayers.Tile.prototype.clear.apply(this, arguments); - var img = this.imgDiv; - if (img) { - var tile = this.getTile(); - if (tile.parentNode === this.layer.div) { - this.layer.div.removeChild(tile); - } - this.setImgSrc(); - if (this.layerAlphaHack === true) { - img.style.filter = ""; - } - OpenLayers.Element.removeClass(img, "olImageLoadError"); - } - this.canvasContext = null; - }, - - /** - * Method: getImage - * Returns or creates and returns the tile image. - */ - getImage: function() { - if (!this.imgDiv) { - this.imgDiv = OpenLayers.Tile.Image.IMAGE.cloneNode(false); - - var style = this.imgDiv.style; - if (this.frame) { - var left = 0, top = 0; - if (this.layer.gutter) { - left = this.layer.gutter / this.layer.tileSize.w * 100; - top = this.layer.gutter / this.layer.tileSize.h * 100; - } - style.left = -left + "%"; - style.top = -top + "%"; - style.width = (2 * left + 100) + "%"; - style.height = (2 * top + 100) + "%"; - } - style.visibility = "hidden"; - style.opacity = 0; - if (this.layer.opacity < 1) { - style.filter = 'alpha(opacity=' + - (this.layer.opacity * 100) + - ')'; - } - style.position = "absolute"; - if (this.layerAlphaHack) { - // move the image out of sight - style.paddingTop = style.height; - style.height = "0"; - style.width = "100%"; - } - if (this.frame) { - this.frame.appendChild(this.imgDiv); - } - } - - return this.imgDiv; - }, - - /** - * APIMethod: setImage - * Sets the image element for this tile. This method should only be called - * from beforeload listeners. - * - * Parameters - * img - {HTMLImageElement} The image to use for this tile. - */ - setImage: function(img) { - this.imgDiv = img; - }, - - /** - * Method: initImage - * Creates the content for the frame on the tile. - */ - initImage: function() { - if (!this.url && !this.imgDiv) { - // fast path out - if there is no tile url and no previous image - this.isLoading = false; - return; - } - this.events.triggerEvent('beforeload'); - this.layer.div.appendChild(this.getTile()); - this.events.triggerEvent(this._loadEvent); - var img = this.getImage(); - var src = img.getAttribute('src') || ''; - if (this.url && OpenLayers.Util.isEquivalentUrl(src, this.url)) { - this._loadTimeout = window.setTimeout( - OpenLayers.Function.bind(this.onImageLoad, this), 0 - ); - } else { - this.stopLoading(); - if (this.crossOriginKeyword) { - img.removeAttribute("crossorigin"); - } - OpenLayers.Event.observe(img, "load", - OpenLayers.Function.bind(this.onImageLoad, this) - ); - OpenLayers.Event.observe(img, "error", - OpenLayers.Function.bind(this.onImageError, this) - ); - this.imageReloadAttempts = 0; - this.setImgSrc(this.url); - } - }, - - /** - * Method: setImgSrc - * Sets the source for the tile image - * - * Parameters: - * url - {String} or undefined to hide the image - */ - setImgSrc: function(url) { - var img = this.imgDiv; - if (url) { - img.style.visibility = 'hidden'; - img.style.opacity = 0; - // don't set crossOrigin if the url is a data URL - if (this.crossOriginKeyword) { - if (url.substr(0, 5) !== 'data:') { - img.setAttribute("crossorigin", this.crossOriginKeyword); - } else { - img.removeAttribute("crossorigin"); - } - } - img.src = url; - } else { - // Remove reference to the image, and leave it to the browser's - // caching and garbage collection. - this.stopLoading(); - this.imgDiv = null; - if (img.parentNode) { - img.parentNode.removeChild(img); - } - } - }, - - /** - * Method: getTile - * Get the tile's markup. - * - * Returns: - * {DOMElement} The tile's markup - */ - getTile: function() { - return this.frame ? this.frame : this.getImage(); - }, - - /** - * Method: createBackBuffer - * Create a backbuffer for this tile. A backbuffer isn't exactly a clone - * of the tile's markup, because we want to avoid the reloading of the - * image. So we clone the frame, and steal the image from the tile. - * - * Returns: - * {DOMElement} The markup, or undefined if the tile has no image - * or if it's currently loading. - */ - createBackBuffer: function() { - if (!this.imgDiv || this.isLoading) { - return; - } - var backBuffer; - if (this.frame) { - backBuffer = this.frame.cloneNode(false); - backBuffer.appendChild(this.imgDiv); - } else { - backBuffer = this.imgDiv; - } - this.imgDiv = null; - return backBuffer; - }, - - /** - * Method: onImageLoad - * Handler for the image onload event - */ - onImageLoad: function() { - var img = this.imgDiv; - this.stopLoading(); - img.style.visibility = 'inherit'; - img.style.opacity = this.layer.opacity; - this.isLoading = false; - this.canvasContext = null; - this.events.triggerEvent("loadend"); - - if (this.layerAlphaHack === true) { - img.style.filter = - "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + - img.src + "', sizingMethod='scale')"; - } - }, - - /** - * Method: onImageError - * Handler for the image onerror event - */ - onImageError: function() { - var img = this.imgDiv; - if (img.src != null) { - this.imageReloadAttempts++; - if (this.imageReloadAttempts <= OpenLayers.IMAGE_RELOAD_ATTEMPTS) { - this.setImgSrc(this.layer.getURL(this.bounds)); - } else { - OpenLayers.Element.addClass(img, "olImageLoadError"); - this.events.triggerEvent("loaderror"); - this.onImageLoad(); - } - } - }, - - /** - * Method: stopLoading - * Stops a loading sequence so <onImageLoad> won't be executed. - */ - stopLoading: function() { - OpenLayers.Event.stopObservingElement(this.imgDiv); - window.clearTimeout(this._loadTimeout); - delete this._loadTimeout; - }, - - /** - * APIMethod: getCanvasContext - * Returns a canvas context associated with the tile image (with - * the image drawn on it). - * Returns undefined if the browser does not support canvas, if - * the tile has no image or if it's currently loading. - * - * The function returns a canvas context instance but the - * underlying canvas is still available in the 'canvas' property: - * (code) - * var context = tile.getCanvasContext(); - * if (context) { - * var data = context.canvas.toDataURL('image/jpeg'); - * } - * (end) - * - * Returns: - * {Boolean} - */ - getCanvasContext: function() { - if (OpenLayers.CANVAS_SUPPORTED && this.imgDiv && !this.isLoading) { - if (!this.canvasContext) { - var canvas = document.createElement("canvas"); - canvas.width = this.size.w; - canvas.height = this.size.h; - this.canvasContext = canvas.getContext("2d"); - this.canvasContext.drawImage(this.imgDiv, 0, 0); - } - return this.canvasContext; - } - }, - - CLASS_NAME: "OpenLayers.Tile.Image" - -}); - -/** - * Constant: OpenLayers.Tile.Image.IMAGE - * {HTMLImageElement} The image for a tile. - */ -OpenLayers.Tile.Image.IMAGE = (function() { - var img = new Image(); - img.className = "olTileImage"; - // avoid image gallery menu in IE6 - img.galleryImg = "no"; - return img; -}()); - -/* ====================================================================== - OpenLayers/Layer/Grid.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - - -/** - * @requires OpenLayers/Layer/HTTPRequest.js - * @requires OpenLayers/Tile/Image.js - */ - -/** - * Class: OpenLayers.Layer.Grid - * Base class for layers that use a lattice of tiles. Create a new grid - * layer with the <OpenLayers.Layer.Grid> constructor. - * - * Inherits from: - * - <OpenLayers.Layer.HTTPRequest> - */ -OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { - - /** - * APIProperty: tileSize - * {<OpenLayers.Size>} - */ - tileSize: null, - - /** - * Property: tileOriginCorner - * {String} If the <tileOrigin> property is not provided, the tile origin - * will be derived from the layer's <maxExtent>. The corner of the - * <maxExtent> used is determined by this property. Acceptable values - * are "tl" (top left), "tr" (top right), "bl" (bottom left), and "br" - * (bottom right). Default is "bl". - */ - tileOriginCorner: "bl", - - /** - * APIProperty: tileOrigin - * {<OpenLayers.LonLat>} Optional origin for aligning the grid of tiles. - * If provided, requests for tiles at all resolutions will be aligned - * with this location (no tiles shall overlap this location). If - * not provided, the grid of tiles will be aligned with the layer's - * <maxExtent>. Default is ``null``. - */ - tileOrigin: null, - - /** APIProperty: tileOptions - * {Object} optional configuration options for <OpenLayers.Tile> instances - * created by this Layer, if supported by the tile class. - */ - tileOptions: null, - - /** - * APIProperty: tileClass - * {<OpenLayers.Tile>} The tile class to use for this layer. - * Defaults is OpenLayers.Tile.Image. - */ - tileClass: OpenLayers.Tile.Image, - - /** - * Property: grid - * {Array(Array(<OpenLayers.Tile>))} This is an array of rows, each row is - * an array of tiles. - */ - grid: null, - - /** - * APIProperty: singleTile - * {Boolean} Moves the layer into single-tile mode, meaning that one tile - * will be loaded. The tile's size will be determined by the 'ratio' - * property. When the tile is dragged such that it does not cover the - * entire viewport, it is reloaded. - */ - singleTile: false, - - /** APIProperty: ratio - * {Float} Used only when in single-tile mode, this specifies the - * ratio of the size of the single tile to the size of the map. - * Default value is 1.5. - */ - ratio: 1.5, - - /** - * APIProperty: buffer - * {Integer} Used only when in gridded mode, this specifies the number of - * extra rows and colums of tiles on each side which will - * surround the minimum grid tiles to cover the map. - * For very slow loading layers, a larger value may increase - * performance somewhat when dragging, but will increase bandwidth - * use significantly. - */ - buffer: 0, - - /** - * APIProperty: transitionEffect - * {String} The transition effect to use when the map is zoomed. - * Two posible values: - * - * "resize" - Existing tiles are resized on zoom to provide a visual - * effect of the zoom having taken place immediately. As the - * new tiles become available, they are drawn on top of the - * resized tiles (this is the default setting). - * "map-resize" - Existing tiles are resized on zoom and placed below the - * base layer. New tiles for the base layer will cover existing tiles. - * This setting is recommended when having an overlay duplicated during - * the transition is undesirable (e.g. street labels or big transparent - * fills). - * null - No transition effect. - * - * Using "resize" on non-opaque layers can cause undesired visual - * effects. Set transitionEffect to null in this case. - */ - transitionEffect: "resize", - - /** - * APIProperty: numLoadingTiles - * {Integer} How many tiles are still loading? - */ - numLoadingTiles: 0, - - /** - * Property: serverResolutions - * {Array(Number}} This property is documented in subclasses as - * an API property. - */ - serverResolutions: null, - - /** - * Property: loading - * {Boolean} Indicates if tiles are being loaded. - */ - loading: false, - - /** - * Property: backBuffer - * {DOMElement} The back buffer. - */ - backBuffer: null, - - /** - * Property: gridResolution - * {Number} The resolution of the current grid. Used for backbuffer and - * client zoom. This property is updated every time the grid is - * initialized. - */ - gridResolution: null, - - /** - * Property: backBufferResolution - * {Number} The resolution of the current back buffer. This property is - * updated each time a back buffer is created. - */ - backBufferResolution: null, - - /** - * Property: backBufferLonLat - * {Object} The top-left corner of the current back buffer. Includes lon - * and lat properties. This object is updated each time a back buffer - * is created. - */ - backBufferLonLat: null, - - /** - * Property: backBufferTimerId - * {Number} The id of the back buffer timer. This timer is used to - * delay the removal of the back buffer, thereby preventing - * flash effects caused by tile animation. - */ - backBufferTimerId: null, - - /** - * APIProperty: removeBackBufferDelay - * {Number} Delay for removing the backbuffer when all tiles have finished - * loading. Can be set to 0 when no css opacity transitions for the - * olTileImage class are used. Default is 0 for <singleTile> layers, - * 2500 for tiled layers. See <className> for more information on - * tile animation. - */ - removeBackBufferDelay: null, - - /** - * APIProperty: className - * {String} Name of the class added to the layer div. If not set in the - * options passed to the constructor then className defaults to - * "olLayerGridSingleTile" for single tile layers (see <singleTile>), - * and "olLayerGrid" for non single tile layers. - * - * Note: - * - * The displaying of tiles is not animated by default for single tile - * layers - OpenLayers' default theme (style.css) includes this: - * (code) - * .olLayerGrid .olTileImage { - * -webkit-transition: opacity 0.2s linear; - * -moz-transition: opacity 0.2s linear; - * -o-transition: opacity 0.2s linear; - * transition: opacity 0.2s linear; - * } - * (end) - * To animate tile displaying for any grid layer the following - * CSS rule can be used: - * (code) - * .olTileImage { - * -webkit-transition: opacity 0.2s linear; - * -moz-transition: opacity 0.2s linear; - * -o-transition: opacity 0.2s linear; - * transition: opacity 0.2s linear; - * } - * (end) - * In that case, to avoid flash effects, <removeBackBufferDelay> - * should not be zero. - */ - className: null, - - /** - * 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 event types: - * addtile - Triggered when a tile is added to this layer. Listeners receive - * an object as first argument, which has a tile property that - * references the tile that has been added. - * tileloadstart - Triggered when a tile starts loading. Listeners receive - * an object as first argument, which has a tile property that - * references the tile that starts loading. - * tileloaded - Triggered when each new tile is - * loaded, as a means of progress update to listeners. - * listeners can access 'numLoadingTiles' if they wish to keep - * track of the loading progress. Listeners are called with an object - * with a 'tile' property as first argument, making the loaded tile - * available to the listener, and an 'aborted' property, which will be - * true when loading was aborted and no tile data is available. - * tileerror - Triggered before the tileloaded event (i.e. when the tile is - * still hidden) if a tile failed to load. Listeners receive an object - * as first argument, which has a tile property that references the - * tile that could not be loaded. - * retile - Triggered when the layer recreates its tile grid. - */ - - /** - * Property: gridLayout - * {Object} Object containing properties tilelon, tilelat, startcol, - * startrow - */ - gridLayout: null, - - /** - * Property: rowSign - * {Number} 1 for grids starting at the top, -1 for grids starting at the - * bottom. This is used for several grid index and offset calculations. - */ - rowSign: null, - - /** - * Property: transitionendEvents - * {Array} Event names for transitionend - */ - transitionendEvents: [ - 'transitionend', 'webkitTransitionEnd', 'otransitionend', - 'oTransitionEnd' - ], - - /** - * Constructor: OpenLayers.Layer.Grid - * Create a new grid layer - * - * Parameters: - * name - {String} - * url - {String} - * params - {Object} - * options - {Object} Hashtable of extra options to tag onto the layer - */ - initialize: function(name, url, params, options) { - OpenLayers.Layer.HTTPRequest.prototype.initialize.apply(this, - arguments); - this.grid = []; - this._removeBackBuffer = OpenLayers.Function.bind(this.removeBackBuffer, this); - - this.initProperties(); - - this.rowSign = this.tileOriginCorner.substr(0, 1) === "t" ? 1 : -1; - }, - - /** - * Method: initProperties - * Set any properties that depend on the value of singleTile. - * Currently sets removeBackBufferDelay and className - */ - initProperties: function() { - if (this.options.removeBackBufferDelay === undefined) { - this.removeBackBufferDelay = this.singleTile ? 0 : 2500; - } - - if (this.options.className === undefined) { - this.className = this.singleTile ? 'olLayerGridSingleTile' : - 'olLayerGrid'; - } - }, - - /** - * Method: setMap - * - * Parameters: - * map - {<OpenLayers.Map>} The map. - */ - setMap: function(map) { - OpenLayers.Layer.HTTPRequest.prototype.setMap.call(this, map); - OpenLayers.Element.addClass(this.div, this.className); - }, - - /** - * Method: removeMap - * Called when the layer is removed from the map. - * - * Parameters: - * map - {<OpenLayers.Map>} The map. - */ - removeMap: function(map) { - this.removeBackBuffer(); - }, - - /** - * APIMethod: destroy - * Deconstruct the layer and clear the grid. - */ - destroy: function() { - this.removeBackBuffer(); - this.clearGrid(); - - this.grid = null; - this.tileSize = null; - OpenLayers.Layer.HTTPRequest.prototype.destroy.apply(this, arguments); - }, - - /** - * APIMethod: mergeNewParams - * Refetches tiles with new params merged, keeping a backbuffer. Each - * loading new tile will have a css class of '.olTileReplacing'. If a - * stylesheet applies a 'display: none' style to that class, any fade-in - * transition will not apply, and backbuffers for each tile will be removed - * as soon as the tile is loaded. - * - * Parameters: - * newParams - {Object} - * - * Returns: - * redrawn: {Boolean} whether the layer was actually redrawn. - */ - - /** - * Method: clearGrid - * Go through and remove all tiles from the grid, calling - * destroy() on each of them to kill circular references - */ - 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.destroyTile(tile); - } - } - this.grid = []; - this.gridResolution = null; - this.gridLayout = null; - } - }, - - /** - * APIMethod: addOptions - * - * Parameters: - * newOptions - {Object} - * reinitialize - {Boolean} If set to true, and if resolution options of the - * current baseLayer were changed, the map will be recentered to make - * sure that it is displayed with a valid resolution, and a - * changebaselayer event will be triggered. - */ - addOptions: function (newOptions, reinitialize) { - var singleTileChanged = newOptions.singleTile !== undefined && - newOptions.singleTile !== this.singleTile; - OpenLayers.Layer.HTTPRequest.prototype.addOptions.apply(this, arguments); - if (this.map && singleTileChanged) { - this.initProperties(); - this.clearGrid(); - this.tileSize = this.options.tileSize; - this.setTileSize(); - this.moveTo(null, true); - } - }, - - /** - * APIMethod: clone - * Create a clone of this layer - * - * Parameters: - * obj - {Object} Is this ever used? - * - * Returns: - * {<OpenLayers.Layer.Grid>} An exact clone of this OpenLayers.Layer.Grid - */ - clone: function (obj) { - - if (obj == null) { - obj = new OpenLayers.Layer.Grid(this.name, - this.url, - this.params, - this.getOptions()); - } - - //get all additions from superclasses - obj = OpenLayers.Layer.HTTPRequest.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 = []; - obj.gridResolution = null; - // same for backbuffer - obj.backBuffer = null; - obj.backBufferTimerId = null; - obj.loading = false; - obj.numLoadingTiles = 0; - - return obj; - }, - - /** - * Method: moveTo - * This function is called whenever the map is moved. All the moving - * of actual 'tiles' is done by the map, but moveTo's role is to accept - * a bounds and make sure the data that that bounds requires is pre-loaded. - * - * Parameters: - * bounds - {<OpenLayers.Bounds>} - * zoomChanged - {Boolean} - * dragging - {Boolean} - */ - moveTo:function(bounds, zoomChanged, dragging) { - - OpenLayers.Layer.HTTPRequest.prototype.moveTo.apply(this, arguments); - - bounds = bounds || this.map.getExtent(); - - if (bounds != null) { - - // if grid is empty or zoom has changed, we *must* re-tile - var forceReTile = !this.grid.length || zoomChanged; - - // total bounds of the tiles - var tilesBounds = this.getTilesBounds(); - - // the new map resolution - var resolution = this.map.getResolution(); - - // the server-supported resolution for the new map resolution - var serverResolution = this.getServerResolution(resolution); - - if (this.singleTile) { - - // We want to redraw whenever even the slightest part of the - // current bounds is not contained by our tile. - // (thus, we do not specify partial -- its default is false) - - if ( forceReTile || - (!dragging && !tilesBounds.containsBounds(bounds))) { - - // In single tile mode with no transition effect, we insert - // a non-scaled backbuffer when the layer is moved. But if - // a zoom occurs right after a move, i.e. before the new - // image is received, we need to remove the backbuffer, or - // an ill-positioned image will be visible during the zoom - // transition. - - if(zoomChanged && this.transitionEffect !== 'resize') { - this.removeBackBuffer(); - } - - if(!zoomChanged || this.transitionEffect === 'resize') { - this.applyBackBuffer(resolution); - } - - this.initSingleTile(bounds); - } - } else { - - // if the bounds have changed such that they are not even - // *partially* contained by our tiles (e.g. when user has - // programmatically panned to the other side of the earth on - // zoom level 18), then moveGriddedTiles could potentially have - // to run through thousands of cycles, so we want to reTile - // instead (thus, partial true). - forceReTile = forceReTile || - !tilesBounds.intersectsBounds(bounds, { - worldBounds: this.map.baseLayer.wrapDateLine && - this.map.getMaxExtent() - }); - - if(forceReTile) { - if(zoomChanged && (this.transitionEffect === 'resize' || - this.gridResolution === resolution)) { - this.applyBackBuffer(resolution); - } - this.initGriddedTiles(bounds); - } else { - this.moveGriddedTiles(); - } - } - } - }, - - /** - * Method: getTileData - * Given a map location, retrieve a tile and the pixel offset within that - * tile corresponding to the location. If there is not an existing - * tile in the grid that covers the given location, null will be - * returned. - * - * Parameters: - * loc - {<OpenLayers.LonLat>} map location - * - * Returns: - * {Object} Object with the following properties: tile ({<OpenLayers.Tile>}), - * i ({Number} x-pixel offset from top left), and j ({Integer} y-pixel - * offset from top left). - */ - getTileData: function(loc) { - var data = null, - x = loc.lon, - y = loc.lat, - numRows = this.grid.length; - - if (this.map && numRows) { - var res = this.map.getResolution(), - tileWidth = this.tileSize.w, - tileHeight = this.tileSize.h, - bounds = this.grid[0][0].bounds, - left = bounds.left, - top = bounds.top; - - if (x < left) { - // deal with multiple worlds - if (this.map.baseLayer.wrapDateLine) { - var worldWidth = this.map.getMaxExtent().getWidth(); - var worldsAway = Math.ceil((left - x) / worldWidth); - x += worldWidth * worldsAway; - } - } - // tile distance to location (fractional number of tiles); - var dtx = (x - left) / (res * tileWidth); - var dty = (top - y) / (res * tileHeight); - // index of tile in grid - var col = Math.floor(dtx); - var row = Math.floor(dty); - if (row >= 0 && row < numRows) { - var tile = this.grid[row][col]; - if (tile) { - data = { - tile: tile, - // pixel index within tile - i: Math.floor((dtx - col) * tileWidth), - j: Math.floor((dty - row) * tileHeight) - }; - } - } - } - return data; - }, - - /** - * Method: destroyTile - * - * Parameters: - * tile - {<OpenLayers.Tile>} - */ - destroyTile: function(tile) { - this.removeTileMonitoringHooks(tile); - tile.destroy(); - }, - - /** - * Method: getServerResolution - * Return the closest server-supported resolution. - * - * Parameters: - * resolution - {Number} The base resolution. If undefined the - * map resolution is used. - * - * Returns: - * {Number} The closest server resolution value. - */ - getServerResolution: function(resolution) { - var distance = Number.POSITIVE_INFINITY; - resolution = resolution || this.map.getResolution(); - if(this.serverResolutions && - OpenLayers.Util.indexOf(this.serverResolutions, resolution) === -1) { - var i, newDistance, newResolution, serverResolution; - for(i=this.serverResolutions.length-1; i>= 0; i--) { - newResolution = this.serverResolutions[i]; - newDistance = Math.abs(newResolution - resolution); - if (newDistance > distance) { - break; - } - distance = newDistance; - serverResolution = newResolution; - } - resolution = serverResolution; - } - return resolution; - }, - - /** - * Method: getServerZoom - * Return the zoom value corresponding to the best matching server - * resolution, taking into account <serverResolutions> and <zoomOffset>. - * - * Returns: - * {Number} The closest server supported zoom. This is not the map zoom - * level, but an index of the server's resolutions array. - */ - getServerZoom: function() { - var resolution = this.getServerResolution(); - return this.serverResolutions ? - OpenLayers.Util.indexOf(this.serverResolutions, resolution) : - this.map.getZoomForResolution(resolution) + (this.zoomOffset || 0); - }, - - /** - * Method: applyBackBuffer - * Create, insert, scale and position a back buffer for the layer. - * - * Parameters: - * resolution - {Number} The resolution to transition to. - */ - applyBackBuffer: function(resolution) { - if(this.backBufferTimerId !== null) { - this.removeBackBuffer(); - } - var backBuffer = this.backBuffer; - if(!backBuffer) { - backBuffer = this.createBackBuffer(); - if(!backBuffer) { - return; - } - if (resolution === this.gridResolution) { - this.div.insertBefore(backBuffer, this.div.firstChild); - } else { - this.map.baseLayer.div.parentNode.insertBefore(backBuffer, this.map.baseLayer.div); - } - this.backBuffer = backBuffer; - - // set some information in the instance for subsequent - // calls to applyBackBuffer where the same back buffer - // is reused - var topLeftTileBounds = this.grid[0][0].bounds; - this.backBufferLonLat = { - lon: topLeftTileBounds.left, - lat: topLeftTileBounds.top - }; - this.backBufferResolution = this.gridResolution; - } - - var ratio = this.backBufferResolution / resolution; - - // scale the tiles inside the back buffer - var tiles = backBuffer.childNodes, tile; - for (var i=tiles.length-1; i>=0; --i) { - tile = tiles[i]; - tile.style.top = ((ratio * tile._i * tile._h) | 0) + 'px'; - tile.style.left = ((ratio * tile._j * tile._w) | 0) + 'px'; - tile.style.width = Math.round(ratio * tile._w) + 'px'; - tile.style.height = Math.round(ratio * tile._h) + 'px'; - } - - // and position it (based on the grid's top-left corner) - var position = this.getViewPortPxFromLonLat( - this.backBufferLonLat, resolution); - var leftOffset = this.map.layerContainerOriginPx.x; - var topOffset = this.map.layerContainerOriginPx.y; - backBuffer.style.left = Math.round(position.x - leftOffset) + 'px'; - backBuffer.style.top = Math.round(position.y - topOffset) + 'px'; - }, - - /** - * Method: createBackBuffer - * Create a back buffer. - * - * Returns: - * {DOMElement} The DOM element for the back buffer, undefined if the - * grid isn't initialized yet. - */ - createBackBuffer: function() { - var backBuffer; - if(this.grid.length > 0) { - backBuffer = document.createElement('div'); - backBuffer.id = this.div.id + '_bb'; - backBuffer.className = 'olBackBuffer'; - backBuffer.style.position = 'absolute'; - var map = this.map; - backBuffer.style.zIndex = this.transitionEffect === 'resize' ? - this.getZIndex() - 1 : - // 'map-resize': - map.Z_INDEX_BASE.BaseLayer - - (map.getNumLayers() - map.getLayerIndex(this)); - for(var i=0, lenI=this.grid.length; i<lenI; i++) { - for(var j=0, lenJ=this.grid[i].length; j<lenJ; j++) { - var tile = this.grid[i][j], - markup = this.grid[i][j].createBackBuffer(); - if (markup) { - markup._i = i; - markup._j = j; - markup._w = tile.size.w; - markup._h = tile.size.h; - markup.id = tile.id + '_bb'; - backBuffer.appendChild(markup); - } - } - } - } - return backBuffer; - }, - - /** - * Method: removeBackBuffer - * Remove back buffer from DOM. - */ - removeBackBuffer: function() { - if (this._transitionElement) { - for (var i=this.transitionendEvents.length-1; i>=0; --i) { - OpenLayers.Event.stopObserving(this._transitionElement, - this.transitionendEvents[i], this._removeBackBuffer); - } - delete this._transitionElement; - } - if(this.backBuffer) { - if (this.backBuffer.parentNode) { - this.backBuffer.parentNode.removeChild(this.backBuffer); - } - this.backBuffer = null; - this.backBufferResolution = null; - if(this.backBufferTimerId !== null) { - window.clearTimeout(this.backBufferTimerId); - this.backBufferTimerId = null; - } - } - }, - - /** - * Method: moveByPx - * Move the layer based on pixel vector. - * - * Parameters: - * dx - {Number} - * dy - {Number} - */ - moveByPx: function(dx, dy) { - if (!this.singleTile) { - this.moveGriddedTiles(); - } - }, - - /** - * APIMethod: setTileSize - * Check if we are in singleTile mode and if so, set the size as a ratio - * of the map size (as specified by the layer's 'ratio' property). - * - * Parameters: - * size - {<OpenLayers.Size>} - */ - setTileSize: function(size) { - if (this.singleTile) { - size = this.map.getSize(); - size.h = parseInt(size.h * this.ratio, 10); - size.w = parseInt(size.w * this.ratio, 10); - } - OpenLayers.Layer.HTTPRequest.prototype.setTileSize.apply(this, [size]); - }, - - /** - * APIMethod: getTilesBounds - * Return the bounds of the tile grid. - * - * Returns: - * {<OpenLayers.Bounds>} A Bounds object representing the bounds of all the - * currently loaded tiles (including those partially or not at all seen - * onscreen). - */ - getTilesBounds: function() { - var bounds = null; - - var length = this.grid.length; - if (length) { - var bottomLeftTileBounds = this.grid[length - 1][0].bounds, - width = this.grid[0].length * bottomLeftTileBounds.getWidth(), - height = this.grid.length * bottomLeftTileBounds.getHeight(); - - bounds = new OpenLayers.Bounds(bottomLeftTileBounds.left, - bottomLeftTileBounds.bottom, - bottomLeftTileBounds.left + width, - bottomLeftTileBounds.bottom + height); - } - return bounds; - }, - - /** - * Method: initSingleTile - * - * Parameters: - * bounds - {<OpenLayers.Bounds>} - */ - initSingleTile: function(bounds) { - this.events.triggerEvent("retile"); - - //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)); - - var px = this.map.getLayerPxFromLonLat({ - lon: tileBounds.left, - lat: tileBounds.top - }); - - if (!this.grid.length) { - this.grid[0] = []; - } - - 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); - } - - //remove all but our single tile - this.removeExcessTiles(1,1); - - // store the resolution of the grid - this.gridResolution = this.getServerResolution(); - }, - - /** - * Method: calculateGridLayout - * Generate parameters for the grid layout. - * - * Parameters: - * bounds - {<OpenLayers.Bound>|Object} OpenLayers.Bounds or an - * object with a 'left' and 'top' properties. - * origin - {<OpenLayers.LonLat>|Object} OpenLayers.LonLat or an - * object with a 'lon' and 'lat' properties. - * resolution - {Number} - * - * Returns: - * {Object} Object containing properties tilelon, tilelat, startcol, - * startrow - */ - calculateGridLayout: function(bounds, origin, resolution) { - var tilelon = resolution * this.tileSize.w; - var tilelat = resolution * this.tileSize.h; - - var offsetlon = bounds.left - origin.lon; - var tilecol = Math.floor(offsetlon/tilelon) - this.buffer; - - var rowSign = this.rowSign; - - var offsetlat = rowSign * (origin.lat - bounds.top + tilelat); - var tilerow = Math[~rowSign ? 'floor' : 'ceil'](offsetlat/tilelat) - this.buffer * rowSign; - - return { - tilelon: tilelon, tilelat: tilelat, - startcol: tilecol, startrow: tilerow - }; - - }, - - /** - * Method: getTileOrigin - * Determine the origin for aligning the grid of tiles. If a <tileOrigin> - * property is supplied, that will be returned. Otherwise, the origin - * will be derived from the layer's <maxExtent> property. In this case, - * the tile origin will be the corner of the <maxExtent> given by the - * <tileOriginCorner> property. - * - * Returns: - * {<OpenLayers.LonLat>} The tile origin. - */ - getTileOrigin: function() { - var origin = this.tileOrigin; - if (!origin) { - var extent = this.getMaxExtent(); - var edges = ({ - "tl": ["left", "top"], - "tr": ["right", "top"], - "bl": ["left", "bottom"], - "br": ["right", "bottom"] - })[this.tileOriginCorner]; - origin = new OpenLayers.LonLat(extent[edges[0]], extent[edges[1]]); - } - return origin; - }, - - /** - * Method: getTileBoundsForGridIndex - * - * Parameters: - * row - {Number} The row of the grid - * col - {Number} The column of the grid - * - * Returns: - * {<OpenLayers.Bounds>} The bounds for the tile at (row, col) - */ - getTileBoundsForGridIndex: function(row, col) { - var origin = this.getTileOrigin(); - var tileLayout = this.gridLayout; - var tilelon = tileLayout.tilelon; - var tilelat = tileLayout.tilelat; - var startcol = tileLayout.startcol; - var startrow = tileLayout.startrow; - var rowSign = this.rowSign; - return new OpenLayers.Bounds( - origin.lon + (startcol + col) * tilelon, - origin.lat - (startrow + row * rowSign) * tilelat * rowSign, - origin.lon + (startcol + col + 1) * tilelon, - origin.lat - (startrow + (row - 1) * rowSign) * tilelat * rowSign - ); - }, - - /** - * Method: initGriddedTiles - * - * Parameters: - * bounds - {<OpenLayers.Bounds>} - */ - initGriddedTiles:function(bounds) { - this.events.triggerEvent("retile"); - - // work out mininum number of rows and columns; this is the number of - // tiles required to cover the viewport plus at least one for panning - - var viewSize = this.map.getSize(); - - var origin = this.getTileOrigin(); - var resolution = this.map.getResolution(), - serverResolution = this.getServerResolution(), - ratio = resolution / serverResolution, - tileSize = { - w: this.tileSize.w / ratio, - h: this.tileSize.h / ratio - }; - - var minRows = Math.ceil(viewSize.h/tileSize.h) + - 2 * this.buffer + 1; - var minCols = Math.ceil(viewSize.w/tileSize.w) + - 2 * this.buffer + 1; - - var tileLayout = this.calculateGridLayout(bounds, origin, serverResolution); - this.gridLayout = tileLayout; - - var tilelon = tileLayout.tilelon; - var tilelat = tileLayout.tilelat; - - var layerContainerDivLeft = this.map.layerContainerOriginPx.x; - var layerContainerDivTop = this.map.layerContainerOriginPx.y; - - var tileBounds = this.getTileBoundsForGridIndex(0, 0); - var startPx = this.map.getViewPortPxFromLonLat( - new OpenLayers.LonLat(tileBounds.left, tileBounds.top) - ); - startPx.x = Math.round(startPx.x) - layerContainerDivLeft; - startPx.y = Math.round(startPx.y) - layerContainerDivTop; - - var tileData = [], center = this.map.getCenter(); - - var rowidx = 0; - do { - var row = this.grid[rowidx]; - if (!row) { - row = []; - this.grid.push(row); - } - - var colidx = 0; - do { - tileBounds = this.getTileBoundsForGridIndex(rowidx, colidx); - var px = startPx.clone(); - px.x = px.x + colidx * Math.round(tileSize.w); - px.y = px.y + rowidx * Math.round(tileSize.h); - var tile = row[colidx]; - if (!tile) { - tile = this.addTile(tileBounds, px); - this.addTileMonitoringHooks(tile); - row.push(tile); - } else { - tile.moveTo(tileBounds, px, false); - } - var tileCenter = tileBounds.getCenterLonLat(); - tileData.push({ - tile: tile, - distance: Math.pow(tileCenter.lon - center.lon, 2) + - Math.pow(tileCenter.lat - center.lat, 2) - }); - - colidx += 1; - } while ((tileBounds.right <= bounds.right + tilelon * this.buffer) - || colidx < minCols); - - rowidx += 1; - } while((tileBounds.bottom >= bounds.bottom - tilelat * this.buffer) - || rowidx < minRows); - - //shave off exceess rows and colums - this.removeExcessTiles(rowidx, colidx); - - var resolution = this.getServerResolution(); - // store the resolution of the grid - this.gridResolution = resolution; - - //now actually draw the tiles - tileData.sort(function(a, b) { - return a.distance - b.distance; - }); - for (var i=0, ii=tileData.length; i<ii; ++i) { - tileData[i].tile.draw(); - } - }, - - /** - * Method: getMaxExtent - * Get this layer's maximum extent. (Implemented as a getter for - * potential specific implementations in sub-classes.) - * - * Returns: - * {<OpenLayers.Bounds>} - */ - getMaxExtent: function() { - return this.maxExtent; - }, - - /** - * APIMethod: addTile - * Create a tile, initialize it, and add it to the layer div. - * - * Parameters - * bounds - {<OpenLayers.Bounds>} - * position - {<OpenLayers.Pixel>} - * - * Returns: - * {<OpenLayers.Tile>} The added OpenLayers.Tile - */ - addTile: function(bounds, position) { - var tile = new this.tileClass( - this, position, bounds, null, this.tileSize, this.tileOptions - ); - this.events.triggerEvent("addtile", {tile: tile}); - return tile; - }, - - /** - * 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 tiles. - * - * Parameters: - * tile - {<OpenLayers.Tile>} - */ - addTileMonitoringHooks: function(tile) { - - var replacingCls = 'olTileReplacing'; - - tile.onLoadStart = function() { - //if that was first tile then trigger a 'loadstart' on the layer - if (this.loading === false) { - this.loading = true; - this.events.triggerEvent("loadstart"); - } - this.events.triggerEvent("tileloadstart", {tile: tile}); - this.numLoadingTiles++; - if (!this.singleTile && this.backBuffer && this.gridResolution === this.backBufferResolution) { - OpenLayers.Element.addClass(tile.getTile(), replacingCls); - } - }; - - tile.onLoadEnd = function(evt) { - this.numLoadingTiles--; - var aborted = evt.type === 'unload'; - this.events.triggerEvent("tileloaded", { - tile: tile, - aborted: aborted - }); - if (!this.singleTile && !aborted && this.backBuffer && this.gridResolution === this.backBufferResolution) { - var tileDiv = tile.getTile(); - if (OpenLayers.Element.getStyle(tileDiv, 'display') === 'none') { - var bufferTile = document.getElementById(tile.id + '_bb'); - if (bufferTile) { - bufferTile.parentNode.removeChild(bufferTile); - } - } - OpenLayers.Element.removeClass(tileDiv, replacingCls); - } - //if that was the last tile, then trigger a 'loadend' on the layer - if (this.numLoadingTiles === 0) { - if (this.backBuffer) { - if (this.backBuffer.childNodes.length === 0) { - // no tiles transitioning, remove immediately - this.removeBackBuffer(); - } else { - // wait until transition has ended or delay has passed - this._transitionElement = aborted ? - this.div.lastChild : tile.imgDiv; - var transitionendEvents = this.transitionendEvents; - for (var i=transitionendEvents.length-1; i>=0; --i) { - OpenLayers.Event.observe(this._transitionElement, - transitionendEvents[i], - this._removeBackBuffer); - } - // the removal of the back buffer is delayed to prevent - // flash effects due to the animation of tile displaying - this.backBufferTimerId = window.setTimeout( - this._removeBackBuffer, this.removeBackBufferDelay - ); - } - } - this.loading = false; - this.events.triggerEvent("loadend"); - } - }; - - tile.onLoadError = function() { - this.events.triggerEvent("tileerror", {tile: tile}); - }; - - tile.events.on({ - "loadstart": tile.onLoadStart, - "loadend": tile.onLoadEnd, - "unload": tile.onLoadEnd, - "loaderror": tile.onLoadError, - scope: this - }); - }, - - /** - * 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, - "loaderror": tile.onLoadError, - scope: this - }); - }, - - /** - * Method: moveGriddedTiles - */ - moveGriddedTiles: function() { - var buffer = this.buffer + 1; - while(true) { - var tlTile = this.grid[0][0]; - var tlViewPort = { - x: tlTile.position.x + - this.map.layerContainerOriginPx.x, - y: tlTile.position.y + - this.map.layerContainerOriginPx.y - }; - var ratio = this.getServerResolution() / this.map.getResolution(); - var tileSize = { - w: Math.round(this.tileSize.w * ratio), - h: Math.round(this.tileSize.h * ratio) - }; - if (tlViewPort.x > -tileSize.w * (buffer - 1)) { - this.shiftColumn(true, tileSize); - } else if (tlViewPort.x < -tileSize.w * buffer) { - this.shiftColumn(false, tileSize); - } else if (tlViewPort.y > -tileSize.h * (buffer - 1)) { - this.shiftRow(true, tileSize); - } else if (tlViewPort.y < -tileSize.h * buffer) { - this.shiftRow(false, tileSize); - } else { - break; - } - } - }, - - /** - * Method: shiftRow - * Shifty grid work - * - * Parameters: - * prepend - {Boolean} if true, prepend to beginning. - * if false, then append to end - * tileSize - {Object} rendered tile size; object with w and h properties - */ - shiftRow: function(prepend, tileSize) { - var grid = this.grid; - var rowIndex = prepend ? 0 : (grid.length - 1); - var sign = prepend ? -1 : 1; - var rowSign = this.rowSign; - var tileLayout = this.gridLayout; - tileLayout.startrow += sign * rowSign; - - var modelRow = grid[rowIndex]; - var row = grid[prepend ? 'pop' : 'shift'](); - for (var i=0, len=row.length; i<len; i++) { - var tile = row[i]; - var position = modelRow[i].position.clone(); - position.y += tileSize.h * sign; - tile.moveTo(this.getTileBoundsForGridIndex(rowIndex, i), position); - } - grid[prepend ? 'unshift' : 'push'](row); - }, - - /** - * Method: shiftColumn - * Shift grid work in the other dimension - * - * Parameters: - * prepend - {Boolean} if true, prepend to beginning. - * if false, then append to end - * tileSize - {Object} rendered tile size; object with w and h properties - */ - shiftColumn: function(prepend, tileSize) { - var grid = this.grid; - var colIndex = prepend ? 0 : (grid[0].length - 1); - var sign = prepend ? -1 : 1; - var tileLayout = this.gridLayout; - tileLayout.startcol += sign; - - for (var i=0, len=grid.length; i<len; i++) { - var row = grid[i]; - var position = row[colIndex].position.clone(); - var tile = row[prepend ? 'pop' : 'shift'](); - position.x += tileSize.w * sign; - tile.moveTo(this.getTileBoundsForGridIndex(i, colIndex), position); - row[prepend ? 'unshift' : 'push'](tile); - } - }, - - /** - * Method: removeExcessTiles - * When the size of the map or the buffer changes, we may need to - * remove some excess rows and columns. - * - * Parameters: - * rows - {Integer} Maximum number of rows we want our grid to have. - * columns - {Integer} Maximum number of columns we want our grid to have. - */ - removeExcessTiles: function(rows, columns) { - var i, l; - - // remove extra rows - while (this.grid.length > rows) { - var row = this.grid.pop(); - for (i=0, l=row.length; i<l; i++) { - var tile = row[i]; - this.destroyTile(tile); - } - } - - // remove extra columns - for (i=0, l=this.grid.length; i<l; i++) { - while (this.grid[i].length > columns) { - var row = this.grid[i]; - var tile = row.pop(); - this.destroyTile(tile); - } - } - }, - - /** - * Method: onMapResize - * For singleTile layers, this will set a new tile size according to the - * dimensions of the map pane. - */ - onMapResize: function() { - if (this.singleTile) { - this.clearGrid(); - this.setTileSize(); - } - }, - - /** - * 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 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/Layer/XYZ.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @requires OpenLayers/Layer/Grid.js - */ - -/** - * Class: OpenLayers.Layer.XYZ - * The XYZ class is designed to make it easier for people who have tiles - * arranged by a standard XYZ grid. - * - * Inherits from: - * - <OpenLayers.Layer.Grid> - */ -OpenLayers.Layer.XYZ = OpenLayers.Class(OpenLayers.Layer.Grid, { - - /** - * APIProperty: isBaseLayer - * Default is true, as this is designed to be a base tile source. - */ - isBaseLayer: true, - - /** - * APIProperty: sphericalMercator - * Whether the tile extents should be set to the defaults for - * spherical mercator. Useful for things like OpenStreetMap. - * Default is false, except for the OSM subclass. - */ - sphericalMercator: false, - - /** - * APIProperty: zoomOffset - * {Number} If your cache has more zoom levels than you want to provide - * access to with this layer, supply a zoomOffset. This zoom offset - * is added to the current map zoom level to determine the level - * for a requested tile. For example, if you supply a zoomOffset - * of 3, when the map is at the zoom 0, tiles will be requested from - * level 3 of your cache. Default is 0 (assumes cache level and map - * zoom are equivalent). Using <zoomOffset> is an alternative to - * setting <serverResolutions> if you only want to expose a subset - * of the server resolutions. - */ - zoomOffset: 0, - - /** - * APIProperty: serverResolutions - * {Array} A list of all resolutions available on the server. Only set this - * property if the map resolutions differ from the server. This - * property serves two purposes. (a) <serverResolutions> can include - * resolutions that the server supports and that you don't want to - * provide with this layer; you can also look at <zoomOffset>, which is - * an alternative to <serverResolutions> for that specific purpose. - * (b) The map can work with resolutions that aren't supported by - * the server, i.e. that aren't in <serverResolutions>. When the - * map is displayed in such a resolution data for the closest - * server-supported resolution is loaded and the layer div is - * stretched as necessary. - */ - serverResolutions: null, - - /** - * Constructor: OpenLayers.Layer.XYZ - * - * Parameters: - * name - {String} - * url - {String} - * options - {Object} Hashtable of extra options to tag onto the layer - */ - initialize: function(name, url, options) { - if (options && options.sphericalMercator || this.sphericalMercator) { - options = OpenLayers.Util.extend({ - projection: "EPSG:900913", - numZoomLevels: 19 - }, options); - } - OpenLayers.Layer.Grid.prototype.initialize.apply(this, [ - name || this.name, url || this.url, {}, options - ]); - }, - - /** - * APIMethod: clone - * Create a clone of this layer - * - * Parameters: - * obj - {Object} Is this ever used? - * - * Returns: - * {<OpenLayers.Layer.XYZ>} An exact clone of this OpenLayers.Layer.XYZ - */ - clone: function (obj) { - - if (obj == null) { - obj = new OpenLayers.Layer.XYZ(this.name, - this.url, - this.getOptions()); - } - - //get all additions from superclasses - obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); - - 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 xyz = this.getXYZ(bounds); - var url = this.url; - if (OpenLayers.Util.isArray(url)) { - var s = '' + xyz.x + xyz.y + xyz.z; - url = this.selectUrl(s, url); - } - - return OpenLayers.String.format(url, xyz); - }, - - /** - * Method: getXYZ - * Calculates x, y and z for the given bounds. - * - * Parameters: - * bounds - {<OpenLayers.Bounds>} - * - * Returns: - * {Object} - an object with x, y and z properties. - */ - getXYZ: function(bounds) { - var res = this.getServerResolution(); - var x = Math.round((bounds.left - this.maxExtent.left) / - (res * this.tileSize.w)); - var y = Math.round((this.maxExtent.top - bounds.top) / - (res * this.tileSize.h)); - var z = this.getServerZoom(); - - if (this.wrapDateLine) { - var limit = Math.pow(2, z); - x = ((x % limit) + limit) % limit; - } - - return {'x': x, 'y': y, 'z': z}; - }, - - /* 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.maxExtent.left, - this.maxExtent.bottom); - } - }, - - CLASS_NAME: "OpenLayers.Layer.XYZ" -}); -/* ====================================================================== - OpenLayers/Layer/OSM.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @requires OpenLayers/Layer/XYZ.js - */ - -/** - * Class: OpenLayers.Layer.OSM - * This layer allows accessing OpenStreetMap tiles. By default the OpenStreetMap - * hosted tile.openstreetmap.org Mapnik tileset is used. If you wish to use - * a different layer instead, you need to provide a different - * URL to the constructor. Here's an example for using OpenCycleMap: - * - * (code) - * new OpenLayers.Layer.OSM("OpenCycleMap", - * ["http://a.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png", - * "http://b.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png", - * "http://c.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png"]); - * (end) - * - * Inherits from: - * - <OpenLayers.Layer.XYZ> - */ -OpenLayers.Layer.OSM = OpenLayers.Class(OpenLayers.Layer.XYZ, { - - /** - * APIProperty: name - * {String} The layer name. Defaults to "OpenStreetMap" if the first - * argument to the constructor is null or undefined. - */ - name: "OpenStreetMap", - - /** - * APIProperty: url - * {String} The tileset URL scheme. Defaults to - * : http://[a|b|c].tile.openstreetmap.org/${z}/${x}/${y}.png - * (the official OSM tileset) if the second argument to the constructor - * is null or undefined. To use another tileset you can have something - * like this: - * (code) - * new OpenLayers.Layer.OSM("OpenCycleMap", - * ["http://a.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png", - * "http://b.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png", - * "http://c.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png"]); - * (end) - */ - url: [ - 'http://a.tile.openstreetmap.org/${z}/${x}/${y}.png', - 'http://b.tile.openstreetmap.org/${z}/${x}/${y}.png', - 'http://c.tile.openstreetmap.org/${z}/${x}/${y}.png' - ], - - /** - * Property: attribution - * {String} The layer attribution. - */ - attribution: "© <a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors", - - /** - * Property: sphericalMercator - * {Boolean} - */ - sphericalMercator: true, - - /** - * Property: wrapDateLine - * {Boolean} - */ - wrapDateLine: true, - - /** APIProperty: tileOptions - * {Object} optional configuration options for <OpenLayers.Tile> instances - * created by this Layer. Default is - * - * (code) - * {crossOriginKeyword: 'anonymous'} - * (end) - * - * When using OSM tilesets other than the default ones, it may be - * necessary to set this to - * - * (code) - * {crossOriginKeyword: null} - * (end) - * - * if the server does not send Access-Control-Allow-Origin headers. - */ - tileOptions: null, - - /** - * Constructor: OpenLayers.Layer.OSM - * - * Parameters: - * name - {String} The layer name. - * url - {String} The tileset URL scheme. - * options - {Object} Configuration options for the layer. Any inherited - * layer option can be set in this object (e.g. - * <OpenLayers.Layer.Grid.buffer>). - */ - initialize: function(name, url, options) { - OpenLayers.Layer.XYZ.prototype.initialize.apply(this, arguments); - this.tileOptions = OpenLayers.Util.extend({ - crossOriginKeyword: 'anonymous' - }, this.options && this.options.tileOptions); - }, - - /** - * Method: clone - */ - clone: function(obj) { - if (obj == null) { - obj = new OpenLayers.Layer.OSM( - this.name, this.url, this.getOptions()); - } - obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]); - return obj; - }, - - CLASS_NAME: "OpenLayers.Layer.OSM" -}); -/* ====================================================================== - OpenLayers/Layer/Bing.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @requires OpenLayers/Layer/XYZ.js - */ - -/** - * Class: OpenLayers.Layer.Bing - * Bing layer using direct tile access as provided by Bing Maps REST Services. - * See http://msdn.microsoft.com/en-us/library/ff701713.aspx for more - * information. Note: Terms of Service compliant use requires the map to be - * configured with an <OpenLayers.Control.Attribution> control and the - * attribution placed on or near the map. - * - * Inherits from: - * - <OpenLayers.Layer.XYZ> - */ -OpenLayers.Layer.Bing = OpenLayers.Class(OpenLayers.Layer.XYZ, { - - /** - * Property: key - * {String} API key for Bing maps, get your own key - * at http://bingmapsportal.com/ . - */ - key: null, - - /** - * Property: serverResolutions - * {Array} the resolutions provided by the Bing servers. - */ - serverResolutions: [ - 156543.03390625, 78271.516953125, 39135.7584765625, - 19567.87923828125, 9783.939619140625, 4891.9698095703125, - 2445.9849047851562, 1222.9924523925781, 611.4962261962891, - 305.74811309814453, 152.87405654907226, 76.43702827453613, - 38.218514137268066, 19.109257068634033, 9.554628534317017, - 4.777314267158508, 2.388657133579254, 1.194328566789627, - 0.5971642833948135, 0.29858214169740677, 0.14929107084870338, - 0.07464553542435169 - ], - - /** - * Property: attributionTemplate - * {String} - */ - attributionTemplate: '<span class="olBingAttribution ${type}">' + - '<div><a target="_blank" href="http://www.bing.com/maps/">' + - '<img src="${logo}" /></a></div>${copyrights}' + - '<a style="white-space: nowrap" target="_blank" '+ - 'href="http://www.microsoft.com/maps/product/terms.html">' + - 'Terms of Use</a></span>', - - /** - * Property: metadata - * {Object} Metadata for this layer, as returned by the callback script - */ - metadata: null, - - /** - * Property: protocolRegex - * {RegExp} Regular expression to match and replace http: in bing urls - */ - protocolRegex: /^http:/i, - - /** - * APIProperty: type - * {String} The layer identifier. Any non-birdseye imageryType - * from http://msdn.microsoft.com/en-us/library/ff701716.aspx can be - * used. Default is "Road". - */ - type: "Road", - - /** - * APIProperty: culture - * {String} The culture identifier. See http://msdn.microsoft.com/en-us/library/ff701709.aspx - * for the definition and the possible values. Default is "en-US". - */ - culture: "en-US", - - /** - * APIProperty: metadataParams - * {Object} Optional url parameters for the Get Imagery Metadata request - * as described here: http://msdn.microsoft.com/en-us/library/ff701716.aspx - */ - metadataParams: null, - - /** APIProperty: tileOptions - * {Object} optional configuration options for <OpenLayers.Tile> instances - * created by this Layer. Default is - * - * (code) - * {crossOriginKeyword: 'anonymous'} - * (end) - */ - tileOptions: null, - - /** APIProperty: protocol - * {String} Protocol to use to fetch Imagery Metadata, tiles and bing logo - * Can be 'http:' 'https:' or '' - * - * Warning: tiles may not be available under both HTTP and HTTPS protocols. - * Microsoft approved use of both HTTP and HTTPS urls for tiles. However - * this is undocumented and the Imagery Metadata API always returns HTTP - * urls. - * - * Default is '', unless when executed from a file:/// uri, in which case - * it is 'http:'. - */ - protocol: ~window.location.href.indexOf('http') ? '' : 'http:', - - /** - * Constructor: OpenLayers.Layer.Bing - * Create a new Bing layer. - * - * Example: - * (code) - * var road = new OpenLayers.Layer.Bing({ - * name: "My Bing Aerial Layer", - * type: "Aerial", - * key: "my-api-key-here", - * }); - * (end) - * - * Parameters: - * options - {Object} Configuration properties for the layer. - * - * Required configuration properties: - * key - {String} Bing Maps API key for your application. Get one at - * http://bingmapsportal.com/. - * type - {String} The layer identifier. Any non-birdseye imageryType - * from http://msdn.microsoft.com/en-us/library/ff701716.aspx can be - * used. - * - * Any other documented layer properties can be provided in the config object. - */ - initialize: function(options) { - options = OpenLayers.Util.applyDefaults({ - sphericalMercator: true - }, options); - var name = options.name || "Bing " + (options.type || this.type); - - var newArgs = [name, null, options]; - OpenLayers.Layer.XYZ.prototype.initialize.apply(this, newArgs); - this.tileOptions = OpenLayers.Util.extend({ - crossOriginKeyword: 'anonymous' - }, this.options.tileOptions); - this.loadMetadata(); - }, - - /** - * Method: loadMetadata - */ - loadMetadata: function() { - this._callbackId = "_callback_" + this.id.replace(/\./g, "_"); - // link the processMetadata method to the global scope and bind it - // to this instance - window[this._callbackId] = OpenLayers.Function.bind( - OpenLayers.Layer.Bing.processMetadata, this - ); - var params = OpenLayers.Util.applyDefaults({ - key: this.key, - jsonp: this._callbackId, - include: "ImageryProviders" - }, this.metadataParams); - var url = this.protocol + "//dev.virtualearth.net/REST/v1/Imagery/Metadata/" + - this.type + "?" + OpenLayers.Util.getParameterString(params); - var script = document.createElement("script"); - script.type = "text/javascript"; - script.src = url; - script.id = this._callbackId; - document.getElementsByTagName("head")[0].appendChild(script); - }, - - /** - * Method: initLayer - * - * Sets layer properties according to the metadata provided by the API - */ - initLayer: function() { - var res = this.metadata.resourceSets[0].resources[0]; - var url = res.imageUrl.replace("{quadkey}", "${quadkey}"); - url = url.replace("{culture}", this.culture); - url = url.replace(this.protocolRegex, this.protocol); - this.url = []; - for (var i=0; i<res.imageUrlSubdomains.length; ++i) { - this.url.push(url.replace("{subdomain}", res.imageUrlSubdomains[i])); - } - this.addOptions({ - maxResolution: Math.min( - this.serverResolutions[res.zoomMin], - this.maxResolution || Number.POSITIVE_INFINITY - ), - numZoomLevels: Math.min( - res.zoomMax + 1 - res.zoomMin, this.numZoomLevels - ) - }, true); - if (!this.isBaseLayer) { - this.redraw(); - } - this.updateAttribution(); - }, - - /** - * Method: getURL - * - * Paramters: - * bounds - {<OpenLayers.Bounds>} - */ - getURL: function(bounds) { - if (!this.url) { - return; - } - var xyz = this.getXYZ(bounds), x = xyz.x, y = xyz.y, z = xyz.z; - var quadDigits = []; - for (var i = z; i > 0; --i) { - var digit = '0'; - var mask = 1 << (i - 1); - if ((x & mask) != 0) { - digit++; - } - if ((y & mask) != 0) { - digit++; - digit++; - } - quadDigits.push(digit); - } - var quadKey = quadDigits.join(""); - var url = this.selectUrl('' + x + y + z, this.url); - - return OpenLayers.String.format(url, {'quadkey': quadKey}); - }, - - /** - * Method: updateAttribution - * Updates the attribution according to the requirements outlined in - * http://gis.638310.n2.nabble.com/Bing-imagery-td5789168.html - */ - updateAttribution: function() { - var metadata = this.metadata; - if (!metadata.resourceSets || !this.map || !this.map.center) { - return; - } - var res = metadata.resourceSets[0].resources[0]; - var extent = this.map.getExtent().transform( - this.map.getProjectionObject(), - new OpenLayers.Projection("EPSG:4326") - ); - var providers = res.imageryProviders || [], - zoom = OpenLayers.Util.indexOf(this.serverResolutions, - this.getServerResolution()), - copyrights = "", provider, i, ii, j, jj, bbox, coverage; - for (i=0,ii=providers.length; i<ii; ++i) { - provider = providers[i]; - for (j=0,jj=provider.coverageAreas.length; j<jj; ++j) { - coverage = provider.coverageAreas[j]; - // axis order provided is Y,X - bbox = OpenLayers.Bounds.fromArray(coverage.bbox, true); - if (extent.intersectsBounds(bbox) && - zoom <= coverage.zoomMax && zoom >= coverage.zoomMin) { - copyrights += provider.attribution + " "; - } - } - } - var logo = metadata.brandLogoUri.replace(this.protocolRegex, this.protocol); - this.attribution = OpenLayers.String.format(this.attributionTemplate, { - type: this.type.toLowerCase(), - logo: logo, - copyrights: copyrights - }); - this.map && this.map.events.triggerEvent("changelayer", { - layer: this, - property: "attribution" - }); - }, - - /** - * Method: setMap - */ - setMap: function() { - OpenLayers.Layer.XYZ.prototype.setMap.apply(this, arguments); - this.map.events.register("moveend", this, this.updateAttribution); - }, - - /** - * APIMethod: clone - * - * Parameters: - * obj - {Object} - * - * Returns: - * {<OpenLayers.Layer.Bing>} An exact clone of this <OpenLayers.Layer.Bing> - */ - clone: function(obj) { - if (obj == null) { - obj = new OpenLayers.Layer.Bing(this.options); - } - //get all additions from superclasses - obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]); - // copy/set any non-init, non-simple values here - return obj; - }, - - /** - * Method: destroy - */ - destroy: function() { - this.map && - this.map.events.unregister("moveend", this, this.updateAttribution); - OpenLayers.Layer.XYZ.prototype.destroy.apply(this, arguments); - }, - - CLASS_NAME: "OpenLayers.Layer.Bing" -}); - -/** - * Function: OpenLayers.Layer.Bing.processMetadata - * This function will be bound to an instance, linked to the global scope with - * an id, and called by the JSONP script returned by the API. - * - * Parameters: - * metadata - {Object} metadata as returned by the API - */ -OpenLayers.Layer.Bing.processMetadata = function(metadata) { - this.metadata = metadata; - this.initLayer(); - var script = document.getElementById(this._callbackId); - script.parentNode.removeChild(script); - window[this._callbackId] = undefined; // cannot delete from window in IE - delete this._callbackId; -}; -/* ====================================================================== - OpenLayers/Handler.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @requires OpenLayers/BaseTypes/Class.js - * @requires OpenLayers/Events.js - */ - -/** - * Class: OpenLayers.Handler - * Base class to construct a higher-level handler for event sequences. All - * handlers have activate and deactivate methods. In addition, they have - * methods named like browser events. When a handler is activated, any - * additional methods named like a browser event is registered as a - * listener for the corresponding event. When a handler is deactivated, - * those same methods are unregistered as event listeners. - * - * Handlers also typically have a callbacks object with keys named like - * the abstracted events or event sequences that they are in charge of - * handling. The controls that wrap handlers define the methods that - * correspond to these abstract events - so instead of listening for - * individual browser events, they only listen for the abstract events - * defined by the handler. - * - * Handlers are created by controls, which ultimately have the responsibility - * of making changes to the the state of the application. Handlers - * themselves may make temporary changes, but in general are expected to - * return the application in the same state that they found it. - */ -OpenLayers.Handler = OpenLayers.Class({ - - /** - * Property: id - * {String} - */ - id: null, - - /** - * APIProperty: 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. - */ - control: null, - - /** - * Property: map - * {<OpenLayers.Map>} - */ - map: null, - - /** - * APIProperty: keyMask - * {Integer} Use bitwise operators and one or more of the OpenLayers.Handler - * constants to construct a keyMask. The keyMask is used by - * <checkModifiers>. If the keyMask matches the combination of keys - * down on an event, checkModifiers returns true. - * - * Example: - * (code) - * // handler only responds if the Shift key is down - * handler.keyMask = OpenLayers.Handler.MOD_SHIFT; - * - * // handler only responds if Ctrl-Shift is down - * handler.keyMask = OpenLayers.Handler.MOD_SHIFT | - * OpenLayers.Handler.MOD_CTRL; - * (end) - */ - keyMask: null, - - /** - * Property: active - * {Boolean} - */ - active: false, - - /** - * Property: evt - * {Event} This property references the last event handled by the handler. - * Note that this property is not part of the stable API. Use of the - * evt property should be restricted to controls in the library - * or other applications that are willing to update with changes to - * the OpenLayers code. - */ - evt: null, - - /** - * Property: touch - * {Boolean} Indicates the support of touch events. When touch events are - * started touch will be true and all mouse related listeners will do - * nothing. - */ - touch: false, - - /** - * Constructor: OpenLayers.Handler - * Construct a 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. If a map property - * is present in the options argument it will be used instead. - * 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.Util.extend(this, options); - this.control = control; - this.callbacks = callbacks; - - var map = this.map || control.map; - if (map) { - this.setMap(map); - } - - this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_"); - }, - - /** - * Method: setMap - */ - setMap: function (map) { - this.map = map; - }, - - /** - * Method: checkModifiers - * Check the keyMask on the handler. If no <keyMask> is set, this always - * returns true. If a <keyMask> is set and it matches the combination - * of keys down on an event, this returns true. - * - * Returns: - * {Boolean} The keyMask matches the keys down on an event. - */ - checkModifiers: function (evt) { - if(this.keyMask == null) { - return true; - } - /* calculate the keyboard modifier mask for this event */ - var keyModifiers = - (evt.shiftKey ? OpenLayers.Handler.MOD_SHIFT : 0) | - (evt.ctrlKey ? OpenLayers.Handler.MOD_CTRL : 0) | - (evt.altKey ? OpenLayers.Handler.MOD_ALT : 0) | - (evt.metaKey ? OpenLayers.Handler.MOD_META : 0); - - /* if it differs from the handler object's key mask, - bail out of the event handler */ - return (keyModifiers == this.keyMask); - }, - - /** - * APIMethod: activate - * Turn on the handler. Returns false if the handler was already active. - * - * Returns: - * {Boolean} The handler was activated. - */ - activate: function() { - if(this.active) { - return false; - } - // register for event handlers defined on this class. - 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; - }, - - /** - * APIMethod: deactivate - * Turn off the handler. Returns false if the handler was already inactive. - * - * Returns: - * {Boolean} The handler was deactivated. - */ - deactivate: function() { - if(!this.active) { - return false; - } - // unregister event handlers defined on this class. - 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.touch = false; - this.active = false; - return true; - }, - - /** - * Method: startTouch - * Start touch events, this method must be called by subclasses in - * "touchstart" method. When touch events are started <touch> will be - * true and all mouse related listeners will do nothing. - */ - startTouch: function() { - if (!this.touch) { - this.touch = true; - var events = [ - "mousedown", "mouseup", "mousemove", "click", "dblclick", - "mouseout" - ]; - for (var i=0, len=events.length; i<len; i++) { - if (this[events[i]]) { - this.unregister(events[i], this[events[i]]); - } - } - } - }, - - /** - * 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 (any type) with which to call - * the callback (defined by the control). - */ - callback: function (name, args) { - if (name && this.callbacks[name]) { - this.callbacks[name].apply(this.control, args); - } - }, - - /** - * Method: register - * register an event on the map - */ - register: function (name, method) { - // TODO: deal with registerPriority in 3.0 - this.map.events.registerPriority(name, this, method); - this.map.events.registerPriority(name, this, this.setEvent); - }, - - /** - * Method: unregister - * unregister an event from the map - */ - unregister: function (name, method) { - this.map.events.unregister(name, this, method); - this.map.events.unregister(name, this, this.setEvent); - }, - - /** - * Method: setEvent - * With each registered browser event, the handler sets its own evt - * property. This property can be accessed by controls if needed - * to get more information about the event that the handler is - * processing. - * - * This allows modifier keys on the event to be checked (alt, shift, ctrl, - * and meta cannot be checked with the keyboard handler). For a - * control to determine which modifier keys are associated with the - * event that a handler is currently processing, it should access - * (code)handler.evt.altKey || handler.evt.shiftKey || - * handler.evt.ctrlKey || handler.evt.metaKey(end). - * - * Parameters: - * evt - {Event} The browser event. - */ - setEvent: function(evt) { - this.evt = evt; - return true; - }, - - /** - * Method: destroy - * Deconstruct the handler. - */ - destroy: function () { - // unregister event listeners - this.deactivate(); - // eliminate circular references - this.control = this.map = null; - }, - - CLASS_NAME: "OpenLayers.Handler" -}); - -/** - * Constant: OpenLayers.Handler.MOD_NONE - * If set as the <keyMask>, <checkModifiers> returns false if any key is down. - */ -OpenLayers.Handler.MOD_NONE = 0; - -/** - * Constant: OpenLayers.Handler.MOD_SHIFT - * If set as the <keyMask>, <checkModifiers> returns false if Shift is down. - */ -OpenLayers.Handler.MOD_SHIFT = 1; - -/** - * Constant: OpenLayers.Handler.MOD_CTRL - * If set as the <keyMask>, <checkModifiers> returns false if Ctrl is down. - */ -OpenLayers.Handler.MOD_CTRL = 2; - -/** - * Constant: OpenLayers.Handler.MOD_ALT - * If set as the <keyMask>, <checkModifiers> returns false if Alt is down. - */ -OpenLayers.Handler.MOD_ALT = 4; - -/** - * Constant: OpenLayers.Handler.MOD_META - * If set as the <keyMask>, <checkModifiers> returns false if Cmd is down. - */ -OpenLayers.Handler.MOD_META = 8; - - -/* ====================================================================== - OpenLayers/Handler/MouseWheel.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @requires OpenLayers/Handler.js - */ - -/** - * Class: OpenLayers.Handler.MouseWheel - * Handler for wheel up/down events. - * - * Inherits from: - * - <OpenLayers.Handler> - */ -OpenLayers.Handler.MouseWheel = OpenLayers.Class(OpenLayers.Handler, { - /** - * Property: wheelListener - * {function} - */ - wheelListener: null, - - /** - * Property: interval - * {Integer} In order to increase server performance, an interval (in - * milliseconds) can be set to reduce the number of up/down events - * called. If set, a new up/down event will not be set until the - * interval has passed. - * Defaults to 0, meaning no interval. - */ - interval: 0, - - /** - * Property: maxDelta - * {Integer} Maximum delta to collect before breaking from the current - * interval. In cumulative mode, this also limits the maximum delta - * returned from the handler. Default is Number.POSITIVE_INFINITY. - */ - maxDelta: Number.POSITIVE_INFINITY, - - /** - * Property: delta - * {Integer} When interval is set, delta collects the mousewheel z-deltas - * of the events that occur within the interval. - * See also the cumulative option - */ - delta: 0, - - /** - * Property: cumulative - * {Boolean} When interval is set: true to collect all the mousewheel - * z-deltas, false to only record the delta direction (positive or - * negative) - */ - cumulative: true, - - /** - * Constructor: OpenLayers.Handler.MouseWheel - * - * Parameters: - * control - {<OpenLayers.Control>} - * 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 point geometry. - * options - {Object} - */ - initialize: function(control, callbacks, options) { - OpenLayers.Handler.prototype.initialize.apply(this, arguments); - this.wheelListener = OpenLayers.Function.bindAsEventListener( - this.onWheelEvent, this - ); - }, - - /** - * Method: destroy - */ - destroy: function() { - OpenLayers.Handler.prototype.destroy.apply(this, arguments); - this.wheelListener = 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){ - - // make sure we have a map and check keyboard modifiers - if (!this.map || !this.checkModifiers(e)) { - return; - } - - // Ride up the element's DOM hierarchy to determine if it or any of - // its ancestors was: - // * specifically marked as scrollable (CSS overflow property) - // * one of our layer divs or a div marked as scrollable - // ('olScrollable' CSS class) - // * the map div - // - var overScrollableDiv = false; - var allowScroll = false; - var overMapDiv = false; - - var elem = OpenLayers.Event.element(e); - while((elem != null) && !overMapDiv && !overScrollableDiv) { - - if (!overScrollableDiv) { - try { - var overflow; - if (elem.currentStyle) { - overflow = elem.currentStyle["overflow"]; - } else { - var style = - document.defaultView.getComputedStyle(elem, null); - overflow = style.getPropertyValue("overflow"); - } - overScrollableDiv = ( overflow && - (overflow == "auto") || (overflow == "scroll") ); - } catch(err) { - //sometimes when scrolling in a popup, this causes - // obscure browser error - } - } - - if (!allowScroll) { - allowScroll = OpenLayers.Element.hasClass(elem, 'olScrollable'); - if (!allowScroll) { - 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) - var layer = this.map.layers[i]; - if (elem == layer.div || elem == layer.pane) { - allowScroll = true; - break; - } - } - } - } - overMapDiv = (elem == this.map.div); - - elem = elem.parentNode; - } - - // Logic below is the following: - // - // If we are over a scrollable div or not over the map div: - // * do nothing (let the browser handle scrolling) - // - // otherwise - // - // If we are over the layer div or a 'olScrollable' div: - // * zoom/in out - // then - // * kill event (so as not to also scroll the page after zooming) - // - // otherwise - // - // Kill the event (dont scroll the page if we wheel over the - // layerswitcher or the pan/zoom control) - // - if (!overScrollableDiv && overMapDiv) { - if (allowScroll) { - var delta = 0; - - if (e.wheelDelta) { - delta = e.wheelDelta; - if (delta % 160 === 0) { - // opera have steps of 160 instead of 120 - delta = delta * 0.75; - } - delta = delta / 120; - } else if (e.detail) { - // detail in Firefox on OS X is 1/3 of Windows - // so force delta 1 / -1 - delta = - (e.detail / Math.abs(e.detail)); - } - this.delta += delta; - - window.clearTimeout(this._timeoutId); - if(this.interval && Math.abs(this.delta) < this.maxDelta) { - // store e because window.event might change during delay - var evt = OpenLayers.Util.extend({}, e); - this._timeoutId = window.setTimeout( - OpenLayers.Function.bind(function(){ - this.wheelZoom(evt); - }, this), - this.interval - ); - } else { - this.wheelZoom(e); - } - } - OpenLayers.Event.stop(e); - } - }, - - /** - * Method: wheelZoom - * Given the wheel event, we carry out the appropriate zooming in or out, - * based on the 'wheelDelta' or 'detail' property of the event. - * - * Parameters: - * e - {Event} - */ - wheelZoom: function(e) { - var delta = this.delta; - this.delta = 0; - - if (delta) { - e.xy = this.map.events.getMousePosition(e); - if (delta < 0) { - this.callback("down", - [e, this.cumulative ? Math.max(-this.maxDelta, delta) : -1]); - } else { - this.callback("up", - [e, this.cumulative ? Math.min(this.maxDelta, delta) : 1]); - } - } - }, - - /** - * Method: activate - */ - activate: function (evt) { - if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) { - //register mousewheel events specifically on the window and document - 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; - } - }, - - /** - * Method: deactivate - */ - deactivate: function (evt) { - if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) { - // unregister mousewheel events specifically on the window and document - 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/Geometry/MultiLineString.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @requires OpenLayers/Geometry/Collection.js - * @requires OpenLayers/Geometry/LineString.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>)} - * - */ - - /** - * Method: split - * Use this geometry (the source) to attempt to split a target geometry. - * - * Parameters: - * geometry - {<OpenLayers.Geometry>} The target geometry. - * options - {Object} Properties of this object will be used to determine - * how the split is conducted. - * - * Valid options: - * mutual - {Boolean} Split the source geometry in addition to the target - * geometry. Default is false. - * edge - {Boolean} Allow splitting when only edges intersect. Default is - * true. If false, a vertex on the source must be within the tolerance - * distance of the intersection to be considered a split. - * tolerance - {Number} If a non-null value is provided, intersections - * within the tolerance distance of an existing vertex on the source - * will be assumed to occur at the vertex. - * - * Returns: - * {Array} A list of geometries (of this same type as the target) that - * result from splitting the target with the source geometry. The - * source and target geometry will remain unmodified. If no split - * results, null will be returned. If mutual is true and a split - * results, return will be an array of two arrays - the first will be - * all geometries that result from splitting the source geometry and - * the second will be all geometries that result from splitting the - * target geometry. - */ - split: function(geometry, options) { - var results = null; - var mutual = options && options.mutual; - var splits, sourceLine, sourceLines, sourceSplit, targetSplit; - var sourceParts = []; - var targetParts = [geometry]; - for(var i=0, len=this.components.length; i<len; ++i) { - sourceLine = this.components[i]; - sourceSplit = false; - for(var j=0; j < targetParts.length; ++j) { - splits = sourceLine.split(targetParts[j], options); - if(splits) { - if(mutual) { - sourceLines = splits[0]; - for(var k=0, klen=sourceLines.length; k<klen; ++k) { - if(k===0 && sourceParts.length) { - sourceParts[sourceParts.length-1].addComponent( - sourceLines[k] - ); - } else { - sourceParts.push( - new OpenLayers.Geometry.MultiLineString([ - sourceLines[k] - ]) - ); - } - } - sourceSplit = true; - splits = splits[1]; - } - if(splits.length) { - // splice in new target parts - splits.unshift(j, 1); - Array.prototype.splice.apply(targetParts, splits); - break; - } - } - } - if(!sourceSplit) { - // source line was not hit - if(sourceParts.length) { - // add line to existing multi - sourceParts[sourceParts.length-1].addComponent( - sourceLine.clone() - ); - } else { - // create a fresh multi - sourceParts = [ - new OpenLayers.Geometry.MultiLineString( - sourceLine.clone() - ) - ]; - } - } - } - if(sourceParts && sourceParts.length > 1) { - sourceSplit = true; - } else { - sourceParts = []; - } - if(targetParts && targetParts.length > 1) { - targetSplit = true; - } else { - targetParts = []; - } - if(sourceSplit || targetSplit) { - if(mutual) { - results = [sourceParts, targetParts]; - } else { - results = targetParts; - } - } - return results; - }, - - /** - * Method: splitWith - * Split this geometry (the target) with the given geometry (the source). - * - * Parameters: - * geometry - {<OpenLayers.Geometry>} A geometry used to split this - * geometry (the source). - * options - {Object} Properties of this object will be used to determine - * how the split is conducted. - * - * Valid options: - * mutual - {Boolean} Split the source geometry in addition to the target - * geometry. Default is false. - * edge - {Boolean} Allow splitting when only edges intersect. Default is - * true. If false, a vertex on the source must be within the tolerance - * distance of the intersection to be considered a split. - * tolerance - {Number} If a non-null value is provided, intersections - * within the tolerance distance of an existing vertex on the source - * will be assumed to occur at the vertex. - * - * Returns: - * {Array} A list of geometries (of this same type as the target) that - * result from splitting the target with the source geometry. The - * source and target geometry will remain unmodified. If no split - * results, null will be returned. If mutual is true and a split - * results, return will be an array of two arrays - the first will be - * all geometries that result from splitting the source geometry and - * the second will be all geometries that result from splitting the - * target geometry. - */ - splitWith: function(geometry, options) { - var results = null; - var mutual = options && options.mutual; - var splits, targetLine, sourceLines, sourceSplit, targetSplit, sourceParts, targetParts; - if(geometry instanceof OpenLayers.Geometry.LineString) { - targetParts = []; - sourceParts = [geometry]; - for(var i=0, len=this.components.length; i<len; ++i) { - targetSplit = false; - targetLine = this.components[i]; - for(var j=0; j<sourceParts.length; ++j) { - splits = sourceParts[j].split(targetLine, options); - if(splits) { - if(mutual) { - sourceLines = splits[0]; - if(sourceLines.length) { - // splice in new source parts - sourceLines.unshift(j, 1); - Array.prototype.splice.apply(sourceParts, sourceLines); - j += sourceLines.length - 2; - } - splits = splits[1]; - if(splits.length === 0) { - splits = [targetLine.clone()]; - } - } - for(var k=0, klen=splits.length; k<klen; ++k) { - if(k===0 && targetParts.length) { - targetParts[targetParts.length-1].addComponent( - splits[k] - ); - } else { - targetParts.push( - new OpenLayers.Geometry.MultiLineString([ - splits[k] - ]) - ); - } - } - targetSplit = true; - } - } - if(!targetSplit) { - // target component was not hit - if(targetParts.length) { - // add it to any existing multi-line - targetParts[targetParts.length-1].addComponent( - targetLine.clone() - ); - } else { - // or start with a fresh multi-line - targetParts = [ - new OpenLayers.Geometry.MultiLineString([ - targetLine.clone() - ]) - ]; - } - - } - } - } else { - results = geometry.split(this); - } - if(sourceParts && sourceParts.length > 1) { - sourceSplit = true; - } else { - sourceParts = []; - } - if(targetParts && targetParts.length > 1) { - targetSplit = true; - } else { - targetParts = []; - } - if(sourceSplit || targetSplit) { - if(mutual) { - results = [sourceParts, targetParts]; - } else { - results = targetParts; - } - } - return results; - }, - - CLASS_NAME: "OpenLayers.Geometry.MultiLineString" -}); -/* ====================================================================== - OpenLayers/Renderer.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @requires OpenLayers/BaseTypes/Class.js - */ - -/** - * 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: root - * {DOMElement} - */ - root: 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, - - /** - * Property: featureDx - * {Number} Feature offset in x direction. Will be calculated for and - * applied to the current feature while rendering (see - * <calculateFeatureDx>). - */ - featureDx: 0, - - /** - * 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); - OpenLayers.Util.extend(this, options); - }, - - /** - * 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} - * - * 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) { - this.extent = extent.clone(); - if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) { - var ratio = extent.getWidth() / this.map.getExtent().getWidth(), - extent = extent.scale(1 / ratio); - this.extent = extent.wrapDateLine(this.map.getMaxExtent()).scale(ratio); - } - if (resolutionChanged) { - this.resolution = null; - } - return true; - }, - - /** - * 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: - * {Float} 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) { - var bounds = feature.geometry.getBounds(); - if(bounds) { - var worldBounds; - if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) { - worldBounds = this.map.getMaxExtent(); - } - if (!bounds.intersectsBounds(this.extent, {worldBounds: worldBounds})) { - style = {display: "none"}; - } else { - this.calculateFeatureDx(bounds, worldBounds); - } - var rendered = this.drawGeometry(feature.geometry, style, feature.id); - if(style.display != "none" && style.label && rendered !== false) { - - var location = feature.geometry.getCentroid(); - if(style.labelXOffset || style.labelYOffset) { - var xOffset = isNaN(style.labelXOffset) ? 0 : style.labelXOffset; - var yOffset = isNaN(style.labelYOffset) ? 0 : style.labelYOffset; - var res = this.getResolution(); - location.move(xOffset*res, yOffset*res); - } - this.drawText(feature.id, style, location); - } else { - this.removeText(feature.id); - } - return rendered; - } - } - }, - - /** - * Method: calculateFeatureDx - * {Number} Calculates the feature offset in x direction. Looking at the - * center of the feature bounds and the renderer extent, we calculate how - * many world widths the two are away from each other. This distance is - * used to shift the feature as close as possible to the center of the - * current enderer extent, which ensures that the feature is visible in the - * current viewport. - * - * Parameters: - * bounds - {<OpenLayers.Bounds>} Bounds of the feature - * worldBounds - {<OpenLayers.Bounds>} Bounds of the world - */ - calculateFeatureDx: function(bounds, worldBounds) { - this.featureDx = 0; - if (worldBounds) { - var worldWidth = worldBounds.getWidth(), - rendererCenterX = (this.extent.left + this.extent.right) / 2, - featureCenterX = (bounds.left + bounds.right) / 2, - worldsAway = Math.round((featureCenterX - rendererCenterX) / worldWidth); - this.featureDx = worldsAway * worldWidth; - } - }, - - /** - * 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: drawText - * Function for drawing text labels. - * This method is only called by the renderer itself. - * - * Parameters: - * featureId - {String} - * style - - * location - {<OpenLayers.Geometry.Point>} - */ - drawText: function(featureId, style, location) {}, - - /** - * Method: removeText - * Function for removing text labels. - * This method is only called by the renderer itself. - * - * Parameters: - * featureId - {String} - */ - removeText: function(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 undefined. - */ - getFeatureIdFromEvent: function(evt) {}, - - /** - * Method: eraseFeatures - * This is called by the layer to erase features - * - * Parameters: - * features - {Array(<OpenLayers.Feature.Vector>)} - */ - eraseFeatures: function(features) { - if(!(OpenLayers.Util.isArray(features))) { - features = [features]; - } - for(var i=0, len=features.length; i<len; ++i) { - var feature = features[i]; - this.eraseGeometry(feature.geometry, feature.id); - this.removeText(feature.id); - } - }, - - /** - * Method: eraseGeometry - * Remove a geometry from the renderer (by id). - * virtual function. - * - * Parameters: - * geometry - {<OpenLayers.Geometry>} - * featureId - {String} - */ - eraseGeometry: function(geometry, featureId) {}, - - /** - * Method: moveRoot - * moves this renderer's root to a (different) renderer. - * To be implemented by subclasses that require a common renderer root for - * feature selection. - * - * Parameters: - * renderer - {<OpenLayers.Renderer>} target renderer for the moved root - */ - moveRoot: function(renderer) {}, - - /** - * Method: getRenderLayerId - * Gets the layer that this renderer's output appears on. If moveRoot was - * used, this will be different from the id of the layer containing the - * features rendered by this renderer. - * - * Returns: - * {String} the id of the output layer. - */ - getRenderLayerId: function() { - return this.container.id; - }, - - /** - * Method: applyDefaultSymbolizer - * - * Parameters: - * symbolizer - {Object} - * - * Returns: - * {Object} - */ - applyDefaultSymbolizer: function(symbolizer) { - var result = OpenLayers.Util.extend({}, - OpenLayers.Renderer.defaultSymbolizer); - if(symbolizer.stroke === false) { - delete result.strokeWidth; - delete result.strokeColor; - } - if(symbolizer.fill === false) { - delete result.fillColor; - } - OpenLayers.Util.extend(result, symbolizer); - return result; - }, - - CLASS_NAME: "OpenLayers.Renderer" -}); - -/** - * Constant: OpenLayers.Renderer.defaultSymbolizer - * {Object} Properties from this symbolizer will be applied to symbolizers - * with missing properties. This can also be used to set a global - * symbolizer default in OpenLayers. To be SLD 1.x compliant, add the - * following code before rendering any vector features: - * (code) - * OpenLayers.Renderer.defaultSymbolizer = { - * fillColor: "#808080", - * fillOpacity: 1, - * strokeColor: "#000000", - * strokeOpacity: 1, - * strokeWidth: 1, - * pointRadius: 3, - * graphicName: "square" - * }; - * (end) - */ -OpenLayers.Renderer.defaultSymbolizer = { - fillColor: "#000000", - strokeColor: "#000000", - strokeWidth: 2, - fillOpacity: 1, - strokeOpacity: 1, - pointRadius: 0, - labelAlign: 'cm' -}; - - - -/** - * 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/Renderer/Elements.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository 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.clear(); - }, - - /** - * 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. - return this.getNextElement(rightIndex); - }, - - /** - * 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; - } - }, - - /** - * APIMethod: getNextElement - * Get the next element in the order stack. - * - * Parameters: - * index - {Integer} The index of the current node in this.order. - * - * Returns: - * {DOMElement} the node following the index passed in, or - * null. - */ - getNextElement: function(index) { - var nextIndex = index + 1; - if (nextIndex < this.order.length) { - var nextElement = OpenLayers.Util.getElement(this.order[nextIndex]); - if (nextElement == undefined) { - nextElement = this.getNextElement(nextIndex); - } - return nextElement; - } else { - return null; - } - }, - - 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 result = nextNode._boundsBottom - newNode._boundsBottom; - 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: vectorRoot - * {DOMElement} - */ - vectorRoot: null, - - /** - * Property: textRoot - * {DOMElement} - */ - textRoot: null, - - /** - * Property: xmlns - * {String} - */ - xmlns: null, - - /** - * Property: xOffset - * {Number} Offset to apply to the renderer viewport translation in x - * direction. If the renderer extent's center is on the right of the - * dateline (i.e. exceeds the world bounds), we shift the viewport to the - * left by one world width. This avoids that features disappear from the - * map viewport. Because our dateline handling logic in other places - * ensures that extents crossing the dateline always have a center - * exceeding the world bounds on the left, we need this offset to make sure - * that the same is true for the renderer extent in pixel space as well. - */ - xOffset: 0, - - /** - * Property: rightOfDateLine - * {Boolean} Keeps track of the location of the map extent relative to the - * date line. The <setExtent> method compares this value (which is the one - * from the previous <setExtent> call) with the current position of the map - * extent relative to the date line and updates the xOffset when the extent - * has moved from one side of the date line to the other. - */ - - /** - * 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", - - /** - * Constant: LABEL_ID_SUFFIX - * {String} - */ - LABEL_ID_SUFFIX: "_label", - - /** - * Constant: LABEL_OUTLINE_SUFFIX - * {String} - */ - LABEL_OUTLINE_SUFFIX: "_outline", - - /** - * 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("_root"); - this.vectorRoot = this.createRoot("_vroot"); - this.textRoot = this.createRoot("_troot"); - - this.root.appendChild(this.vectorRoot); - this.root.appendChild(this.textRoot); - - 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() { - var child; - var root = this.vectorRoot; - if (root) { - while (child = root.firstChild) { - root.removeChild(child); - } - } - root = this.textRoot; - if (root) { - while (child = root.firstChild) { - root.removeChild(child); - } - } - if (this.indexer) { - this.indexer.clear(); - } - }, - - /** - * Method: setExtent - * Set the visible part of the layer. - * - * 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) { - var coordSysUnchanged = OpenLayers.Renderer.prototype.setExtent.apply(this, arguments); - var resolution = this.getResolution(); - if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) { - var rightOfDateLine, - ratio = extent.getWidth() / this.map.getExtent().getWidth(), - extent = extent.scale(1 / ratio), - world = this.map.getMaxExtent(); - if (world.right > extent.left && world.right < extent.right) { - rightOfDateLine = true; - } else if (world.left > extent.left && world.left < extent.right) { - rightOfDateLine = false; - } - if (rightOfDateLine !== this.rightOfDateLine || resolutionChanged) { - coordSysUnchanged = false; - this.xOffset = rightOfDateLine === true ? - world.getWidth() / resolution : 0; - } - this.rightOfDateLine = rightOfDateLine; - } - return coordSysUnchanged; - }, - - /** - * Method: getNodeType - * This function is in charge of asking the specific renderer which type - * of node to create for the given geometry and style. All geometries - * in an Elements-based renderer consist of one node and some - * attributes. We have the nodeFactory() function which creates a node - * for us, but it takes a 'type' as input, and that is precisely what - * this function tells us. - * - * Parameters: - * geometry - {<OpenLayers.Geometry>} - * style - {Object} - * - * Returns: - * {String} The corresponding node type for the specified geometry - */ - getNodeType: function(geometry, style) { }, - - /** - * Method: drawGeometry - * Draw the geometry, creating new nodes, setting paths, setting style, - * setting featureId on the node. This method should only be called - * by the renderer itself. - * - * Parameters: - * geometry - {<OpenLayers.Geometry>} - * style - {Object} - * featureId - {String} - * - * Returns: - * {Boolean} true if the geometry has been drawn completely; null if - * incomplete; false otherwise - */ - drawGeometry: function(geometry, style, featureId) { - var className = geometry.CLASS_NAME; - var rendered = true; - if ((className == "OpenLayers.Geometry.Collection") || - (className == "OpenLayers.Geometry.MultiPoint") || - (className == "OpenLayers.Geometry.MultiLineString") || - (className == "OpenLayers.Geometry.MultiPolygon")) { - for (var i = 0, len=geometry.components.length; i<len; i++) { - rendered = this.drawGeometry( - geometry.components[i], style, featureId) && rendered; - } - return rendered; - } - - rendered = false; - var removeBackground = false; - if (style.display != "none") { - if (style.backgroundGraphic) { - this.redrawBackgroundNode(geometry.id, geometry, style, - featureId); - } else { - removeBackground = true; - } - rendered = this.redrawNode(geometry.id, geometry, style, - featureId); - } - if (rendered == false) { - var node = document.getElementById(geometry.id); - if (node) { - if (node._style.backgroundGraphic) { - removeBackground = true; - } - node.parentNode.removeChild(node); - } - } - if (removeBackground) { - var node = document.getElementById( - geometry.id + this.BACKGROUND_ID_SUFFIX); - if (node) { - 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) { - style = this.applyDefaultSymbolizer(style); - // Get the node if it's already on the map. - var node = this.nodeFactory(id, this.getNodeType(geometry, style)); - - // Set the data for the node, then draw it. - node._featureId = featureId; - node._boundsBottom = geometry.getBounds().bottom; - node._geometryClass = geometry.CLASS_NAME; - node._style = style; - - var drawResult = this.drawGeometryNode(node, geometry, style); - if(drawResult === false) { - return false; - } - - node = drawResult.node; - - // Insert the node into the indexer so it can show us where to - // place it. Note that this operation is O(log(n)). If there's a - // performance problem (when dragging, for instance) this is - // likely where it would be. - if (this.indexer) { - var insert = this.indexer.insert(node); - if (insert) { - this.vectorRoot.insertBefore(node, insert); - } else { - this.vectorRoot.appendChild(node); - } - } else { - // if there's no indexer, simply append the node to root, - // but only if the node is a new one - if (node.parentNode !== this.vectorRoot){ - this.vectorRoot.appendChild(node); - } - } - - this.postDraw(node); - - return drawResult.complete; - }, - - /** - * Method: redrawBackgroundNode - * Redraws the node using special 'background' style properties. Basically - * just calls redrawNode(), but instead of directly using the - * 'externalGraphic', 'graphicXOffset', 'graphicYOffset', and - * 'graphicZIndex' properties directly from the specified 'style' - * parameter, we create a new style object and set those properties - * from the corresponding 'background'-prefixed properties from - * specified 'style' parameter. - * - * Parameters: - * id - {String} - * geometry - {<OpenLayers.Geometry>} - * style - {Object} - * featureId - {String} - * - * Returns: - * {Boolean} true if the complete geometry could be drawn, null if parts of - * the geometry could not be drawn, false otherwise - */ - redrawBackgroundNode: function(id, geometry, style, featureId) { - var backgroundStyle = OpenLayers.Util.extend({}, style); - - // Set regular style attributes to apply to the background styles. - backgroundStyle.externalGraphic = backgroundStyle.backgroundGraphic; - backgroundStyle.graphicXOffset = backgroundStyle.backgroundXOffset; - backgroundStyle.graphicYOffset = backgroundStyle.backgroundYOffset; - backgroundStyle.graphicZIndex = backgroundStyle.backgroundGraphicZIndex; - backgroundStyle.graphicWidth = backgroundStyle.backgroundWidth || backgroundStyle.graphicWidth; - backgroundStyle.graphicHeight = backgroundStyle.backgroundHeight || backgroundStyle.graphicHeight; - - // 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; - - var options = { - 'isFilled': style.fill === undefined ? - true : - style.fill, - 'isStroked': style.stroke === undefined ? - !!style.strokeWidth : - style.stroke - }; - var drawn; - switch (geometry.CLASS_NAME) { - case "OpenLayers.Geometry.Point": - if(style.graphic === false) { - options.isFilled = false; - options.isStroked = false; - } - 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.Rectangle": - drawn = this.drawRectangle(node, geometry); - break; - default: - break; - } - - 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: removeText - * Removes a label - * - * Parameters: - * featureId - {String} - */ - removeText: function(featureId) { - var label = document.getElementById(featureId + this.LABEL_ID_SUFFIX); - if (label) { - this.textRoot.removeChild(label); - } - var outline = document.getElementById(featureId + this.LABEL_OUTLINE_SUFFIX); - if (outline) { - this.textRoot.removeChild(outline); - } - }, - - /** - * Method: getFeatureIdFromEvent - * - * Parameters: - * evt - {Object} An <OpenLayers.Event> object - * - * Returns: - * {String} A feature id or undefined. - */ - getFeatureIdFromEvent: function(evt) { - var target = evt.target; - var useElement = target && target.correspondingUseElement; - var node = useElement ? useElement : (target || evt.srcElement); - return node._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>} - * featureId - {String} - */ - eraseGeometry: function(geometry, featureId) { - 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], featureId); - } - } else { - var element = OpenLayers.Util.getElement(geometry.id); - if (element && element.parentNode) { - if (element.geometry) { - element.geometry.destroy(); - element.geometry = null; - } - element.parentNode.removeChild(element); - - if (this.indexer) { - this.indexer.remove(element); - } - - if (element._style.backgroundGraphic) { - var backgroundId = geometry.id + this.BACKGROUND_ID_SUFFIX; - var bElem = OpenLayers.Util.getElement(backgroundId); - if (bElem && bElem.parentNode) { - // No need to destroy the geometry since the element and the background - // node share the same geometry. - bElem.parentNode.removeChild(bElem); - } - } - } - } - }, - - /** - * Method: nodeFactory - * Create new node of the specified type, with the (optional) specified id. - * - * If node already exists with same ID and a different type, we remove it - * and then call ourselves again to recreate it. - * - * Parameters: - * id - {String} - * type - {String} type Kind of node to draw. - * - * Returns: - * {DOMElement} A new node of the given type and id. - */ - nodeFactory: function(id, type) { - var node = OpenLayers.Util.getElement(id); - if (node) { - if (!this.nodeTypeCompare(node, type)) { - node.parentNode.removeChild(node); - node = this.nodeFactory(id, type); - } - } else { - node = this.createNode(type, id); - } - return node; - }, - - /** - * Method: nodeTypeCompare - * - * Parameters: - * node - {DOMElement} - * type - {String} Kind of node - * - * Returns: - * {Boolean} Whether or not the specified node is of the specified type - * This function must be overridden by subclasses. - */ - nodeTypeCompare: function(node, type) {}, - - /** - * Method: createNode - * - * Parameters: - * type - {String} Kind of node to draw. - * id - {String} Id for node. - * - * Returns: - * {DOMElement} A new node of the given type and id. - * This function must be overridden by subclasses. - */ - createNode: function(type, id) {}, - - /** - * Method: moveRoot - * moves this renderer's root to a different renderer. - * - * Parameters: - * renderer - {<OpenLayers.Renderer>} target renderer for the moved root - */ - moveRoot: function(renderer) { - var root = this.root; - if(renderer.root.parentNode == this.rendererRoot) { - root = renderer.root; - } - root.parentNode.removeChild(root); - renderer.rendererRoot.appendChild(root); - }, - - /** - * Method: getRenderLayerId - * Gets the layer that this renderer's output appears on. If moveRoot was - * used, this will be different from the id of the layer containing the - * features rendered by this renderer. - * - * Returns: - * {String} the id of the output layer. - */ - getRenderLayerId: function() { - return this.root.parentNode.parentNode.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" -}); - -/* ====================================================================== - OpenLayers/Control.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @requires OpenLayers/BaseTypes/Class.js - */ - -/** - * Class: OpenLayers.Control - * Controls affect the display or behavior of the map. They allow everything - * from panning and zooming to displaying a scale indicator. Controls by - * default are added to the map they are contained within however it is - * possible to add a control to an external div by passing the div in the - * options parameter. - * - * Example: - * The following example shows how to add many of the common controls - * to a map. - * - * > var map = new OpenLayers.Map('map', { controls: [] }); - * > - * > map.addControl(new OpenLayers.Control.PanZoomBar()); - * > map.addControl(new OpenLayers.Control.LayerSwitcher({'ascending':false})); - * > map.addControl(new OpenLayers.Control.Permalink()); - * > map.addControl(new OpenLayers.Control.Permalink('permalink')); - * > map.addControl(new OpenLayers.Control.MousePosition()); - * > map.addControl(new OpenLayers.Control.OverviewMap()); - * > map.addControl(new OpenLayers.Control.KeyboardDefaults()); - * - * The next code fragment is a quick example of how to intercept - * shift-mouse click to display the extent of the bounding box - * dragged out by the user. Usually controls are not created - * in exactly this manner. See the source for a more complete - * example: - * - * > var control = new OpenLayers.Control(); - * > OpenLayers.Util.extend(control, { - * > draw: function () { - * > // this Handler.Box will intercept the shift-mousedown - * > // before Control.MouseDefault gets to see it - * > this.box = new OpenLayers.Handler.Box( control, - * > {"done": this.notice}, - * > {keyMask: OpenLayers.Handler.MOD_SHIFT}); - * > this.box.activate(); - * > }, - * > - * > notice: function (bounds) { - * > OpenLayers.Console.userError(bounds); - * > } - * > }); - * > map.addControl(control); - * - */ -OpenLayers.Control = OpenLayers.Class({ - - /** - * Property: id - * {String} - */ - id: null, - - /** - * Property: map - * {<OpenLayers.Map>} this gets set in the addControl() function in - * OpenLayers.Map - */ - map: null, - - /** - * APIProperty: div - * {DOMElement} The element that contains the control, if not present the - * control is placed inside the map. - */ - div: null, - - /** - * APIProperty: type - * {Number} Controls can have a 'type'. The type determines the type of - * interactions which are possible with them when they are placed in an - * <OpenLayers.Control.Panel>. - */ - type: null, - - /** - * Property: allowSelection - * {Boolean} By default, controls do not allow selection, because - * it may interfere with map dragging. If this is true, OpenLayers - * will not prevent selection of the control. - * Default is false. - */ - allowSelection: false, - - /** - * Property: displayClass - * {string} This property is used for CSS related to the drawing of the - * Control. - */ - displayClass: "", - - /** - * APIProperty: title - * {string} This property is used for showing a tooltip over the - * Control. - */ - title: "", - - /** - * APIProperty: autoActivate - * {Boolean} Activate the control when it is added to a map. Default is - * false. - */ - autoActivate: false, - - /** - * APIProperty: active - * {Boolean} The control is active (read-only). Use <activate> and - * <deactivate> to change control state. - */ - active: null, - - /** - * Property: handlerOptions - * {Object} Used to set non-default properties on the control's handler - */ - handlerOptions: null, - - /** - * Property: handler - * {<OpenLayers.Handler>} null - */ - handler: null, - - /** - * APIProperty: eventListeners - * {Object} If set as an option at construction, the eventListeners - * object will be registered with <OpenLayers.Events.on>. Object - * structure must be a listeners object as shown in the example for - * the events.on method. - */ - eventListeners: null, - - /** - * APIProperty: events - * {<OpenLayers.Events>} Events instance for listeners and triggering - * control specific events. - * - * Register a listener for a particular event with the following syntax: - * (code) - * control.events.register(type, obj, listener); - * (end) - * - * Listeners will be called with a reference to an event object. The - * properties of this event depends on exactly what happened. - * - * All event objects have at least the following properties: - * object - {Object} A reference to control.events.object (a reference - * to the control). - * element - {DOMElement} A reference to control.events.element (which - * will be null unless documented otherwise). - * - * Supported map event types: - * activate - Triggered when activated. - * deactivate - Triggered when deactivated. - */ - events: null, - - /** - * Constructor: OpenLayers.Control - * Create an OpenLayers Control. The options passed as a parameter - * directly extend the control. For example passing the following: - * - * > var control = new OpenLayers.Control({div: myDiv}); - * - * Overrides the default div attribute value of null. - * - * Parameters: - * options - {Object} - */ - initialize: function (options) { - // We do this before the extend so that instances can override - // className in options. - this.displayClass = - this.CLASS_NAME.replace("OpenLayers.", "ol").replace(/\./g, ""); - - OpenLayers.Util.extend(this, options); - - this.events = new OpenLayers.Events(this); - if(this.eventListeners instanceof Object) { - this.events.on(this.eventListeners); - } - if (this.id == null) { - this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_"); - } - }, - - /** - * Method: destroy - * The destroy method is used to perform any clean up before the control - * is dereferenced. Typically this is where event listeners are removed - * to prevent memory leaks. - */ - destroy: function () { - if(this.events) { - if(this.eventListeners) { - this.events.un(this.eventListeners); - } - this.events.destroy(); - this.events = null; - } - this.eventListeners = null; - - // eliminate circular references - 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(); - } - } - this.handlers = null; - } - if (this.map) { - this.map.removeControl(this); - this.map = null; - } - this.div = null; - }, - - /** - * Method: setMap - * Set the map property for the control. 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) { - this.map = map; - if (this.handler) { - this.handler.setMap(map); - } - }, - - /** - * Method: draw - * The draw method is called when the control is ready to be displayed - * on the page. If a div has not been created one is created. Controls - * with a visual component will almost always want to override this method - * to customize the look of control. - * - * Parameters: - * px - {<OpenLayers.Pixel>} The top-left pixel position of the control - * or null. - * - * Returns: - * {DOMElement} A reference to the DIV DOMElement containing the control - */ - draw: function (px) { - if (this.div == null) { - this.div = OpenLayers.Util.createDiv(this.id); - this.div.className = this.displayClass; - if (!this.allowSelection) { - this.div.className += " olControlNoSelect"; - this.div.setAttribute("unselectable", "on", 0); - this.div.onselectstart = OpenLayers.Function.False; - } - if (this.title != "") { - this.div.title = this.title; - } - } - if (px != null) { - this.position = px.clone(); - } - this.moveTo(this.position); - return this.div; - }, - - /** - * Method: moveTo - * Sets the left and top style attributes to the passed in pixel - * coordinates. - * - * Parameters: - * px - {<OpenLayers.Pixel>} - */ - moveTo: function (px) { - if ((px != null) && (this.div != null)) { - this.div.style.left = px.x + "px"; - this.div.style.top = px.y + "px"; - } - }, - - /** - * APIMethod: activate - * Explicitly activates a control and it's associated - * handler if one has been set. Controls can be - * deactivated by calling the deactivate() method. - * - * Returns: - * {Boolean} True if the control was successfully activated or - * false if the control was already active. - */ - activate: function () { - if (this.active) { - return false; - } - if (this.handler) { - this.handler.activate(); - } - this.active = true; - if(this.map) { - OpenLayers.Element.addClass( - this.map.viewPortDiv, - this.displayClass.replace(/ /g, "") + "Active" - ); - } - this.events.triggerEvent("activate"); - return true; - }, - - /** - * APIMethod: deactivate - * Deactivates a control and it's associated handler if any. The exact - * effect of this depends on the control itself. - * - * Returns: - * {Boolean} True if the control was effectively deactivated or false - * if the control was already inactive. - */ - deactivate: function () { - if (this.active) { - if (this.handler) { - this.handler.deactivate(); - } - this.active = false; - if(this.map) { - OpenLayers.Element.removeClass( - this.map.viewPortDiv, - this.displayClass.replace(/ /g, "") + "Active" - ); - } - this.events.triggerEvent("deactivate"); - return true; - } - return false; - }, - - CLASS_NAME: "OpenLayers.Control" -}); - -/** - * Constant: OpenLayers.Control.TYPE_BUTTON - */ -OpenLayers.Control.TYPE_BUTTON = 1; - -/** - * Constant: OpenLayers.Control.TYPE_TOGGLE - */ -OpenLayers.Control.TYPE_TOGGLE = 2; - -/** - * Constant: OpenLayers.Control.TYPE_TOOL - */ -OpenLayers.Control.TYPE_TOOL = 3; -/* ====================================================================== - OpenLayers/Control/Panel.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @requires OpenLayers/Control.js - * @requires OpenLayers/Events/buttonclick.js - */ - -/** - * Class: OpenLayers.Control.Panel - * The Panel control is a container for other controls. With it toolbars - * may be composed. - * - * Inherits from: - * - <OpenLayers.Control> - */ -OpenLayers.Control.Panel = OpenLayers.Class(OpenLayers.Control, { - /** - * Property: controls - * {Array(<OpenLayers.Control>)} - */ - controls: null, - - /** - * APIProperty: autoActivate - * {Boolean} Activate the control when it is added to a map. Default is - * true. - */ - autoActivate: true, - - /** - * APIProperty: defaultControl - * {<OpenLayers.Control>} The control which is activated when the control is - * activated (turned on), which also happens at instantiation. - * If <saveState> is true, <defaultControl> will be nullified after the - * first activation of the panel. - */ - defaultControl: null, - - /** - * APIProperty: saveState - * {Boolean} If set to true, the active state of this panel's controls will - * be stored on panel deactivation, and restored on reactivation. Default - * is false. - */ - saveState: false, - - /** - * APIProperty: allowDepress - * {Boolean} If is true the <OpenLayers.Control.TYPE_TOOL> controls can - * be deactivated by clicking the icon that represents them. Default - * is false. - */ - allowDepress: false, - - /** - * Property: activeState - * {Object} stores the active state of this panel's controls. - */ - activeState: null, - - /** - * Constructor: OpenLayers.Control.Panel - * Create a new control panel. - * - * Each control in the panel is represented by an icon. When clicking - * on an icon, the <activateControl> method is called. - * - * Specific properties for controls on a panel: - * type - {Number} One of <OpenLayers.Control.TYPE_TOOL>, - * <OpenLayers.Control.TYPE_TOGGLE>, <OpenLayers.Control.TYPE_BUTTON>. - * If not provided, <OpenLayers.Control.TYPE_TOOL> is assumed. - * title - {string} Text displayed when mouse is over the icon that - * represents the control. - * - * The <OpenLayers.Control.type> of a control determines the behavior when - * clicking its icon: - * <OpenLayers.Control.TYPE_TOOL> - The control is activated and other - * controls of this type in the same panel are deactivated. This is - * the default type. - * <OpenLayers.Control.TYPE_TOGGLE> - The active state of the control is - * toggled. - * <OpenLayers.Control.TYPE_BUTTON> - The - * <OpenLayers.Control.Button.trigger> method of the control is called, - * but its active state is not changed. - * - * If a control is <OpenLayers.Control.active>, it will be drawn with the - * olControl[Name]ItemActive class, otherwise with the - * olControl[Name]ItemInactive class. - * - * 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 = []; - this.activeState = {}; - }, - - /** - * APIMethod: destroy - */ - destroy: function() { - if (this.map) { - this.map.events.unregister("buttonclick", this, this.onButtonClick); - } - OpenLayers.Control.prototype.destroy.apply(this, arguments); - for (var ctl, i = this.controls.length - 1; i >= 0; i--) { - ctl = this.controls[i]; - if (ctl.events) { - ctl.events.un({ - activate: this.iconOn, - deactivate: this.iconOff - }); - } - ctl.panel_div = null; - } - this.activeState = null; - }, - - /** - * APIMethod: activate - */ - activate: function() { - if (OpenLayers.Control.prototype.activate.apply(this, arguments)) { - var control; - for (var i=0, len=this.controls.length; i<len; i++) { - control = this.controls[i]; - if (control === this.defaultControl || - (this.saveState && this.activeState[control.id])) { - control.activate(); - } - } - if (this.saveState === true) { - this.defaultControl = null; - } - this.redraw(); - return true; - } else { - return false; - } - }, - - /** - * APIMethod: deactivate - */ - deactivate: function() { - if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) { - var control; - for (var i=0, len=this.controls.length; i<len; i++) { - control = this.controls[i]; - this.activeState[control.id] = control.deactivate(); - } - this.redraw(); - return true; - } else { - return false; - } - }, - - /** - * Method: draw - * - * Returns: - * {DOMElement} - */ - draw: function() { - OpenLayers.Control.prototype.draw.apply(this, arguments); - if (this.outsideViewport) { - this.events.attachToElement(this.div); - this.events.register("buttonclick", this, this.onButtonClick); - } else { - this.map.events.register("buttonclick", this, this.onButtonClick); - } - this.addControlsToMap(this.controls); - return this.div; - }, - - /** - * Method: redraw - */ - redraw: function() { - for (var l=this.div.childNodes.length, i=l-1; i>=0; i--) { - this.div.removeChild(this.div.childNodes[i]); - } - this.div.innerHTML = ""; - if (this.active) { - for (var i=0, len=this.controls.length; i<len; i++) { - this.div.appendChild(this.controls[i].panel_div); - } - } - }, - - /** - * APIMethod: activateControl - * This method is called when the user click on the icon representing a - * control in the panel. - * - * Parameters: - * control - {<OpenLayers.Control>} - */ - activateControl: function (control) { - if (!this.active) { return false; } - if (control.type == OpenLayers.Control.TYPE_BUTTON) { - control.trigger(); - return; - } - if (control.type == OpenLayers.Control.TYPE_TOGGLE) { - if (control.active) { - control.deactivate(); - } else { - control.activate(); - } - return; - } - if (this.allowDepress && control.active) { - control.deactivate(); - } else { - var c; - for (var i=0, len=this.controls.length; i<len; i++) { - c = this.controls[i]; - if (c != control && - (c.type === OpenLayers.Control.TYPE_TOOL || c.type == null)) { - c.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>} Controls to add in the panel. - */ - addControls: function(controls) { - if (!(OpenLayers.Util.isArray(controls))) { - controls = [controls]; - } - this.controls = this.controls.concat(controls); - - for (var i=0, len=controls.length; i<len; i++) { - var control = controls[i], - element = this.createControlMarkup(control); - OpenLayers.Element.addClass(element, - control.displayClass + "ItemInactive"); - OpenLayers.Element.addClass(element, "olButton"); - if (control.title != "" && !element.title) { - element.title = control.title; - } - control.panel_div = element; - } - - if (this.map) { // map.addControl() has already been called on the panel - this.addControlsToMap(controls); - this.redraw(); - } - }, - - /** - * APIMethod: createControlMarkup - * This function just creates a div for the control. If specific HTML - * markup is needed this function can be overridden in specific classes, - * or at panel instantiation time: - * - * Example: - * (code) - * var panel = new OpenLayers.Control.Panel({ - * defaultControl: control, - * // ovverride createControlMarkup to create actual buttons - * // including texts wrapped into span elements. - * createControlMarkup: function(control) { - * var button = document.createElement('button'), - * span = document.createElement('span'); - * if (control.text) { - * span.innerHTML = control.text; - * } - * return button; - * } - * }); - * (end) - * - * Parameters: - * control - {<OpenLayers.Control>} The control to create the HTML - * markup for. - * - * Returns: - * {DOMElement} The markup. - */ - createControlMarkup: function(control) { - return document.createElement("div"); - }, - - /** - * Method: addControlsToMap - * Only for internal use in draw() and addControls() methods. - * - * Parameters: - * controls - {Array(<OpenLayers.Control>)} Controls to add into map. - */ - addControlsToMap: function (controls) { - var control; - for (var i=0, len=controls.length; i<len; i++) { - control = controls[i]; - if (control.autoActivate === true) { - control.autoActivate = false; - this.map.addControl(control); - control.autoActivate = true; - } else { - this.map.addControl(control); - control.deactivate(); - } - control.events.on({ - activate: this.iconOn, - deactivate: this.iconOff - }); - } - }, - - /** - * Method: iconOn - * Internal use, for use only with "controls[i].events.on/un". - */ - iconOn: function() { - var d = this.panel_div; // "this" refers to a control on panel! - var re = new RegExp("\\b(" + this.displayClass + "Item)Inactive\\b"); - d.className = d.className.replace(re, "$1Active"); - }, - - /** - * Method: iconOff - * Internal use, for use only with "controls[i].events.on/un". - */ - iconOff: function() { - var d = this.panel_div; // "this" refers to a control on panel! - var re = new RegExp("\\b(" + this.displayClass + "Item)Active\\b"); - d.className = d.className.replace(re, "$1Inactive"); - }, - - /** - * Method: onButtonClick - * - * Parameters: - * evt - {Event} - */ - onButtonClick: function (evt) { - var controls = this.controls, - button = evt.buttonElement; - for (var i=controls.length-1; i>=0; --i) { - if (controls[i].panel_div === button) { - this.activateControl(controls[i]); - break; - } - } - }, - - /** - * APIMethod: getControlsBy - * Get a list of controls with properties matching the given criteria. - * - * Parameters: - * 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. - * - * Parameters: - * 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). - * - * Parameters: - * 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/Strategy.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @requires OpenLayers/BaseTypes/Class.js - */ - -/** - * 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>} The layer this strategy belongs to. - */ - 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 - * Called to set the <layer> property. - * - * Parameters: - * layer - {<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/Strategy/Fixed.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository 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, { - - /** - * APIProperty: preload - * {Boolean} Load data before layer made visible. Enabling this may result - * in considerable overhead if your application loads many data layers - * that are not visible by default. Default is false. - */ - preload: false, - - /** - * Constructor: OpenLayers.Strategy.Fixed - * Create a new Fixed strategy. - * - * Parameters: - * options - {Object} Optional object whose properties will be set on the - * instance. - */ - - /** - * Method: activate - * Activate the strategy: load data or add listener to load when visible - * - * Returns: - * {Boolean} True if the strategy was successfully activated or false if - * the strategy was already active. - */ - activate: function() { - var activated = OpenLayers.Strategy.prototype.activate.apply(this, arguments); - if(activated) { - this.layer.events.on({ - "refresh": this.load, - scope: this - }); - if(this.layer.visibility == true || this.preload) { - this.load(); - } else { - this.layer.events.on({ - "visibilitychanged": this.load, - scope: this - }); - } - } - return activated; - }, - - /** - * Method: deactivate - * Deactivate the strategy. Undo what is done in <activate>. - * - * Returns: - * {Boolean} The strategy was successfully deactivated. - */ - deactivate: function() { - var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this); - if(deactivated) { - this.layer.events.un({ - "refresh": this.load, - "visibilitychanged": this.load, - scope: this - }); - } - return deactivated; - }, - - /** - * Method: load - * Tells protocol to load data and unhooks the visibilitychanged event - * - * Parameters: - * options - {Object} options to pass to protocol read. - */ - load: function(options) { - var layer = this.layer; - layer.events.triggerEvent("loadstart", {filter: layer.filter}); - layer.protocol.read(OpenLayers.Util.applyDefaults({ - callback: this.merge, - filter: layer.filter, - scope: this - }, options)); - layer.events.un({ - "visibilitychanged": this.load, - scope: this - }); - }, - - /** - * Method: merge - * Add all features to the layer. - * If the layer projection differs from the map projection, features - * will be transformed from the layer projection to the map projection. - * - * Parameters: - * resp - {<OpenLayers.Protocol.Response>} The response object passed - * by the protocol. - */ - merge: function(resp) { - var layer = this.layer; - layer.destroyFeatures(); - var features = resp.features; - if (features && features.length > 0) { - var remote = layer.projection; - var local = layer.map.getProjectionObject(); - if(!local.equals(remote)) { - var geom; - for(var i=0, len=features.length; i<len; ++i) { - geom = features[i].geometry; - if(geom) { - geom.transform(remote, local); - } - } - } - layer.addFeatures(features); - } - layer.events.triggerEvent("loadend", {response: resp}); - }, - - CLASS_NAME: "OpenLayers.Strategy.Fixed" -}); -/* ====================================================================== - OpenLayers/Control/Zoom.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @requires OpenLayers/Control.js - * @requires OpenLayers/Events/buttonclick.js - */ - -/** - * Class: OpenLayers.Control.Zoom - * The Zoom control is a pair of +/- links for zooming in and out. - * - * Inherits from: - * - <OpenLayers.Control> - */ -OpenLayers.Control.Zoom = OpenLayers.Class(OpenLayers.Control, { - - /** - * APIProperty: zoomInText - * {String} - * Text for zoom-in link. Default is "+". - */ - zoomInText: "+", - - /** - * APIProperty: zoomInId - * {String} - * Instead of having the control create a zoom in link, you can provide - * the identifier for an anchor element already added to the document. - * By default, an element with id "olZoomInLink" will be searched for - * and used if it exists. - */ - zoomInId: "olZoomInLink", - - /** - * APIProperty: zoomOutText - * {String} - * Text for zoom-out link. Default is "\u2212". - */ - zoomOutText: "\u2212", - - /** - * APIProperty: zoomOutId - * {String} - * Instead of having the control create a zoom out link, you can provide - * the identifier for an anchor element already added to the document. - * By default, an element with id "olZoomOutLink" will be searched for - * and used if it exists. - */ - zoomOutId: "olZoomOutLink", - - /** - * Method: draw - * - * Returns: - * {DOMElement} A reference to the DOMElement containing the zoom links. - */ - draw: function() { - var div = OpenLayers.Control.prototype.draw.apply(this), - links = this.getOrCreateLinks(div), - zoomIn = links.zoomIn, - zoomOut = links.zoomOut, - eventsInstance = this.map.events; - - if (zoomOut.parentNode !== div) { - eventsInstance = this.events; - eventsInstance.attachToElement(zoomOut.parentNode); - } - eventsInstance.register("buttonclick", this, this.onZoomClick); - - this.zoomInLink = zoomIn; - this.zoomOutLink = zoomOut; - return div; - }, - - /** - * Method: getOrCreateLinks - * - * Parameters: - * el - {DOMElement} - * - * Return: - * {Object} Object with zoomIn and zoomOut properties referencing links. - */ - getOrCreateLinks: function(el) { - var zoomIn = document.getElementById(this.zoomInId), - zoomOut = document.getElementById(this.zoomOutId); - if (!zoomIn) { - zoomIn = document.createElement("a"); - zoomIn.href = "#zoomIn"; - zoomIn.appendChild(document.createTextNode(this.zoomInText)); - zoomIn.className = "olControlZoomIn"; - el.appendChild(zoomIn); - } - OpenLayers.Element.addClass(zoomIn, "olButton"); - if (!zoomOut) { - zoomOut = document.createElement("a"); - zoomOut.href = "#zoomOut"; - zoomOut.appendChild(document.createTextNode(this.zoomOutText)); - zoomOut.className = "olControlZoomOut"; - el.appendChild(zoomOut); - } - OpenLayers.Element.addClass(zoomOut, "olButton"); - return { - zoomIn: zoomIn, zoomOut: zoomOut - }; - }, - - /** - * Method: onZoomClick - * Called when zoomin/out link is clicked. - */ - onZoomClick: function(evt) { - var button = evt.buttonElement; - if (button === this.zoomInLink) { - this.map.zoomIn(); - } else if (button === this.zoomOutLink) { - this.map.zoomOut(); - } - }, - - /** - * Method: destroy - * Clean up. - */ - destroy: function() { - if (this.map) { - this.map.events.unregister("buttonclick", this, this.onZoomClick); - } - delete this.zoomInLink; - delete this.zoomOutLink; - OpenLayers.Control.prototype.destroy.apply(this); - }, - - CLASS_NAME: "OpenLayers.Control.Zoom" -}); -/* ====================================================================== - OpenLayers/Geometry/Polygon.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @requires OpenLayers/Geometry/Collection.js - * @requires OpenLayers/Geometry/LinearRing.js - */ - -/** - * Class: OpenLayers.Geometry.Polygon - * Polygon is a collection of Geometry.LinearRings. - * - * Inherits from: - * - <OpenLayers.Geometry.Collection> - * - <OpenLayers.Geometry> - */ -OpenLayers.Geometry.Polygon = OpenLayers.Class( - OpenLayers.Geometry.Collection, { - - /** - * Property: componentTypes - * {Array(String)} An array of class names representing the types of - * components that the collection can include. A null value means the - * component types are not restricted. - */ - componentTypes: ["OpenLayers.Geometry.LinearRing"], - - /** - * Constructor: OpenLayers.Geometry.Polygon - * Constructor for a Polygon geometry. - * The first ring (this.component[0])is the outer bounds of the polygon and - * all subsequent rings (this.component[1-n]) are internal holes. - * - * - * Parameters: - * components - {Array(<OpenLayers.Geometry.LinearRing>)} - */ - - /** - * 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; - }, - - /** - * APIMethod: getGeodesicArea - * Calculate the approximate area of the polygon were it projected onto - * the earth. - * - * Parameters: - * projection - {<OpenLayers.Projection>} The spatial reference system - * for the geometry coordinates. If not provided, Geographic/WGS84 is - * assumed. - * - * Reference: - * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for - * Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion - * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409 - * - * Returns: - * {float} The approximate geodesic area of the polygon in square meters. - */ - getGeodesicArea: function(projection) { - var area = 0.0; - if(this.components && (this.components.length > 0)) { - area += Math.abs(this.components[0].getGeodesicArea(projection)); - for(var i=1, len=this.components.length; i<len; i++) { - area -= Math.abs(this.components[i].getGeodesicArea(projection)); - } - } - 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; - }, - - /** - * APIMethod: distanceTo - * Calculate the closest distance between two geometries (on the x-y plane). - * - * Parameters: - * geometry - {<OpenLayers.Geometry>} The target geometry. - * options - {Object} Optional properties for configuring the distance - * calculation. - * - * Valid options: - * details - {Boolean} Return details from the distance calculation. - * Default is false. - * edge - {Boolean} Calculate the distance from this geometry to the - * nearest edge of the target geometry. Default is true. If true, - * calling distanceTo from a geometry that is wholly contained within - * the target will result in a non-zero distance. If false, whenever - * geometries intersect, calling distanceTo will return 0. If false, - * details cannot be returned. - * - * Returns: - * {Number | Object} The distance between this geometry and the target. - * If details is true, the return will be an object with distance, - * x0, y0, x1, and y1 properties. The x0 and y0 properties represent - * the coordinates of the closest point on this geometry. The x1 and y1 - * properties represent the coordinates of the closest point on the - * target geometry. - */ - distanceTo: function(geometry, options) { - var edge = !(options && options.edge === false); - var result; - // this is the case where we might not be looking for distance to edge - if(!edge && this.intersects(geometry)) { - result = 0; - } else { - result = OpenLayers.Geometry.Collection.prototype.distanceTo.apply( - this, [geometry, options] - ); - } - return result; - }, - - 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/Geometry/MultiPolygon.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @requires OpenLayers/Geometry/Collection.js - * @requires OpenLayers/Geometry/Polygon.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 - * - */ - - CLASS_NAME: "OpenLayers.Geometry.MultiPolygon" -}); -/* ====================================================================== - OpenLayers/Feature.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - - -/** - * @requires OpenLayers/BaseTypes/Class.js - * @requires OpenLayers/Util.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.Anchored>. - */ - popupClass: null, - - /** - * 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); - } - } - // remove the marker from the layer - if (this.layer != null && this.marker != null) { - this.layer.removeMarker(this.marker); - } - - 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) { - if (!this.popup) { - var anchor = (this.marker) ? this.marker.icon : null; - var popupClass = this.popupClass ? - this.popupClass : OpenLayers.Popup.Anchored; - this.popup = new popupClass(this.id + "_popup", - 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/Feature/Vector.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository 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, serializable properties that - * describe the feature. - */ - attributes: null, - - /** - * Property: bounds - * {<OpenLayers.Bounds>} The box bounding that feature's geometry, that - * property can be set by an <OpenLayers.Format> object when - * deserializing the feature, so in most cases it represents an - * information set by the server. - */ - bounds: null, - - /** - * Property: state - * {String} - */ - state: null, - - /** - * APIProperty: style - * {Object} - */ - style: null, - - /** - * APIProperty: url - * {String} If this property is set it will be taken into account by - * {<OpenLayers.HTTP>} when upadting or deleting the feature. - */ - url: null, - - /** - * Property: renderIntent - * {String} rendering intent currently being used - */ - renderIntent: "default", - - /** - * APIProperty: modified - * {Object} An object with the originals of the geometry and attributes of - * the feature, if they were changed. Currently this property is only read - * by <OpenLayers.Format.WFST.v1>, and written by - * <OpenLayers.Control.ModifyFeature>, which sets the geometry property. - * Applications can set the originals of modified attributes in the - * attributes property. Note that applications have to check if this - * object and the attributes property is already created before using it. - * After a change made with ModifyFeature, this object could look like - * - * (code) - * { - * geometry: >Object - * } - * (end) - * - * When an application has made changes to feature attributes, it could - * have set the attributes to something like this: - * - * (code) - * { - * attributes: { - * myAttribute: "original" - * } - * } - * (end) - * - * Note that <OpenLayers.Format.WFST.v1> only checks for truthy values in - * *modified.geometry* and the attribute names in *modified.attributes*, - * but it is recommended to set the original values (and not just true) as - * attribute value, so applications could use this information to undo - * changes. - */ - modified: null, - - /** - * 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; - this.modified = 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: getVisibility - * Determine whether the feature is displayed or not. It may not displayed - * because: - * - its style display property is set to 'none', - * - it doesn't belong to any layer, - * - the styleMap creates a symbolizer with display property set to 'none' - * for it, - * - the layer which it belongs to is not visible. - * - * Returns: - * {Boolean} The feature is currently displayed. - */ - getVisibility: function() { - return !(this.style && this.style.display == 'none' || - !this.layer || - this.layer && this.layer.styleMap && - this.layer.styleMap.createSymbolizer(this, this.renderIntent).display == 'none' || - this.layer && !this.layer.getVisibility()); - }, - - /** - * 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>|Object} OpenLayers.LonLat or an - * object with a 'lon' and 'lat' properties. - * 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: - * location - {<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 undefined; - } - - 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. These - * styles correspond for the most part, to the styling properties defined - * by the SVG standard. - * Information on fill properties: http://www.w3.org/TR/SVG/painting.html#FillProperties - * Information on stroke properties: http://www.w3.org/TR/SVG/painting.html#StrokeProperties - * - * Symbolizer properties: - * fill - {Boolean} Set to false if no fill is desired. - * fillColor - {String} Hex fill color. Default is "#ee9900". - * fillOpacity - {Number} Fill opacity (0-1). Default is 0.4 - * stroke - {Boolean} Set to false if no stroke is desired. - * strokeColor - {String} Hex stroke color. Default is "#ee9900". - * strokeOpacity - {Number} Stroke opacity (0-1). Default is 1. - * strokeWidth - {Number} Pixel stroke width. Default is 1. - * strokeLinecap - {String} Stroke cap type. Default is "round". [butt | round | square] - * strokeDashstyle - {String} Stroke dash style. Default is "solid". [dot | dash | dashdot | longdash | longdashdot | solid] - * graphic - {Boolean} Set to false if no graphic is desired. - * pointRadius - {Number} Pixel point radius. Default is 6. - * pointerEvents - {String} Default is "visiblePainted". - * cursor - {String} Default is "". - * externalGraphic - {String} Url to an external graphic that will be used for rendering points. - * graphicWidth - {Number} Pixel width for sizing an external graphic. - * graphicHeight - {Number} Pixel height for sizing an external graphic. - * graphicOpacity - {Number} Opacity (0-1) for an external graphic. - * graphicXOffset - {Number} Pixel offset along the positive x axis for displacing an external graphic. - * graphicYOffset - {Number} Pixel offset along the positive y axis for displacing an external graphic. - * rotation - {Number} For point symbolizers, this is the rotation of a graphic in the clockwise direction about its center point (or any point off center as specified by graphicXOffset and graphicYOffset). - * graphicZIndex - {Number} The integer z-index value to use in rendering. - * graphicName - {String} Named graphic to use when rendering points. Supported values include "circle" (default), - * "square", "star", "x", "cross", "triangle". - * graphicTitle - {String} Tooltip when hovering over a feature. *deprecated*, use title instead - * title - {String} Tooltip when hovering over a feature. Not supported by the canvas renderer. - * backgroundGraphic - {String} Url to a graphic to be used as the background under an externalGraphic. - * backgroundGraphicZIndex - {Number} The integer z-index value to use in rendering the background graphic. - * backgroundXOffset - {Number} The x offset (in pixels) for the background graphic. - * backgroundYOffset - {Number} The y offset (in pixels) for the background graphic. - * backgroundHeight - {Number} The height of the background graphic. If not provided, the graphicHeight will be used. - * backgroundWidth - {Number} The width of the background width. If not provided, the graphicWidth will be used. - * label - {String} The text for an optional label. For browsers that use the canvas renderer, this requires either - * fillText or mozDrawText to be available. - * labelAlign - {String} Label alignment. This specifies the insertion point relative to the text. It is a string - * composed of two characters. The first character is for the horizontal alignment, the second for the vertical - * alignment. Valid values for horizontal alignment: "l"=left, "c"=center, "r"=right. Valid values for vertical - * alignment: "t"=top, "m"=middle, "b"=bottom. Example values: "lt", "cm", "rb". Default is "cm". - * labelXOffset - {Number} Pixel offset along the positive x axis for displacing the label. Not supported by the canvas renderer. - * labelYOffset - {Number} Pixel offset along the positive y axis for displacing the label. Not supported by the canvas renderer. - * labelSelect - {Boolean} If set to true, labels will be selectable using SelectFeature or similar controls. - * Default is false. - * labelOutlineColor - {String} The color of the label outline. Default is 'white'. Only supported by the canvas & SVG renderers. - * labelOutlineWidth - {Number} The width of the label outline. Default is 3, set to 0 or null to disable. Only supported by the SVG renderers. - * labelOutlineOpacity - {Number} The opacity (0-1) of the label outline. Default is fontOpacity. Only supported by the canvas & SVG renderers. - * fontColor - {String} The font color for the label, to be provided like CSS. - * fontOpacity - {Number} Opacity (0-1) for the label - * fontFamily - {String} The font family for the label, to be provided like in CSS. - * fontSize - {String} The font size for the label, to be provided like in CSS. - * fontStyle - {String} The font style for the label, to be provided like in CSS. - * fontWeight - {String} The font weight for the label, to be provided like in CSS. - * display - {String} Symbolizers will have no effect if display is set to "none". All other values have no effect. - */ -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", - fontColor: "#000000", - labelAlign: "cm", - labelOutlineColor: "white", - labelOutlineWidth: 3 - }, - '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", - fontColor: "#000000", - labelAlign: "cm", - labelOutlineColor: "white", - labelOutlineWidth: 3 - - }, - 'temporary': { - fillColor: "#66cccc", - fillOpacity: 0.2, - hoverFillColor: "white", - hoverFillOpacity: 0.8, - strokeColor: "#66cccc", - strokeOpacity: 1, - strokeLinecap: "round", - strokeWidth: 2, - strokeDashstyle: "solid", - hoverStrokeColor: "red", - hoverStrokeOpacity: 1, - hoverStrokeWidth: 0.2, - pointRadius: 6, - hoverPointRadius: 1, - hoverPointUnit: "%", - pointerEvents: "visiblePainted", - cursor: "inherit", - fontColor: "#000000", - labelAlign: "cm", - labelOutlineColor: "white", - labelOutlineWidth: 3 - - }, - 'delete': { - display: "none" - } -}; -/* ====================================================================== - OpenLayers/Style.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - - -/** - * @requires OpenLayers/BaseTypes/Class.js - * @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({ - - /** - * Property: id - * {String} A unique id for this session. - */ - id: null, - - /** - * 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, - - /** - * APIProperty: 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. If <defaultsPerSymbolizer> is set to - * true, the defaultStyle will only be taken into account if there are - * rules defined. - */ - defaultStyle: null, - - /** - * Property: defaultsPerSymbolizer - * {Boolean} If set to true, the <defaultStyle> will extend the symbolizer - * of every rule. Properties of the <defaultStyle> will also be used to set - * missing symbolizer properties if the symbolizer has stroke, fill or - * graphic set to true. Default is false. - */ - defaultsPerSymbolizer: false, - - /** - * 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 - * style. - * - * Valid options: - * rules - {Array(<OpenLayers.Rule>)} List of rules to be added to the - * style. - * - * Returns: - * {<OpenLayers.Style>} - */ - initialize: function(style, options) { - - OpenLayers.Util.extend(this, options); - this.rules = []; - if(options && options.rules) { - this.addRules(options.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"]); - - this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_"); - }, - - /** - * 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.defaultsPerSymbolizer ? {} : 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"; - } - - if (style.label != null && typeof style.label !== "string") { - style.label = String(style.label); - } - - 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; - - if(this.defaultsPerSymbolizer === true) { - var defaults = this.defaultStyle; - OpenLayers.Util.applyDefaults(symbolizer, { - pointRadius: defaults.pointRadius - }); - if(symbolizer.stroke === true || symbolizer.graphic === true) { - OpenLayers.Util.applyDefaults(symbolizer, { - strokeWidth: defaults.strokeWidth, - strokeColor: defaults.strokeColor, - strokeOpacity: defaults.strokeOpacity, - strokeDashstyle: defaults.strokeDashstyle, - strokeLinecap: defaults.strokeLinecap - }); - } - if(symbolizer.fill === true || symbolizer.graphic === true) { - OpenLayers.Util.applyDefaults(symbolizer, { - fillColor: defaults.fillColor, - fillOpacity: defaults.fillOpacity - }); - } - if(symbolizer.graphic === true) { - OpenLayers.Util.applyDefaults(symbolizer, { - pointRadius: this.defaultStyle.pointRadius, - externalGraphic: this.defaultStyle.externalGraphic, - graphicName: this.defaultStyle.graphicName, - graphicOpacity: this.defaultStyle.graphicOpacity, - graphicWidth: this.defaultStyle.graphicWidth, - graphicHeight: this.defaultStyle.graphicHeight, - graphicXOffset: this.defaultStyle.graphicXOffset, - graphicYOffset: this.defaultStyle.graphicYOffset - }); - } - } - - // 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 = OpenLayers.Util.extend({}, feature.attributes || feature.data); - OpenLayers.Util.extend(context, this.context); - - for (var i in this.propertyStyles) { - style[i] = OpenLayers.Style.createLiteral(style[i], context, feature, i); - } - 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++) { - 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) { - Array.prototype.push.apply(this.rules, 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]; - } - } - }, - - /** - * APIMethod: clone - * Clones this style. - * - * Returns: - * {<OpenLayers.Style>} Clone of this style. - */ - clone: function() { - var options = OpenLayers.Util.extend({}, this); - // clone rules - if(this.rules) { - options.rules = []; - for(var i=0, len=this.rules.length; i<len; ++i) { - options.rules.push(this.rules[i].clone()); - } - } - // clone context - options.context = this.context && OpenLayers.Util.extend({}, this.context); - //clone default style - var defaultStyle = OpenLayers.Util.extend({}, this.defaultStyle); - return new OpenLayers.Style(defaultStyle, options); - }, - - 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>} optional feature to pass to - * <OpenLayers.String.format> for evaluating functions in the - * context. - * property - {String} optional, name of the property for which the literal is - * being created 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, property) { - if (typeof value == "string" && value.indexOf("${") != -1) { - value = OpenLayers.String.format(value, context, [feature, property]); - 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', - 'Raster']; -/* ====================================================================== - OpenLayers/Filter.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - - -/** - * @requires OpenLayers/BaseTypes/Class.js - * @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 class represents a generic filter. - * - * 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. Instances or subclasses - * are supposed to override this method. - * - * Parameters: - * context - {Object} Context to use in evaluating the filter. If a vector - * feature is provided, the feature.attributes will be used as context. - * - * Returns: - * {Boolean} The filter applies. - */ - evaluate: function(context) { - return true; - }, - - /** - * APIMethod: clone - * Clones this filter. Should be implemented by subclasses. - * - * Returns: - * {<OpenLayers.Filter>} Clone of this filter. - */ - clone: function() { - return null; - }, - - /** - * APIMethod: toString - * - * Returns: - * {String} Include <OpenLayers.Format.CQL> in your build to get a CQL - * representation of the filter returned. Otherwise "[Object object]" - * will be returned. - */ - toString: function() { - var string; - if (OpenLayers.Format && OpenLayers.Format.CQL) { - string = OpenLayers.Format.CQL.prototype.write(this); - } else { - string = Object.prototype.toString.call(this); - } - return string; - }, - - CLASS_NAME: "OpenLayers.Filter" -}); -/* ====================================================================== - OpenLayers/Filter/Spatial.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @requires OpenLayers/Filter.js - */ - -/** - * Class: OpenLayers.Filter.Spatial - * This class represents a spatial filter. - * Currently implemented: BBOX, DWithin and Intersects - * - * Inherits from: - * - <OpenLayers.Filter> - */ -OpenLayers.Filter.Spatial = OpenLayers.Class(OpenLayers.Filter, { - - /** - * APIProperty: type - * {String} Type of spatial filter. - * - * The type should be one of: - * - OpenLayers.Filter.Spatial.BBOX - * - OpenLayers.Filter.Spatial.INTERSECTS - * - OpenLayers.Filter.Spatial.DWITHIN - * - OpenLayers.Filter.Spatial.WITHIN - * - OpenLayers.Filter.Spatial.CONTAINS - */ - type: null, - - /** - * APIProperty: property - * {String} Name of the context property to compare. - */ - property: null, - - /** - * APIProperty: value - * {<OpenLayers.Bounds> || <OpenLayers.Geometry>} The bounds or geometry - * to be used by the filter. Use bounds for BBOX filters and geometry - * for INTERSECTS or DWITHIN filters. - */ - value: null, - - /** - * APIProperty: distance - * {Number} The distance to use in a DWithin spatial filter. - */ - distance: null, - - /** - * APIProperty: distanceUnits - * {String} The units to use for the distance, e.g. 'm'. - */ - distanceUnits: null, - - /** - * Constructor: OpenLayers.Filter.Spatial - * Creates a spatial filter. - * - * Parameters: - * options - {Object} An optional object with properties to set on the - * filter. - * - * Returns: - * {<OpenLayers.Filter.Spatial>} - */ - - /** - * Method: evaluate - * Evaluates this filter for a specific feature. - * - * Parameters: - * feature - {<OpenLayers.Feature.Vector>} feature to apply the filter to. - * - * Returns: - * {Boolean} The feature meets filter criteria. - */ - evaluate: function(feature) { - var intersect = false; - switch(this.type) { - case OpenLayers.Filter.Spatial.BBOX: - case OpenLayers.Filter.Spatial.INTERSECTS: - if(feature.geometry) { - var geom = this.value; - if(this.value.CLASS_NAME == "OpenLayers.Bounds") { - geom = this.value.toGeometry(); - } - if(feature.geometry.intersects(geom)) { - intersect = true; - } - } - break; - default: - throw new Error('evaluate is not implemented for this filter type.'); - } - return intersect; - }, - - /** - * APIMethod: clone - * Clones this filter. - * - * Returns: - * {<OpenLayers.Filter.Spatial>} Clone of this filter. - */ - clone: function() { - var options = OpenLayers.Util.applyDefaults({ - value: this.value && this.value.clone && this.value.clone() - }, this); - return new OpenLayers.Filter.Spatial(options); - }, - CLASS_NAME: "OpenLayers.Filter.Spatial" -}); - -OpenLayers.Filter.Spatial.BBOX = "BBOX"; -OpenLayers.Filter.Spatial.INTERSECTS = "INTERSECTS"; -OpenLayers.Filter.Spatial.DWITHIN = "DWITHIN"; -OpenLayers.Filter.Spatial.WITHIN = "WITHIN"; -OpenLayers.Filter.Spatial.CONTAINS = "CONTAINS"; -/* ====================================================================== - OpenLayers/Strategy/BBOX.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @requires OpenLayers/Strategy.js - * @requires OpenLayers/Filter/Spatial.js - */ - -/** - * Class: OpenLayers.Strategy.BBOX - * A simple strategy that reads new features when the viewport invalidates - * some bounds. - * - * Inherits from: - * - <OpenLayers.Strategy> - */ -OpenLayers.Strategy.BBOX = OpenLayers.Class(OpenLayers.Strategy, { - - /** - * Property: bounds - * {<OpenLayers.Bounds>} The current data bounds (in the same projection - * as the layer - not always the same projection as the map). - */ - bounds: null, - - /** - * Property: resolution - * {Float} The current data resolution. - */ - resolution: null, - - /** - * APIProperty: ratio - * {Float} The ratio of the data bounds to the viewport bounds (in each - * dimension). Default is 2. - */ - ratio: 2, - - /** - * Property: resFactor - * {Float} Optional factor used to determine when previously requested - * features are invalid. If set, the resFactor will be compared to the - * resolution of the previous request to the current map resolution. - * If resFactor > (old / new) and 1/resFactor < (old / new). If you - * set a resFactor of 1, data will be requested every time the - * resolution changes. If you set a resFactor of 3, data will be - * requested if the old resolution is 3 times the new, or if the new is - * 3 times the old. If the old bounds do not contain the new bounds - * new data will always be requested (with or without considering - * resFactor). - */ - resFactor: null, - - /** - * Property: response - * {<OpenLayers.Protocol.Response>} The protocol response object returned - * by the layer protocol. - */ - response: null, - - /** - * Constructor: OpenLayers.Strategy.BBOX - * Create a new BBOX strategy. - * - * Parameters: - * options - {Object} Optional object whose properties will be set on the - * instance. - */ - - /** - * Method: activate - * Set up strategy with regard to reading new batches of remote data. - * - * Returns: - * {Boolean} The strategy was successfully activated. - */ - activate: function() { - var activated = OpenLayers.Strategy.prototype.activate.call(this); - if(activated) { - this.layer.events.on({ - "moveend": this.update, - "refresh": this.update, - "visibilitychanged": this.update, - scope: this - }); - this.update(); - } - return activated; - }, - - /** - * Method: deactivate - * Tear down strategy with regard to reading new batches of remote data. - * - * Returns: - * {Boolean} The strategy was successfully deactivated. - */ - deactivate: function() { - var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this); - if(deactivated) { - this.layer.events.un({ - "moveend": this.update, - "refresh": this.update, - "visibilitychanged": this.update, - scope: this - }); - } - return deactivated; - }, - - /** - * Method: update - * Callback function called on "moveend" or "refresh" layer events. - * - * Parameters: - * options - {Object} Optional object whose properties will determine - * the behaviour of this Strategy - * - * Valid options include: - * force - {Boolean} if true, new data must be unconditionally read. - * noAbort - {Boolean} if true, do not abort previous requests. - */ - update: function(options) { - var mapBounds = this.getMapBounds(); - if (mapBounds !== null && ((options && options.force) || - (this.layer.visibility && this.layer.calculateInRange() && this.invalidBounds(mapBounds)))) { - this.calculateBounds(mapBounds); - this.resolution = this.layer.map.getResolution(); - this.triggerRead(options); - } - }, - - /** - * Method: getMapBounds - * Get the map bounds expressed in the same projection as this layer. - * - * Returns: - * {<OpenLayers.Bounds>} Map bounds in the projection of the layer. - */ - getMapBounds: function() { - if (this.layer.map === null) { - return null; - } - var bounds = this.layer.map.getExtent(); - if(bounds && !this.layer.projection.equals( - this.layer.map.getProjectionObject())) { - bounds = bounds.clone().transform( - this.layer.map.getProjectionObject(), this.layer.projection - ); - } - return bounds; - }, - - /** - * Method: invalidBounds - * Determine whether the previously requested set of features is invalid. - * This occurs when the new map bounds do not contain the previously - * requested bounds. In addition, if <resFactor> is set, it will be - * considered. - * - * Parameters: - * mapBounds - {<OpenLayers.Bounds>} the current map extent, will be - * retrieved from the map object if not provided - * - * Returns: - * {Boolean} - */ - invalidBounds: function(mapBounds) { - if(!mapBounds) { - mapBounds = this.getMapBounds(); - } - var invalid = !this.bounds || !this.bounds.containsBounds(mapBounds); - if(!invalid && this.resFactor) { - var ratio = this.resolution / this.layer.map.getResolution(); - invalid = (ratio >= this.resFactor || ratio <= (1 / this.resFactor)); - } - return invalid; - }, - - /** - * Method: calculateBounds - * - * Parameters: - * mapBounds - {<OpenLayers.Bounds>} the current map extent, will be - * retrieved from the map object if not provided - */ - calculateBounds: function(mapBounds) { - if(!mapBounds) { - mapBounds = this.getMapBounds(); - } - var center = mapBounds.getCenterLonLat(); - var dataWidth = mapBounds.getWidth() * this.ratio; - var dataHeight = mapBounds.getHeight() * this.ratio; - this.bounds = new OpenLayers.Bounds( - center.lon - (dataWidth / 2), - center.lat - (dataHeight / 2), - center.lon + (dataWidth / 2), - center.lat + (dataHeight / 2) - ); - }, - - /** - * Method: triggerRead - * - * Parameters: - * options - {Object} Additional options for the protocol's read method - * (optional) - * - * Returns: - * {<OpenLayers.Protocol.Response>} The protocol response object - * returned by the layer protocol. - */ - triggerRead: function(options) { - if (this.response && !(options && options.noAbort === true)) { - this.layer.protocol.abort(this.response); - this.layer.events.triggerEvent("loadend"); - } - var evt = {filter: this.createFilter()}; - this.layer.events.triggerEvent("loadstart", evt); - this.response = this.layer.protocol.read( - OpenLayers.Util.applyDefaults({ - filter: evt.filter, - callback: this.merge, - scope: this - }, options)); - }, - - /** - * Method: createFilter - * Creates a spatial BBOX filter. If the layer that this strategy belongs - * to has a filter property, this filter will be combined with the BBOX - * filter. - * - * Returns - * {<OpenLayers.Filter>} The filter object. - */ - createFilter: function() { - var filter = new OpenLayers.Filter.Spatial({ - type: OpenLayers.Filter.Spatial.BBOX, - value: this.bounds, - projection: this.layer.projection - }); - if (this.layer.filter) { - filter = new OpenLayers.Filter.Logical({ - type: OpenLayers.Filter.Logical.AND, - filters: [this.layer.filter, filter] - }); - } - return filter; - }, - - /** - * Method: merge - * Given a list of features, determine which ones to add to the layer. - * If the layer projection differs from the map projection, features - * will be transformed from the layer projection to the map projection. - * - * Parameters: - * resp - {<OpenLayers.Protocol.Response>} The response object passed - * by the protocol. - */ - merge: function(resp) { - this.layer.destroyFeatures(); - if (resp.success()) { - var features = resp.features; - if(features && features.length > 0) { - var remote = this.layer.projection; - var local = this.layer.map.getProjectionObject(); - if(!local.equals(remote)) { - var geom; - for(var i=0, len=features.length; i<len; ++i) { - geom = features[i].geometry; - if(geom) { - geom.transform(remote, local); - } - } - } - this.layer.addFeatures(features); - } - } else { - this.bounds = null; - } - this.response = null; - this.layer.events.triggerEvent("loadend", {response: resp}); - }, - - CLASS_NAME: "OpenLayers.Strategy.BBOX" -}); -/* ====================================================================== - OpenLayers/Handler/Feature.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository 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. - * - * Inherits from: - * - <OpenLayers.Handler> - */ -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}, - 'touchstart': {'in': 'click', 'out': 'clickout'} - }, - - /** - * 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: clickTolerance - * {Number} The number of pixels the mouse can move between mousedown - * and mouseup for the event to still be considered a click. - * Dragging the map should not trigger the click and clickout callbacks - * unless the map is moved by less than this tolerance. Defaults to 4. - */ - clickTolerance: 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>} - * layer - {<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: touchstart - * Handle touchstart events - * - * Parameters: - * evt - {Event} - * - * Returns: - * {Boolean} Let the event propagate. - */ - touchstart: function(evt) { - this.startTouch(); - return OpenLayers.Event.isMultiTouch(evt) ? - true : this.mousedown(evt); - }, - - /** - * Method: touchmove - * Handle touchmove events. We just prevent the browser default behavior, - * for Android Webkit not to select text when moving the finger after - * selecting a feature. - * - * Parameters: - * evt - {Event} - */ - touchmove: function(evt) { - OpenLayers.Event.preventDefault(evt); - }, - - /** - * 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) { - // Feature selection is only done with a left click. Other handlers may stop the - // propagation of left-click mousedown events but not right-click mousedown events. - // This mismatch causes problems when comparing the location of the down and up - // events in the click function so it is important ignore right-clicks. - if (OpenLayers.Event.isLeftClick(evt) || OpenLayers.Event.isSingleTouch(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) { - if(this.feature && !this.feature.layer) { - // feature has been destroyed - this.feature = null; - } - var type = evt.type; - var handled = false; - var previouslyIn = !!(this.feature); // previously in a feature - var click = (type == "click" || type == "dblclick" || type == "touchstart"); - this.feature = this.layer.getFeatureFromEvent(evt); - if(this.feature && !this.feature.layer) { - // feature has been destroyed - this.feature = null; - } - if(this.lastFeature && !this.lastFeature.layer) { - // last feature has been destroyed - this.lastFeature = null; - } - if(this.feature) { - if(type === "touchstart") { - // stop the event to prevent Android Webkit from - // "flashing" the map div - OpenLayers.Event.preventDefault(evt); - } - 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 - if(this.lastFeature) { - 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(this.lastFeature && (previouslyIn && inNew || click)) { - // out of last feature for the first time - this.triggerCallback(type, 'out', [this.lastFeature]); - } - // next time the mouse goes in a feature whose geometry type - // doesn't match we don't want to call the 'out' callback - // again, so let's set this.feature to null so that - // previouslyIn will evaluate to false the next time - // we enter handle. Yes, a bit hackish... - this.feature = null; - } - } else if(this.lastFeature && (previouslyIn || click)) { - this.triggerCallback(type, 'out', [this.lastFeature]); - } - return handled; - }, - - /** - * Method: triggerCallback - * Call the callback keyed in the event map with the supplied arguments. - * For click and clickout, the <clickTolerance> is checked first. - * - * Parameters: - * type - {String} - */ - triggerCallback: function(type, mode, args) { - var key = this.EVENTMAP[type][mode]; - if(key) { - if(type == 'click' && this.up && this.down) { - // for click/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.clickTolerance) { - this.callback(key, args); - } - // we're done with this set of events now: clear the cached - // positions so we can't trip over them later (this can occur - // if one of the up/down events gets eaten before it gets to us - // but we still get the click) - this.up = this.down = null; - } 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.type == "removelayer" || 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/StyleMap.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @requires OpenLayers/BaseTypes/Class.js - * @requires OpenLayers/Style.js - * @requires OpenLayers/Feature/Vector.js - */ - -/** - * Class: OpenLayers.StyleMap - */ -OpenLayers.StyleMap = OpenLayers.Class({ - - /** - * Property: styles - * {Object} Hash of {<OpenLayers.Style>}, keyed by names of well known - * rendering intents (e.g. "default", "temporary", "select", "delete"). - */ - 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"]), - "delete": new OpenLayers.Style( - OpenLayers.Feature.Vector.style["delete"]) - }; - - // 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; - this.styles["delete"] = 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); - this.styles["delete"] = 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/Layer/Vector.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @requires OpenLayers/Layer.js - * @requires OpenLayers/Renderer.js - * @requires OpenLayers/StyleMap.js - * @requires OpenLayers/Feature/Vector.js - * @requires OpenLayers/Console.js - * @requires OpenLayers/Lang.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, { - - /** - * APIProperty: events - * {<OpenLayers.Events>} - * - * 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.events>): - * beforefeatureadded - Triggered before a feature is added. Listeners - * will receive an object with a *feature* property referencing the - * feature to be added. To stop the feature from being added, a - * listener should return false. - * beforefeaturesadded - Triggered before an array of features is added. - * Listeners will receive an object with a *features* property - * referencing the feature to be added. To stop the features from - * being added, a listener should return false. - * 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. - * beforefeaturesremoved - Triggered before multiple features are removed. - * Listeners will receive an object with a *features* property - * referencing the features 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. - * beforefeatureselected - Triggered before a feature is selected. Listeners - * will receive an object with a *feature* property referencing the - * feature to be selected. To stop the feature from being selectd, a - * listener should return false. - * 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. - * vertexmodified - Triggered when a vertex within any feature geometry - * has been modified. Listeners will receive an object with a - * *feature* property referencing the modified feature, a *vertex* - * property referencing the vertex modified (always a point geometry), - * and a *pixel* property referencing the pixel location of the - * modification. - * vertexremoved - Triggered when a vertex within any feature geometry - * has been deleted. Listeners will receive an object with a - * *feature* property referencing the modified feature, a *vertex* - * property referencing the vertex modified (always a point geometry), - * and a *pixel* property referencing the pixel location of the - * removal. - * sketchstarted - Triggered when a feature sketch bound for this layer - * is started. Listeners will receive an object with a *feature* - * property referencing the new sketch feature and a *vertex* property - * referencing the creation point. - * sketchmodified - Triggered when a feature sketch bound for this layer - * is modified. Listeners will receive an object with a *vertex* - * property referencing the modified vertex and a *feature* property - * referencing the sketch feature. - * sketchcomplete - Triggered when a feature sketch bound for this layer - * is complete. Listeners will receive an object with a *feature* - * property referencing the sketch feature. By returning false, a - * listener can stop the sketch feature from being added to the layer. - * refresh - Triggered when something wants a strategy to ask the protocol - * for a new set of features. - */ - - /** - * APIProperty: isBaseLayer - * {Boolean} The layer is a base layer. Default is false. Set this property - * in the layer options. - */ - isBaseLayer: false, - - /** - * APIProperty: isFixed - * {Boolean} Whether the layer remains in one place while dragging the - * map. Note that setting this to true will move the layer to the bottom - * of the layer stack. - */ - isFixed: false, - - /** - * APIProperty: features - * {Array(<OpenLayers.Feature.Vector>)} - */ - features: null, - - /** - * Property: filter - * {<OpenLayers.Filter>} The filter set in this layer, - * a strategy launching read requests can combined - * this filter with its own filter. - */ - filter: 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, - - /** - * APIProperty: ratio - * {Float} This specifies the ratio of the size of the visiblity of the Vector Layer features to the size of the map. - */ - ratio: 1, - - /** - * 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) { - 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: clone - * Create a clone of this layer. - * - * Note: Features of the layer are also cloned. - * - * Returns: - * {<OpenLayers.Layer.Vector>} An exact clone of this layer - */ - clone: function (obj) { - - if (obj == null) { - obj = new OpenLayers.Layer.Vector(this.name, this.getOptions()); - } - - //get all additions from superclasses - obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]); - - // copy/set any non-init, non-simple values here - var features = this.features; - var len = features.length; - var clonedFeatures = new Array(len); - for(var i=0; i<len; ++i) { - clonedFeatures[i] = features[i].clone(); - } - obj.features = clonedFeatures; - - return obj; - }, - - /** - * Method: refresh - * Ask the layer to request features again and redraw them. Triggers - * the refresh event if the layer is in range and visible. - * - * Parameters: - * obj - {Object} Optional object with properties for any listener of - * the refresh event. - */ - refresh: function(obj) { - if(this.calculateInRange() && this.visibility) { - this.events.triggerEvent("refresh", obj); - } - }, - - /** - * 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<len; i++) { - var rendererClass = this.renderers[i]; - var renderer = (typeof rendererClass == "function") ? - rendererClass : - OpenLayers.Renderer[rendererClass]; - if (renderer && renderer.prototype.supported()) { - this.renderer = new renderer(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; - - var newSize = this.map.getSize(); - newSize.w = newSize.w * this.ratio; - newSize.h = newSize.h * this.ratio; - this.renderer.setSize(newSize); - } - }, - - /** - * Method: afterAdd - * Called at the end of the map.addLayer sequence. At this point, the map - * will have a base layer. Any autoActivate strategies will be - * activated here. - */ - afterAdd: function() { - 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) { - this.drawn = false; - 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); - - var newSize = this.map.getSize(); - newSize.w = newSize.w * this.ratio; - newSize.h = newSize.h * this.ratio; - this.renderer.setSize(newSize); - }, - - /** - * 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'; - - var viewSize = this.map.getSize(), - viewWidth = viewSize.w, - viewHeight = viewSize.h, - offsetLeft = (viewWidth / 2 * this.ratio) - viewWidth / 2, - offsetTop = (viewHeight / 2 * this.ratio) - viewHeight / 2; - offsetLeft += this.map.layerContainerOriginPx.x; - offsetLeft = -Math.round(offsetLeft); - offsetTop += this.map.layerContainerOriginPx.y; - offsetTop = -Math.round(offsetTop); - - this.div.style.left = offsetLeft + 'px'; - this.div.style.top = offsetTop + 'px'; - - var extent = this.map.getExtent().scale(this.ratio); - 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 (OpenLayers.IS_GECKO === true) { - this.div.scrollLeft = this.div.scrollLeft; - } - - if (!zoomChanged && coordSysUnchanged) { - for (var i in this.unrenderedFeatures) { - var feature = this.unrenderedFeatures[i]; - this.drawFeature(feature); - } - } - } - if (!this.drawn || zoomChanged || !coordSysUnchanged) { - this.drawn = true; - var feature; - for(var i=0, len=this.features.length; i<len; i++) { - this.renderer.locked = (i !== (len - 1)); - feature = this.features[i]; - this.drawFeature(feature); - } - } - }, - - /** - * APIMethod: display - * Hide or show the Layer - * - * Parameters: - * display - {Boolean} - */ - display: function(display) { - OpenLayers.Layer.prototype.display.apply(this, arguments); - // we need to set the display style of the root in case it is attached - // to a foreign layer - var currentDisplay = this.div.style.display; - if(currentDisplay != this.renderer.root.style.display) { - this.renderer.root.style.display = currentDisplay; - } - }, - - /** - * APIMethod: addFeatures - * Add Features to the layer. - * - * Parameters: - * features - {Array(<OpenLayers.Feature.Vector>)} - * options - {Object} - */ - addFeatures: function(features, options) { - if (!(OpenLayers.Util.isArray(features))) { - features = [features]; - } - - var notify = !options || !options.silent; - if(notify) { - var event = {features: features}; - var ret = this.events.triggerEvent("beforefeaturesadded", event); - if(ret === false) { - return; - } - features = event.features; - } - - // Track successfully added features for featuresadded event, since - // beforefeatureadded can veto single features. - var featuresAdded = []; - 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)) { - throw new TypeError('addFeatures: component should be an ' + - this.geometryType.prototype.CLASS_NAME); - } - - //give feature reference to its layer - feature.layer = this; - - if (!feature.style && this.style) { - feature.style = OpenLayers.Util.extend({}, this.style); - } - - if (notify) { - if(this.events.triggerEvent("beforefeatureadded", - {feature: feature}) === false) { - continue; - } - this.preFeatureInsert(feature); - } - - featuresAdded.push(feature); - this.features.push(feature); - this.drawFeature(feature); - - if (notify) { - this.events.triggerEvent("featureadded", { - feature: feature - }); - this.onFeatureInsert(feature); - } - } - - if(notify) { - this.events.triggerEvent("featuresadded", {features: featuresAdded}); - } - }, - - - /** - * APIMethod: removeFeatures - * Remove features from the layer. This erases any drawn features and - * removes them from the layer's control. The beforefeatureremoved - * and featureremoved events will be triggered for each feature. The - * featuresremoved event will be triggered after all features have - * been removed. To supress event triggering, use the silent option. - * - * Parameters: - * features - {Array(<OpenLayers.Feature.Vector>)} List of features to be - * removed. - * options - {Object} Optional properties for changing behavior of the - * removal. - * - * Valid options: - * silent - {Boolean} Supress event triggering. Default is false. - */ - removeFeatures: function(features, options) { - if(!features || features.length === 0) { - return; - } - if (features === this.features) { - return this.removeAllFeatures(options); - } - if (!(OpenLayers.Util.isArray(features))) { - features = [features]; - } - if (features === this.selectedFeatures) { - features = features.slice(); - } - - var notify = !options || !options.silent; - - if (notify) { - this.events.triggerEvent( - "beforefeaturesremoved", {features: features} - ); - } - - 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.eraseFeatures(feature); - } - - //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: removeAllFeatures - * Remove all features from the layer. - * - * Parameters: - * options - {Object} Optional properties for changing behavior of the - * removal. - * - * Valid options: - * silent - {Boolean} Supress event triggering. Default is false. - */ - removeAllFeatures: function(options) { - var notify = !options || !options.silent; - var features = this.features; - if (notify) { - this.events.triggerEvent( - "beforefeaturesremoved", {features: features} - ); - } - var feature; - for (var i = features.length-1; i >= 0; i--) { - feature = features[i]; - if (notify) { - this.events.triggerEvent("beforefeatureremoved", { - feature: feature - }); - } - feature.layer = null; - if (notify) { - this.events.triggerEvent("featureremoved", { - feature: feature - }); - } - } - this.renderer.clear(); - this.features = []; - this.unrenderedFeatures = {}; - this.selectedFeatures = []; - 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; - } - if(features) { - this.removeFeatures(features, options); - for(var i=features.length-1; i>=0; 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. - * - * This function is not designed to be used when adding features to - * the layer (use addFeatures instead). It is meant to be used when - * the style of a feature has changed, or in some other way needs to - * visually updated *after* it has already been added to a layer. You - * must add the feature to the layer for most layer-related events to - * happen. - * - * Parameters: - * feature - {<OpenLayers.Feature.Vector>} - * style - {String | Object} Named render intent or full symbolizer object. - */ - drawFeature: function(feature, style) { - // don't try to draw the feature with the renderer if the layer is not - // drawn itself - if (!this.drawn) { - return; - } - if (typeof style != "object") { - if(!style && feature.state === OpenLayers.State.DELETE) { - style = "delete"; - } - var renderIntent = style || feature.renderIntent; - style = feature.style || this.style; - if (!style) { - style = this.styleMap.createSymbolizer(feature, renderIntent); - } - } - - var drawn = this.renderer.drawFeature(feature, style); - //TODO remove the check for null when we get rid of Renderer.SVG - if (drawn === false || drawn === null) { - this.unrenderedFeatures[feature.id] = feature; - } else { - delete this.unrenderedFeatures[feature.id]; - } - }, - - /** - * 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) { - throw new Error('getFeatureFromEvent called on layer with no ' + - 'renderer. This usually means you destroyed a ' + - 'layer, but not some handler which is associated ' + - 'with it.'); - } - var feature = null; - var featureId = this.renderer.getFeatureIdFromEvent(evt); - if (featureId) { - if (typeof featureId === "string") { - feature = this.getFeatureById(featureId); - } else { - feature = featureId; - } - } - return feature; - }, - - /** - * APIMethod: getFeatureBy - * Given a property value, return the feature if it exists in the features array - * - * Parameters: - * property - {String} - * value - {String} - * - * Returns: - * {<OpenLayers.Feature.Vector>} A feature corresponding to the given - * property value or null if there is no such feature. - */ - getFeatureBy: function(property, value) { - //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][property] == value) { - feature = this.features[i]; - break; - } - } - return feature; - }, - - /** - * 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 or null if there is no such feature. - */ - getFeatureById: function(featureId) { - return this.getFeatureBy('id', featureId); - }, - - /** - * APIMethod: getFeatureByFid - * Given a feature fid, return the feature if it exists in the features array - * - * Parameters: - * featureFid - {String} - * - * Returns: - * {<OpenLayers.Feature.Vector>} A feature corresponding to the given - * featureFid or null if there is no such feature. - */ - getFeatureByFid: function(featureFid) { - return this.getFeatureBy('fid', featureFid); - }, - - /** - * APIMethod: getFeaturesByAttribute - * Returns an array of features that have the given attribute key set to the - * given value. Comparison of attribute values takes care of datatypes, e.g. - * the string '1234' is not equal to the number 1234. - * - * Parameters: - * attrName - {String} - * attrValue - {Mixed} - * - * Returns: - * Array({<OpenLayers.Feature.Vector>}) An array of features that have the - * passed named attribute set to the given value. - */ - getFeaturesByAttribute: function(attrName, attrValue) { - var i, - feature, - len = this.features.length, - foundFeatures = []; - for(i = 0; i < len; i++) { - feature = this.features[i]; - if(feature && feature.attributes) { - if (feature.attributes[attrName] === attrValue) { - foundFeatures.push(feature); - } - } - } - return foundFeatures; - }, - - /** - * 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. - * - * Parameters: - * 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>} or null if the layer has no features with - * geometries. - */ - getDataExtent: function () { - var maxExtent = null; - var features = this.features; - if(features && (features.length > 0)) { - var geometry = null; - for(var i=0, len=features.length; i<len; i++) { - geometry = features[i].geometry; - if (geometry) { - if (maxExtent === null) { - maxExtent = new OpenLayers.Bounds(); - } - maxExtent.extend(geometry.getBounds()); - } - } - } - return maxExtent; - }, - - CLASS_NAME: "OpenLayers.Layer.Vector" -}); -/* ====================================================================== - OpenLayers/Layer/Vector/RootContainer.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @requires OpenLayers/Layer/Vector.js - */ - -/** - * Class: OpenLayers.Layer.Vector.RootContainer - * A special layer type to combine multiple vector layers inside a single - * renderer root container. This class is not supposed to be instantiated - * from user space, it is a helper class for controls that require event - * processing for multiple vector layers. - * - * Inherits from: - * - <OpenLayers.Layer.Vector> - */ -OpenLayers.Layer.Vector.RootContainer = OpenLayers.Class(OpenLayers.Layer.Vector, { - - /** - * Property: displayInLayerSwitcher - * Set to false for this layer type - */ - displayInLayerSwitcher: false, - - /** - * APIProperty: layers - * Layers that are attached to this container. Required config option. - */ - layers: null, - - /** - * Constructor: OpenLayers.Layer.Vector.RootContainer - * Create a new root container for multiple vector layer. This constructor - * is not supposed to be used from user space, it is only to be used by - * controls that need feature selection across multiple vector layers. - * - * Parameters: - * name - {String} A name for the layer - * options - {Object} Optional object with non-default properties to set on - * the layer. - * - * Required options properties: - * layers - {Array(<OpenLayers.Layer.Vector>)} The layers managed by this - * container - * - * Returns: - * {<OpenLayers.Layer.Vector.RootContainer>} A new vector layer root - * container - */ - - /** - * Method: display - */ - display: function() {}, - - /** - * Method: getFeatureFromEvent - * walk through the layers to find the feature returned by the event - * - * Parameters: - * evt - {Object} event object with a feature property - * - * Returns: - * {<OpenLayers.Feature.Vector>} - */ - getFeatureFromEvent: function(evt) { - var layers = this.layers; - var feature; - for(var i=0; i<layers.length; i++) { - feature = layers[i].getFeatureFromEvent(evt); - if(feature) { - return feature; - } - } - }, - - /** - * Method: setMap - * - * Parameters: - * map - {<OpenLayers.Map>} - */ - setMap: function(map) { - OpenLayers.Layer.Vector.prototype.setMap.apply(this, arguments); - this.collectRoots(); - map.events.register("changelayer", this, this.handleChangeLayer); - }, - - /** - * Method: removeMap - * - * Parameters: - * map - {<OpenLayers.Map>} - */ - removeMap: function(map) { - map.events.unregister("changelayer", this, this.handleChangeLayer); - this.resetRoots(); - OpenLayers.Layer.Vector.prototype.removeMap.apply(this, arguments); - }, - - /** - * Method: collectRoots - * Collects the root nodes of all layers this control is configured with - * and moveswien the nodes to this control's layer - */ - collectRoots: function() { - var layer; - // walk through all map layers, because we want to keep the order - for(var i=0; i<this.map.layers.length; ++i) { - layer = this.map.layers[i]; - if(OpenLayers.Util.indexOf(this.layers, layer) != -1) { - layer.renderer.moveRoot(this.renderer); - } - } - }, - - /** - * Method: resetRoots - * Resets the root nodes back into the layers they belong to. - */ - resetRoots: function() { - var layer; - for(var i=0; i<this.layers.length; ++i) { - layer = this.layers[i]; - if(this.renderer && layer.renderer.getRenderLayerId() == this.id) { - this.renderer.moveRoot(layer.renderer); - } - } - }, - - /** - * Method: handleChangeLayer - * Event handler for the map's changelayer event. We need to rebuild - * this container's layer dom if order of one of its layers changes. - * This handler is added with the setMap method, and removed with the - * removeMap method. - * - * Parameters: - * evt - {Object} - */ - handleChangeLayer: function(evt) { - var layer = evt.layer; - if(evt.property == "order" && - OpenLayers.Util.indexOf(this.layers, layer) != -1) { - this.resetRoots(); - this.collectRoots(); - } - }, - - CLASS_NAME: "OpenLayers.Layer.Vector.RootContainer" -}); -/* ====================================================================== - OpenLayers/Control/SelectFeature.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - - -/** - * @requires OpenLayers/Control.js - * @requires OpenLayers/Feature/Vector.js - * @requires OpenLayers/Handler/Feature.js - * @requires OpenLayers/Layer/Vector/RootContainer.js - */ - -/** - * Class: OpenLayers.Control.SelectFeature - * The SelectFeature control selects vector features from a given layer on - * click or hover. - * - * Inherits from: - * - <OpenLayers.Control> - */ -OpenLayers.Control.SelectFeature = OpenLayers.Class(OpenLayers.Control, { - - /** - * APIProperty: events - * {<OpenLayers.Events>} Events instance for listeners and triggering - * control specific events. - * - * Register a listener for a particular event with the following syntax: - * (code) - * control.events.register(type, obj, listener); - * (end) - * - * Supported event types (in addition to those from <OpenLayers.Control.events>): - * beforefeaturehighlighted - Triggered before a feature is highlighted - * featurehighlighted - Triggered when a feature is highlighted - * featureunhighlighted - Triggered when a feature is unhighlighted - * boxselectionstart - Triggered before box selection starts - * boxselectionend - Triggered after box selection ends - */ - - /** - * 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: highlightOnly - * {Boolean} If true do not actually select features (that is place them in - * the layer's selected features array), just highlight them. This property - * has no effect if hover is false. Defaults to false. - */ - highlightOnly: false, - - /** - * APIProperty: box - * {Boolean} Allow feature selection by drawing a box. - */ - box: false, - - /** - * Property: onBeforeSelect - * {Function} Optional function to be called before a feature is selected. - * The function should expect to be called with a feature. - */ - onBeforeSelect: function() {}, - - /** - * 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() {}, - - /** - * Property: scope - * {Object} The scope to use with the onBeforeSelect, onSelect, onUnselect - * callbacks. If null the scope will be this control. - */ - scope: null, - - /** - * 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>} The vector layer with a common renderer - * root for all layers this control is configured with (if an array of - * layers was passed to the constructor), or the vector layer the control - * was configured with (if a single layer was passed to the constructor). - */ - layer: null, - - /** - * Property: layers - * {Array(<OpenLayers.Layer.Vector>)} The layers this control will work on, - * or null if the control was configured with a single layer - */ - layers: 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 - * Create a new control for selecting features. - * - * Parameters: - * layers - {<OpenLayers.Layer.Vector>}, or an array of vector layers. The - * layer(s) this control will select features from. - * options - {Object} - */ - initialize: function(layers, options) { - OpenLayers.Control.prototype.initialize.apply(this, [options]); - - if(this.scope === null) { - this.scope = this; - } - this.initLayer(layers); - 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, this.layer, this.callbacks, - {geometryTypes: this.geometryTypes} - ) - }; - - if (this.box) { - this.handlers.box = new OpenLayers.Handler.Box( - this, {done: this.selectBox}, - {boxDivClassName: "olHandlerBoxSelectFeature"} - ); - } - }, - - /** - * Method: initLayer - * Assign the layer property. If layers is an array, we need to use - * a RootContainer. - * - * Parameters: - * layers - {<OpenLayers.Layer.Vector>}, or an array of vector layers. - */ - initLayer: function(layers) { - if(OpenLayers.Util.isArray(layers)) { - this.layers = layers; - this.layer = new OpenLayers.Layer.Vector.RootContainer( - this.id + "_container", { - layers: layers - } - ); - } else { - this.layer = layers; - } - }, - - /** - * Method: destroy - */ - destroy: function() { - if(this.active && this.layers) { - this.map.removeLayer(this.layer); - } - OpenLayers.Control.prototype.destroy.apply(this, arguments); - if(this.layers) { - this.layer.destroy(); - } - }, - - /** - * Method: activate - * Activates the control. - * - * Returns: - * {Boolean} The control was effectively activated. - */ - activate: function () { - if (!this.active) { - if(this.layers) { - this.map.addLayer(this.layer); - } - 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(); - } - if(this.layers) { - this.map.removeLayer(this.layer); - } - } - 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 layers = this.layers || [this.layer], - layer, feature, l, numExcept; - for(l=0; l<layers.length; ++l) { - layer = layers[l]; - numExcept = 0; - //layer.selectedFeatures is null when layer is destroyed and - //one of it's preremovelayer listener calls setLayer - //with another layer on this control - if(layer.selectedFeatures != null) { - while(layer.selectedFeatures.length > numExcept) { - feature = layer.selectedFeatures[numExcept]; - if(!options || options.except != feature) { - this.unselect(feature); - } else { - ++numExcept; - } - } - } - } - }, - - /** - * 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( - feature.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.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.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) { - var layer = feature.layer; - if(this.hover) { - if(this.highlightOnly) { - this.highlight(feature); - } else if(OpenLayers.Util.indexOf( - 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) { - if(this.highlightOnly) { - // we do nothing if we're not the last highlighter of the - // feature - if(feature._lastHighlighter == this.id) { - // if another select control had highlighted the feature before - // we did it ourself then we use that control to highlight the - // feature as it was before we highlighted it, else we just - // unhighlight it - if(feature._prevHighlighter && - feature._prevHighlighter != this.id) { - delete feature._lastHighlighter; - var control = this.map.getControl( - feature._prevHighlighter); - if(control) { - control.highlight(feature); - } - } else { - this.unhighlight(feature); - } - } - } else { - this.unselect(feature); - } - } - }, - - /** - * Method: highlight - * Redraw feature with the select style. - * - * Parameters: - * feature - {<OpenLayers.Feature.Vector>} - */ - highlight: function(feature) { - var layer = feature.layer; - var cont = this.events.triggerEvent("beforefeaturehighlighted", { - feature : feature - }); - if(cont !== false) { - feature._prevHighlighter = feature._lastHighlighter; - feature._lastHighlighter = this.id; - var style = this.selectStyle || this.renderIntent; - layer.drawFeature(feature, style); - this.events.triggerEvent("featurehighlighted", {feature : feature}); - } - }, - - /** - * Method: unhighlight - * Redraw feature with the "default" style - * - * Parameters: - * feature - {<OpenLayers.Feature.Vector>} - */ - unhighlight: function(feature) { - var layer = feature.layer; - // three cases: - // 1. there's no other highlighter, in that case _prev is undefined, - // and we just need to undef _last - // 2. another control highlighted the feature after we did it, in - // that case _last references this other control, and we just - // need to undef _prev - // 3. another control highlighted the feature before we did it, in - // that case _prev references this other control, and we need to - // set _last to _prev and undef _prev - if(feature._prevHighlighter == undefined) { - delete feature._lastHighlighter; - } else if(feature._prevHighlighter == this.id) { - delete feature._prevHighlighter; - } else { - feature._lastHighlighter = feature._prevHighlighter; - delete feature._prevHighlighter; - } - layer.drawFeature(feature, feature.style || feature.layer.style || - "default"); - this.events.triggerEvent("featureunhighlighted", {feature : 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.onBeforeSelect.call(this.scope, feature); - var layer = feature.layer; - if(cont !== false) { - cont = layer.events.triggerEvent("beforefeatureselected", { - feature: feature - }); - if(cont !== false) { - layer.selectedFeatures.push(feature); - this.highlight(feature); - // if the feature handler isn't involved in the feature - // selection (because the box handler is used or the - // feature is selected programatically) we fake the - // feature handler to allow unselecting on click - if(!this.handlers.feature.lastFeature) { - this.handlers.feature.lastFeature = layer.selectedFeatures[0]; - } - layer.events.triggerEvent("featureselected", {feature: feature}); - this.onSelect.call(this.scope, 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) { - var layer = feature.layer; - // Store feature style for restoration later - this.unhighlight(feature); - OpenLayers.Util.removeItem(layer.selectedFeatures, feature); - layer.events.triggerEvent("featureunselected", {feature: feature}); - this.onUnselect.call(this.scope, 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({ - x: position.left, - y: position.bottom - }); - var maxXY = this.map.getLonLatFromPixel({ - x: position.right, - y: 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; - var layers = this.layers || [this.layer]; - this.events.triggerEvent("boxselectionstart", {layers: layers}); - var layer; - for(var l=0; l<layers.length; ++l) { - layer = layers[l]; - for(var i=0, len = layer.features.length; i<len; ++i) { - var feature = layer.features[i]; - // check if the feature is displayed - if (!feature.getVisibility()) { - continue; - } - - if (this.geometryTypes == null || OpenLayers.Util.indexOf( - this.geometryTypes, feature.geometry.CLASS_NAME) > -1) { - if (bounds.toGeometry().intersects(feature.geometry)) { - if (OpenLayers.Util.indexOf(layer.selectedFeatures, feature) == -1) { - this.select(feature); - } - } - } - } - } - this.multiple = prevMultiple; - this.events.triggerEvent("boxselectionend", {layers: layers}); - } - }, - - /** - * 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); - }, - - /** - * APIMethod: setLayer - * Attach a new layer to the control, overriding any existing layers. - * - * Parameters: - * layers - Array of {<OpenLayers.Layer.Vector>} or a single - * {<OpenLayers.Layer.Vector>} - */ - setLayer: function(layers) { - var isActive = this.active; - this.unselectAll(); - this.deactivate(); - if(this.layers) { - this.layer.destroy(); - this.layers = null; - } - this.initLayer(layers); - this.handlers.feature.layer = this.layer; - if (isActive) { - this.activate(); - } - }, - - CLASS_NAME: "OpenLayers.Control.SelectFeature" -}); -/* ====================================================================== - OpenLayers/Control/Attribution.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @requires OpenLayers/Control.js - */ - -/** - * Class: OpenLayers.Control.Attribution - * The attribution control adds attribution from layers to the map display. - * It uses 'attribution' property of each layer. - * - * Inherits from: - * - <OpenLayers.Control> - */ -OpenLayers.Control.Attribution = - OpenLayers.Class(OpenLayers.Control, { - - /** - * APIProperty: separator - * {String} String used to separate layers. - */ - separator: ", ", - - /** - * APIProperty: template - * {String} Template for the attribution. This has to include the substring - * "${layers}", which will be replaced by the layer specific - * attributions, separated by <separator>. The default is "${layers}". - */ - template: "${layers}", - - /** - * Constructor: OpenLayers.Control.Attribution - * - * Parameters: - * options - {Object} Options for control. - */ - - /** - * 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()) { - // add attribution only if attribution text is unique - if (OpenLayers.Util.indexOf( - attributions, layer.attribution) === -1) { - attributions.push( layer.attribution ); - } - } - } - this.div.innerHTML = OpenLayers.String.format(this.template, { - layers: attributions.join(this.separator) - }); - } - }, - - CLASS_NAME: "OpenLayers.Control.Attribution" -}); -/* ====================================================================== - OpenLayers/Kinetic.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @requires OpenLayers/BaseTypes/Class.js - * @requires OpenLayers/Animation.js - */ - -OpenLayers.Kinetic = OpenLayers.Class({ - - /** - * Property: threshold - * In most cases changing the threshold isn't needed. - * In px/ms, default to 0. - */ - threshold: 0, - - /** - * Property: deceleration - * {Float} the deseleration in px/ms², default to 0.0035. - */ - deceleration: 0.0035, - - /** - * Property: nbPoints - * {Integer} the number of points we use to calculate the kinetic - * initial values. - */ - nbPoints: 100, - - /** - * Property: delay - * {Float} time to consider to calculate the kinetic initial values. - * In ms, default to 200. - */ - delay: 200, - - /** - * Property: points - * List of points use to calculate the kinetic initial values. - */ - points: undefined, - - /** - * Property: timerId - * ID of the timer. - */ - timerId: undefined, - - /** - * Constructor: OpenLayers.Kinetic - * - * Parameters: - * options - {Object} - */ - initialize: function(options) { - OpenLayers.Util.extend(this, options); - }, - - /** - * Method: begin - * Begins the dragging. - */ - begin: function() { - OpenLayers.Animation.stop(this.timerId); - this.timerId = undefined; - this.points = []; - }, - - /** - * Method: update - * Updates during the dragging. - * - * Parameters: - * xy - {<OpenLayers.Pixel>} The new position. - */ - update: function(xy) { - this.points.unshift({xy: xy, tick: new Date().getTime()}); - if (this.points.length > this.nbPoints) { - this.points.pop(); - } - }, - - /** - * Method: end - * Ends the dragging, start the kinetic. - * - * Parameters: - * xy - {<OpenLayers.Pixel>} The last position. - * - * Returns: - * {Object} An object with two properties: "speed", and "theta". The - * "speed" and "theta" values are to be passed to the move - * function when starting the animation. - */ - end: function(xy) { - var last, now = new Date().getTime(); - for (var i = 0, l = this.points.length, point; i < l; i++) { - point = this.points[i]; - if (now - point.tick > this.delay) { - break; - } - last = point; - } - if (!last) { - return; - } - var time = new Date().getTime() - last.tick; - var dist = Math.sqrt(Math.pow(xy.x - last.xy.x, 2) + - Math.pow(xy.y - last.xy.y, 2)); - var speed = dist / time; - if (speed == 0 || speed < this.threshold) { - return; - } - var theta = Math.asin((xy.y - last.xy.y) / dist); - if (last.xy.x <= xy.x) { - theta = Math.PI - theta; - } - return {speed: speed, theta: theta}; - }, - - /** - * Method: move - * Launch the kinetic move pan. - * - * Parameters: - * info - {Object} An object with two properties, "speed", and "theta". - * These values are those returned from the "end" call. - * callback - {Function} Function called on every step of the animation, - * receives x, y (values to pan), end (is the last point). - */ - move: function(info, callback) { - var v0 = info.speed; - var fx = Math.cos(info.theta); - var fy = -Math.sin(info.theta); - - var initialTime = new Date().getTime(); - - var lastX = 0; - var lastY = 0; - - var timerCallback = function() { - if (this.timerId == null) { - return; - } - - var t = new Date().getTime() - initialTime; - - var p = (-this.deceleration * Math.pow(t, 2)) / 2.0 + v0 * t; - var x = p * fx; - var y = p * fy; - - var args = {}; - args.end = false; - var v = -this.deceleration * t + v0; - - if (v <= 0) { - OpenLayers.Animation.stop(this.timerId); - this.timerId = null; - args.end = true; - } - - args.x = x - lastX; - args.y = y - lastY; - lastX = x; - lastY = y; - callback(args.x, args.y, args.end); - }; - - this.timerId = OpenLayers.Animation.start( - OpenLayers.Function.bind(timerCallback, this) - ); - }, - - CLASS_NAME: "OpenLayers.Kinetic" -}); -/* ====================================================================== - OpenLayers/Filter/Logical.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository 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.Logical.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. - * - * Parameters: - * context - {Object} Context to use in evaluating the filter. A vector - * feature may also be provided to evaluate feature attributes in - * comparison filters or geometries in spatial filters. - * - * Returns: - * {Boolean} The filter applies. - */ - evaluate: function(context) { - var i, len; - switch(this.type) { - case OpenLayers.Filter.Logical.AND: - for (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 (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)); - } - return undefined; - }, - - /** - * APIMethod: clone - * Clones this filter. - * - * Returns: - * {<OpenLayers.Filter.Logical>} Clone of this filter. - */ - clone: function() { - var filters = []; - for(var i=0, len=this.filters.length; i<len; ++i) { - filters.push(this.filters[i].clone()); - } - return new OpenLayers.Filter.Logical({ - type: this.type, - filters: filters - }); - }, - - CLASS_NAME: "OpenLayers.Filter.Logical" -}); - - -OpenLayers.Filter.Logical.AND = "&&"; -OpenLayers.Filter.Logical.OR = "||"; -OpenLayers.Filter.Logical.NOT = "!"; -/* ====================================================================== - OpenLayers/Handler/Drag.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @requires OpenLayers/Handler.js - */ - -/** - * Class: OpenLayers.Handler.Drag - * The drag handler is used to deal with sequences of browser events related - * to dragging. The handler is used by controls that want to know when - * a drag sequence begins, when a drag is happening, and when it has - * finished. - * - * Controls that use the drag handler typically construct it with callbacks - * for 'down', 'move', and 'done'. Callbacks for these keys are called - * when the drag begins, with each move, and when the drag is done. In - * addition, controls can have callbacks keyed to 'up' and 'out' if they - * care to differentiate between the types of events that correspond with - * the end of a drag sequence. If no drag actually occurs (no mouse move) - * the 'down' and 'up' callbacks will be called, but not the 'done' - * callback. - * - * Create a new drag handler with the <OpenLayers.Handler.Drag> constructor. - * - * Inherits from: - * - <OpenLayers.Handler> - */ -OpenLayers.Handler.Drag = OpenLayers.Class(OpenLayers.Handler, { - - /** - * Property: started - * {Boolean} When a mousedown or touchstart event is received, we want to - * record it, but not set 'dragging' until the mouse moves after starting. - */ - started: false, - - /** - * Property: stopDown - * {Boolean} Stop propagation of mousedown events from getting to listeners - * on the same element. Default is true. - */ - stopDown: true, - - /** - * Property: dragging - * {Boolean} - */ - dragging: false, - - /** - * Property: last - * {<OpenLayers.Pixel>} The last pixel location of the drag. - */ - last: null, - - /** - * Property: start - * {<OpenLayers.Pixel>} The first pixel location of the drag. - */ - start: null, - - /** - * Property: lastMoveEvt - * {Object} The last mousemove event that occurred. Used to - * position the map correctly when our "delay drag" - * timeout expired. - */ - lastMoveEvt: null, - - /** - * Property: oldOnselectstart - * {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, - - /** - * APIProperty: documentDrag - * {Boolean} If set to true, the handler will also handle mouse moves when - * the cursor has moved out of the map viewport. Default is false. - */ - documentDrag: false, - - /** - * Property: documentEvents - * {Boolean} Are we currently observing document events? - */ - documentEvents: null, - - /** - * Constructor: OpenLayers.Handler.Drag - * Returns OpenLayers.Handler.Drag - * - * 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 'move' and 'done' are supported. You can also speficy - * callbacks for 'down', 'up', and 'out' to respond to those events. - * options - {Object} - */ - initialize: function(control, callbacks, options) { - OpenLayers.Handler.prototype.initialize.apply(this, arguments); - - if (this.documentDrag === true) { - var me = this; - this._docMove = function(evt) { - me.mousemove({ - xy: {x: evt.clientX, y: evt.clientY}, - element: document - }); - }; - this._docUp = function(evt) { - me.mouseup({xy: {x: evt.clientX, y: evt.clientY}}); - }; - } - }, - - - /** - * Method: dragstart - * This private method is factorized from mousedown and touchstart methods - * - * Parameters: - * evt - {Event} The event - * - * Returns: - * {Boolean} Let the event propagate. - */ - dragstart: function (evt) { - var propagate = true; - this.dragging = false; - if (this.checkModifiers(evt) && - (OpenLayers.Event.isLeftClick(evt) || - OpenLayers.Event.isSingleTouch(evt))) { - this.started = true; - this.start = evt.xy; - this.last = evt.xy; - OpenLayers.Element.addClass( - this.map.viewPortDiv, "olDragDown" - ); - this.down(evt); - this.callback("down", [evt.xy]); - - // prevent document dragging - OpenLayers.Event.preventDefault(evt); - - if(!this.oldOnselectstart) { - this.oldOnselectstart = document.onselectstart ? - document.onselectstart : OpenLayers.Function.True; - } - document.onselectstart = OpenLayers.Function.False; - - propagate = !this.stopDown; - } else { - this.started = false; - this.start = null; - this.last = null; - } - return propagate; - }, - - /** - * Method: dragmove - * This private method is factorized from mousemove and touchmove methods - * - * Parameters: - * evt - {Event} The event - * - * Returns: - * {Boolean} Let the event propagate. - */ - dragmove: function (evt) { - this.lastMoveEvt = evt; - if (this.started && !this.timeoutId && (evt.xy.x != this.last.x || - evt.xy.y != this.last.y)) { - if(this.documentDrag === true && this.documentEvents) { - if(evt.element === document) { - this.adjustXY(evt); - // do setEvent manually because the documentEvents are not - // registered with the map - this.setEvent(evt); - } else { - this.removeDocumentEvents(); - } - } - 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 = OpenLayers.Function.False; - } - this.last = evt.xy; - } - return true; - }, - - /** - * Method: dragend - * This private method is factorized from mouseup and touchend methods - * - * Parameters: - * evt - {Event} The event - * - * Returns: - * {Boolean} Let the event propagate. - */ - dragend: function (evt) { - if (this.started) { - if(this.documentDrag === true && this.documentEvents) { - this.adjustXY(evt); - this.removeDocumentEvents(); - } - var dragged = (this.start != this.last); - this.started = false; - this.dragging = false; - OpenLayers.Element.removeClass( - this.map.viewPortDiv, "olDragDown" - ); - this.up(evt); - this.callback("up", [evt.xy]); - if(dragged) { - this.callback("done", [evt.xy]); - } - document.onselectstart = this.oldOnselectstart; - } - return true; - }, - - /** - * The four methods below (down, move, up, and out) are used by subclasses - * to do their own processing related to these mouse events. - */ - - /** - * Method: down - * This method is called during the handling of the mouse down event. - * Subclasses can do their own processing here. - * - * Parameters: - * evt - {Event} The mouse down event - */ - down: function(evt) { - }, - - /** - * Method: move - * This method is called during the handling of the mouse move event. - * Subclasses can do their own processing here. - * - * Parameters: - * evt - {Event} The mouse move event - * - */ - move: function(evt) { - }, - - /** - * Method: up - * This method is called during the handling of the mouse up event. - * Subclasses can do their own processing here. - * - * Parameters: - * evt - {Event} The mouse up event - */ - up: function(evt) { - }, - - /** - * Method: out - * This method is called during the handling of the mouse out event. - * Subclasses can do their own processing here. - * - * Parameters: - * evt - {Event} The mouse out event - */ - out: function(evt) { - }, - - /** - * The methods below are part of the magic of event handling. Because - * they are named like browser events, they are registered as listeners - * for the events they represent. - */ - - /** - * Method: mousedown - * Handle mousedown events - * - * Parameters: - * evt - {Event} - * - * Returns: - * {Boolean} Let the event propagate. - */ - mousedown: function(evt) { - return this.dragstart(evt); - }, - - /** - * Method: touchstart - * Handle touchstart events - * - * Parameters: - * evt - {Event} - * - * Returns: - * {Boolean} Let the event propagate. - */ - touchstart: function(evt) { - this.startTouch(); - return this.dragstart(evt); - }, - - /** - * Method: mousemove - * Handle mousemove events - * - * Parameters: - * evt - {Event} - * - * Returns: - * {Boolean} Let the event propagate. - */ - mousemove: function(evt) { - return this.dragmove(evt); - }, - - /** - * Method: touchmove - * Handle touchmove events - * - * Parameters: - * evt - {Event} - * - * Returns: - * {Boolean} Let the event propagate. - */ - touchmove: function(evt) { - return this.dragmove(evt); - }, - - /** - * Method: removeTimeout - * Private. Called by mousemove() to remove the drag timeout. - */ - removeTimeout: function() { - this.timeoutId = null; - // if timeout expires while we're still dragging (mouseup - // hasn't occurred) then call mousemove to move to the - // correct position - if(this.dragging) { - this.mousemove(this.lastMoveEvt); - } - }, - - /** - * Method: mouseup - * Handle mouseup events - * - * Parameters: - * evt - {Event} - * - * Returns: - * {Boolean} Let the event propagate. - */ - mouseup: function(evt) { - return this.dragend(evt); - }, - - /** - * Method: touchend - * Handle touchend events - * - * Parameters: - * evt - {Event} - * - * Returns: - * {Boolean} Let the event propagate. - */ - touchend: function(evt) { - // override evt.xy with last position since touchend does not have - // any touch position - evt.xy = this.last; - return this.dragend(evt); - }, - - /** - * Method: mouseout - * Handle mouseout events - * - * Parameters: - * evt - {Event} - * - * Returns: - * {Boolean} Let the event propagate. - */ - mouseout: function (evt) { - if (this.started && OpenLayers.Util.mouseLeft(evt, this.map.viewPortDiv)) { - if(this.documentDrag === true) { - this.addDocumentEvents(); - } else { - var dragged = (this.start != this.last); - this.started = false; - this.dragging = false; - OpenLayers.Element.removeClass( - this.map.viewPortDiv, "olDragDown" - ); - this.out(evt); - this.callback("out", []); - if(dragged) { - this.callback("done", [evt.xy]); - } - if(document.onselectstart) { - document.onselectstart = this.oldOnselectstart; - } - } - } - return true; - }, - - /** - * Method: click - * The drag handler captures the click event. If something else registers - * for clicks on the same element, its listener will not be called - * after a drag. - * - * Parameters: - * evt - {Event} - * - * Returns: - * {Boolean} Let the event propagate. - */ - click: function (evt) { - // let the click event propagate only if the mouse moved - return (this.start == this.last); - }, - - /** - * Method: activate - * Activate the handler. - * - * Returns: - * {Boolean} The handler was successfully activated. - */ - activate: function() { - var activated = false; - if(OpenLayers.Handler.prototype.activate.apply(this, arguments)) { - this.dragging = false; - activated = true; - } - return activated; - }, - - /** - * Method: 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.started = false; - this.dragging = false; - this.start = null; - this.last = null; - deactivated = true; - OpenLayers.Element.removeClass( - this.map.viewPortDiv, "olDragDown" - ); - } - return deactivated; - }, - - /** - * Method: adjustXY - * Converts event coordinates that are relative to the document body to - * ones that are relative to the map viewport. The latter is the default in - * OpenLayers. - * - * Parameters: - * evt - {Object} - */ - adjustXY: function(evt) { - var pos = OpenLayers.Util.pagePosition(this.map.viewPortDiv); - evt.xy.x -= pos[0]; - evt.xy.y -= pos[1]; - }, - - /** - * Method: addDocumentEvents - * Start observing document events when documentDrag is true and the mouse - * cursor leaves the map viewport while dragging. - */ - addDocumentEvents: function() { - OpenLayers.Element.addClass(document.body, "olDragDown"); - this.documentEvents = true; - OpenLayers.Event.observe(document, "mousemove", this._docMove); - OpenLayers.Event.observe(document, "mouseup", this._docUp); - }, - - /** - * Method: removeDocumentEvents - * Stops observing document events when documentDrag is true and the mouse - * cursor re-enters the map viewport while dragging. - */ - removeDocumentEvents: function() { - OpenLayers.Element.removeClass(document.body, "olDragDown"); - this.documentEvents = false; - OpenLayers.Event.stopObserving(document, "mousemove", this._docMove); - OpenLayers.Event.stopObserving(document, "mouseup", this._docUp); - }, - - CLASS_NAME: "OpenLayers.Handler.Drag" -}); -/* ====================================================================== - OpenLayers/Handler/Box.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @requires OpenLayers/Handler.js - * @requires OpenLayers/Handler/Drag.js - */ - -/** - * Class: OpenLayers.Handler.Box - * Handler for dragging a rectangle across the map. Box is displayed - * on mouse down, moves on mouse move, and is finished on mouse up. - * - * Inherits from: - * - <OpenLayers.Handler> - */ -OpenLayers.Handler.Box = OpenLayers.Class(OpenLayers.Handler, { - - /** - * Property: dragHandler - * {<OpenLayers.Handler.Drag>} - */ - dragHandler: null, - - /** - * APIProperty: boxDivClassName - * {String} The CSS class to use for drawing the box. Default is - * olHandlerBoxZoomBox - */ - boxDivClassName: 'olHandlerBoxZoomBox', - - /** - * Property: boxOffsets - * {Object} Caches box offsets from css. This is used by the getBoxOffsets - * method. - */ - boxOffsets: null, - - /** - * Constructor: OpenLayers.Handler.Box - * - * Parameters: - * control - {<OpenLayers.Control>} - * callbacks - {Object} An object with a properties whose values are - * functions. Various callbacks described below. - * options - {Object} - * - * Named callbacks: - * start - Called when the box drag operation starts. - * done - Called when the box drag operation is finished. - * The callback should expect to receive a single argument, the box - * bounds or a pixel. If the box dragging didn't span more than a 5 - * pixel distance, a pixel will be returned instead of a bounds object. - */ - initialize: function(control, callbacks, options) { - OpenLayers.Handler.prototype.initialize.apply(this, arguments); - this.dragHandler = new OpenLayers.Handler.Drag( - this, - { - down: this.startBox, - move: this.moveBox, - out: this.removeBox, - up: this.endBox - }, - {keyMask: this.keyMask} - ); - }, - - /** - * Method: destroy - */ - destroy: function() { - OpenLayers.Handler.prototype.destroy.apply(this, arguments); - if (this.dragHandler) { - this.dragHandler.destroy(); - this.dragHandler = null; - } - }, - - /** - * Method: setMap - */ - setMap: function (map) { - OpenLayers.Handler.prototype.setMap.apply(this, arguments); - if (this.dragHandler) { - this.dragHandler.setMap(map); - } - }, - - /** - * Method: startBox - * - * Parameters: - * xy - {<OpenLayers.Pixel>} - */ - startBox: function (xy) { - this.callback("start", []); - this.zoomBox = OpenLayers.Util.createDiv('zoomBox', { - x: -9999, y: -9999 - }); - this.zoomBox.className = this.boxDivClassName; - this.zoomBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1; - - this.map.viewPortDiv.appendChild(this.zoomBox); - - OpenLayers.Element.addClass( - this.map.viewPortDiv, "olDrawBox" - ); - }, - - /** - * Method: moveBox - */ - 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); - - var offset = this.getBoxOffsets(); - this.zoomBox.style.width = (deltaX + offset.width + 1) + "px"; - this.zoomBox.style.height = (deltaY + offset.height + 1) + "px"; - this.zoomBox.style.left = (xy.x < startX ? - startX - deltaX - offset.left : startX - offset.left) + "px"; - this.zoomBox.style.top = (xy.y < startY ? - startY - deltaY - offset.top : startY - offset.top) + "px"; - }, - - /** - * Method: endBox - */ - 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(); // i.e. OL.Pixel - } - this.removeBox(); - - this.callback("done", [result]); - }, - - /** - * Method: removeBox - * Remove the zoombox from the screen and nullify our reference to it. - */ - removeBox: function() { - this.map.viewPortDiv.removeChild(this.zoomBox); - this.zoomBox = null; - this.boxOffsets = null; - OpenLayers.Element.removeClass( - this.map.viewPortDiv, "olDrawBox" - ); - - }, - - /** - * Method: activate - */ - activate: function () { - if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) { - this.dragHandler.activate(); - return true; - } else { - return false; - } - }, - - /** - * Method: deactivate - */ - deactivate: function () { - if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) { - if (this.dragHandler.deactivate()) { - if (this.zoomBox) { - this.removeBox(); - } - } - return true; - } else { - return false; - } - }, - - /** - * Method: getBoxOffsets - * Determines border offsets for a box, according to the box model. - * - * Returns: - * {Object} an object with the following offsets: - * - left - * - right - * - top - * - bottom - * - width - * - height - */ - getBoxOffsets: function() { - if (!this.boxOffsets) { - // Determine the box model. If the testDiv's clientWidth is 3, then - // the borders are outside and we are dealing with the w3c box - // model. Otherwise, the browser uses the traditional box model and - // the borders are inside the box bounds, leaving us with a - // clientWidth of 1. - var testDiv = document.createElement("div"); - //testDiv.style.visibility = "hidden"; - testDiv.style.position = "absolute"; - testDiv.style.border = "1px solid black"; - testDiv.style.width = "3px"; - document.body.appendChild(testDiv); - var w3cBoxModel = testDiv.clientWidth == 3; - document.body.removeChild(testDiv); - - var left = parseInt(OpenLayers.Element.getStyle(this.zoomBox, - "border-left-width")); - var right = parseInt(OpenLayers.Element.getStyle( - this.zoomBox, "border-right-width")); - var top = parseInt(OpenLayers.Element.getStyle(this.zoomBox, - "border-top-width")); - var bottom = parseInt(OpenLayers.Element.getStyle( - this.zoomBox, "border-bottom-width")); - this.boxOffsets = { - left: left, - right: right, - top: top, - bottom: bottom, - width: w3cBoxModel === false ? left + right : 0, - height: w3cBoxModel === false ? top + bottom : 0 - }; - } - return this.boxOffsets; - }, - - CLASS_NAME: "OpenLayers.Handler.Box" -}); -/* ====================================================================== - OpenLayers/Control/ZoomBox.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @requires OpenLayers/Control.js - * @requires OpenLayers/Handler/Box.js - */ - -/** - * Class: OpenLayers.Control.ZoomBox - * The ZoomBox control enables zooming directly to a given extent, by drawing - * a box on the map. The box is drawn by holding down shift, whilst dragging - * the mouse. - * - * Inherits from: - * - <OpenLayers.Control> - */ -OpenLayers.Control.ZoomBox = OpenLayers.Class(OpenLayers.Control, { - /** - * Property: type - * {OpenLayers.Control.TYPE} - */ - type: OpenLayers.Control.TYPE_TOOL, - - /** - * Property: out - * {Boolean} Should the control be used for zooming out? - */ - out: false, - - /** - * APIProperty: keyMask - * {Integer} Zoom only occurs if the keyMask matches the combination of - * keys down. Use bitwise operators and one or more of the - * <OpenLayers.Handler> constants to construct a keyMask. Leave null if - * not used mask. Default is null. - */ - keyMask: null, - - /** - * APIProperty: alwaysZoom - * {Boolean} Always zoom in/out when box drawn, even if the zoom level does - * not change. - */ - alwaysZoom: false, - - /** - * APIProperty: zoomOnClick - * {Boolean} Should we zoom when no box was dragged, i.e. the user only - * clicked? Default is true. - */ - zoomOnClick: true, - - /** - * Method: draw - */ - draw: function() { - this.handler = new OpenLayers.Handler.Box( this, - {done: this.zoomBox}, {keyMask: this.keyMask} ); - }, - - /** - * Method: zoomBox - * - * Parameters: - * position - {<OpenLayers.Bounds>} or {<OpenLayers.Pixel>} - */ - zoomBox: function (position) { - if (position instanceof OpenLayers.Bounds) { - var bounds, - targetCenterPx = position.getCenterPixel(); - if (!this.out) { - var minXY = this.map.getLonLatFromPixel({ - x: position.left, - y: position.bottom - }); - var maxXY = this.map.getLonLatFromPixel({ - x: position.right, - y: position.top - }); - bounds = new OpenLayers.Bounds(minXY.lon, minXY.lat, - maxXY.lon, maxXY.lat); - } else { - var pixWidth = position.right - position.left; - var pixHeight = position.bottom - position.top; - var zoomFactor = Math.min((this.map.size.h / pixHeight), - (this.map.size.w / pixWidth)); - var extent = this.map.getExtent(); - var center = this.map.getLonLatFromPixel(targetCenterPx); - 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; - bounds = new OpenLayers.Bounds(xmin, ymin, xmax, ymax); - } - // always zoom in/out - var lastZoom = this.map.getZoom(), - size = this.map.getSize(), - centerPx = {x: size.w / 2, y: size.h / 2}, - zoom = this.map.getZoomForExtent(bounds), - oldRes = this.map.getResolution(), - newRes = this.map.getResolutionForZoom(zoom); - if (oldRes == newRes) { - this.map.setCenter(this.map.getLonLatFromPixel(targetCenterPx)); - } else { - var zoomOriginPx = { - x: (oldRes * targetCenterPx.x - newRes * centerPx.x) / - (oldRes - newRes), - y: (oldRes * targetCenterPx.y - newRes * centerPx.y) / - (oldRes - newRes) - }; - this.map.zoomTo(zoom, zoomOriginPx); - } - if (lastZoom == this.map.getZoom() && this.alwaysZoom == true){ - this.map.zoomTo(lastZoom + (this.out ? -1 : 1)); - } - } else if (this.zoomOnClick) { // it's a pixel - if (!this.out) { - this.map.zoomTo(this.map.getZoom() + 1, position); - } else { - this.map.zoomTo(this.map.getZoom() - 1, position); - } - } - }, - - CLASS_NAME: "OpenLayers.Control.ZoomBox" -}); -/* ====================================================================== - OpenLayers/Control/DragPan.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @requires OpenLayers/Control.js - * @requires OpenLayers/Handler/Drag.js - */ - -/** - * Class: OpenLayers.Control.DragPan - * The DragPan control pans the map with a drag of the mouse. - * - * Inherits from: - * - <OpenLayers.Control> - */ -OpenLayers.Control.DragPan = OpenLayers.Class(OpenLayers.Control, { - - /** - * Property: type - * {OpenLayers.Control.TYPES} - */ - type: OpenLayers.Control.TYPE_TOOL, - - /** - * Property: panned - * {Boolean} The map moved. - */ - panned: false, - - /** - * Property: interval - * {Integer} The number of milliseconds that should ellapse before - * panning the map again. Defaults to 0 milliseconds, which means that - * no separate cycle is used for panning. In most cases you won't want - * to change this value. For slow machines/devices larger values can be - * tried out. - */ - interval: 0, - - /** - * APIProperty: documentDrag - * {Boolean} If set to true, mouse dragging will continue even if the - * mouse cursor leaves the map viewport. Default is false. - */ - documentDrag: false, - - /** - * Property: kinetic - * {<OpenLayers.Kinetic>} The OpenLayers.Kinetic object. - */ - kinetic: null, - - /** - * APIProperty: enableKinetic - * {Boolean} Set this option to enable "kinetic dragging". Can be - * set to true or to an object. If set to an object this - * object will be passed to the {<OpenLayers.Kinetic>} - * constructor. Defaults to true. - * To get kinetic dragging, ensure that OpenLayers/Kinetic.js is - * included in your build config. - */ - enableKinetic: true, - - /** - * APIProperty: kineticInterval - * {Integer} Interval in milliseconds between 2 steps in the "kinetic - * scrolling". Applies only if enableKinetic is set. Defaults - * to 10 milliseconds. - */ - kineticInterval: 10, - - - /** - * Method: draw - * Creates a Drag handler, using <panMap> and - * <panMapDone> as callbacks. - */ - draw: function() { - if (this.enableKinetic && OpenLayers.Kinetic) { - var config = {interval: this.kineticInterval}; - if(typeof this.enableKinetic === "object") { - config = OpenLayers.Util.extend(config, this.enableKinetic); - } - this.kinetic = new OpenLayers.Kinetic(config); - } - this.handler = new OpenLayers.Handler.Drag(this, { - "move": this.panMap, - "done": this.panMapDone, - "down": this.panMapStart - }, { - interval: this.interval, - documentDrag: this.documentDrag - } - ); - }, - - /** - * Method: panMapStart - */ - panMapStart: function() { - if(this.kinetic) { - this.kinetic.begin(); - } - }, - - /** - * Method: panMap - * - * Parameters: - * xy - {<OpenLayers.Pixel>} Pixel of the mouse position - */ - panMap: function(xy) { - if(this.kinetic) { - this.kinetic.update(xy); - } - this.panned = true; - this.map.pan( - this.handler.last.x - xy.x, - this.handler.last.y - xy.y, - {dragging: true, animate: false} - ); - }, - - /** - * Method: panMapDone - * Finish the panning operation. Only call setCenter (through <panMap>) - * if the map has actually been moved. - * - * Parameters: - * xy - {<OpenLayers.Pixel>} Pixel of the mouse position - */ - panMapDone: function(xy) { - if(this.panned) { - var res = null; - if (this.kinetic) { - res = this.kinetic.end(xy); - } - this.map.pan( - this.handler.last.x - xy.x, - this.handler.last.y - xy.y, - {dragging: !!res, animate: false} - ); - if (res) { - var self = this; - this.kinetic.move(res, function(x, y, end) { - self.map.pan(x, y, {dragging: !end, animate: false}); - }); - } - this.panned = false; - } - }, - - CLASS_NAME: "OpenLayers.Control.DragPan" -}); -/* ====================================================================== - OpenLayers/Handler/Click.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @requires OpenLayers/Handler.js - */ - -/** - * Class: OpenLayers.Handler.Click - * A handler for mouse clicks. The intention of this handler is to give - * controls more flexibility with handling clicks. Browsers trigger - * click events twice for a double-click. In addition, the mousedown, - * mousemove, mouseup sequence fires a click event. With this handler, - * controls can decide whether to ignore clicks associated with a double - * click. By setting a <pixelTolerance>, controls can also ignore clicks - * that include a drag. Create a new instance with the - * <OpenLayers.Handler.Click> constructor. - * - * Inherits from: - * - <OpenLayers.Handler> - */ -OpenLayers.Handler.Click = OpenLayers.Class(OpenLayers.Handler, { - /** - * APIProperty: delay - * {Number} Number of milliseconds between clicks before the event is - * considered a double-click. - */ - delay: 300, - - /** - * APIProperty: single - * {Boolean} Handle single clicks. Default is true. If false, clicks - * will not be reported. If true, single-clicks will be reported. - */ - single: true, - - /** - * APIProperty: double - * {Boolean} Handle double-clicks. Default is false. - */ - 'double': false, - - /** - * APIProperty: pixelTolerance - * {Number} Maximum number of pixels between mouseup and mousedown for an - * event to be considered a click. Default is 0. If set to an - * integer value, clicks with a drag greater than the value will be - * ignored. This property can only be set when the handler is - * constructed. - */ - pixelTolerance: 0, - - /** - * APIProperty: dblclickTolerance - * {Number} Maximum distance in pixels between clicks for a sequence of - * events to be considered a double click. Default is 13. If the - * distance between two clicks is greater than this value, a double- - * click will not be fired. - */ - dblclickTolerance: 13, - - /** - * APIProperty: stopSingle - * {Boolean} Stop other listeners from being notified of clicks. Default - * is false. If true, any listeners registered before this one for - * click or rightclick events will not be notified. - */ - stopSingle: false, - - /** - * APIProperty: stopDouble - * {Boolean} Stop other listeners from being notified of double-clicks. - * Default is false. If true, any click listeners registered before - * this one will not be notified of *any* double-click events. - * - * The one caveat with stopDouble is that given a map with two click - * handlers, one with stopDouble true and the other with stopSingle - * true, the stopSingle handler should be activated last to get - * uniform cross-browser performance. Since IE triggers one click - * with a dblclick and FF triggers two, if a stopSingle handler is - * activated first, all it gets in IE is a single click when the - * second handler stops propagation on the dblclick. - */ - stopDouble: false, - - /** - * Property: timerId - * {Number} The id of the timeout waiting to clear the <delayedCall>. - */ - timerId: null, - - /** - * Property: down - * {Object} Object that store relevant information about the last - * mousedown or touchstart. Its 'xy' OpenLayers.Pixel property gives - * the average location of the mouse/touch event. Its 'touches' - * property records clientX/clientY of each touches. - */ - down: null, - - /** - * Property: last - * {Object} Object that store relevant information about the last - * mousemove or touchmove. Its 'xy' OpenLayers.Pixel property gives - * the average location of the mouse/touch event. Its 'touches' - * property records clientX/clientY of each touches. - */ - last: null, - - /** - * Property: first - * {Object} When waiting for double clicks, this object will store - * information about the first click in a two click sequence. - */ - first: 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. - * - * Parameters: - * control - {<OpenLayers.Control>} The control that is making use of - * this handler. If a handler is being used without a control, the - * handler's setMap method must be overridden to deal properly with - * the map. - * callbacks - {Object} An object with keys corresponding to callbacks - * that will be called by the handler. The callbacks should - * expect to recieve a single argument, the click event. - * Callbacks for 'click' and 'dblclick' are supported. - * options - {Object} Optional object whose properties will be set on the - * handler. - */ - - /** - * Method: touchstart - * Handle touchstart. - * - * Returns: - * {Boolean} Continue propagating this event. - */ - touchstart: function(evt) { - this.startTouch(); - this.down = this.getEventInfo(evt); - this.last = this.getEventInfo(evt); - return true; - }, - - /** - * Method: touchmove - * Store position of last move, because touchend event can have - * an empty "touches" property. - * - * Returns: - * {Boolean} Continue propagating this event. - */ - touchmove: function(evt) { - this.last = this.getEventInfo(evt); - return true; - }, - - /** - * Method: touchend - * Correctly set event xy property, and add lastTouches to have - * touches property from last touchstart or touchmove - * - * Returns: - * {Boolean} Continue propagating this event. - */ - touchend: function(evt) { - // touchstart may not have been allowed to propagate - if (this.down) { - evt.xy = this.last.xy; - evt.lastTouches = this.last.touches; - this.handleSingle(evt); - this.down = null; - } - return true; - }, - - /** - * Method: mousedown - * Handle mousedown. - * - * Returns: - * {Boolean} Continue propagating this event. - */ - mousedown: function(evt) { - this.down = this.getEventInfo(evt); - this.last = this.getEventInfo(evt); - return true; - }, - - /** - * 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)) { - propagate = 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]); - } - }, - - /** - * Method: click - * Handle click events from the browser. This is registered as a listener - * for click events and should not be called from other events in this - * handler. - * - * Returns: - * {Boolean} Continue propagating this event. - */ - click: function(evt) { - if (!this.last) { - this.last = this.getEventInfo(evt); - } - this.handleSingle(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 - * dblclick to properly handle single clicks. This method is registered - * as a listener for the dblclick browser event. It should *not* be - * called by other methods in this handler. - * - * Returns: - * {Boolean} Continue propagating this event. - */ - dblclick: function(evt) { - this.handleDouble(evt); - return !this.stopDouble; - }, - - /** - * Method: handleDouble - * Handle double-click sequence. - */ - handleDouble: function(evt) { - if (this.passesDblclickTolerance(evt)) { - if (this["double"]) { - this.callback("dblclick", [evt]); - } - // to prevent a dblclick from firing the click callback in IE - this.clearTimer(); - } - }, - - /** - * Method: handleSingle - * Handle single click sequence. - */ - handleSingle: function(evt) { - if (this.passesTolerance(evt)) { - if (this.timerId != null) { - // already received a click - if (this.last.touches && this.last.touches.length === 1) { - // touch device, no dblclick event - this may be a double - if (this["double"]) { - // on Android don't let the browser zoom on the page - OpenLayers.Event.preventDefault(evt); - } - this.handleDouble(evt); - } - // if we're not in a touch environment we clear the click timer - // if we've got a second touch, we'll get two touchend events - if (!this.last.touches || this.last.touches.length !== 2) { - this.clearTimer(); - } - } else { - // remember the first click info so we can compare to the second - this.first = this.getEventInfo(evt); - // set the timer, send evt only if single is true - //use a clone of the event object because it will no longer - //be a valid event object in IE in the timer callback - var clickEvent = this.single ? - OpenLayers.Util.extend({}, evt) : null; - this.queuePotentialClick(clickEvent); - } - } - }, - - /** - * Method: queuePotentialClick - * This method is separated out largely to make testing easier (so we - * don't have to override window.setTimeout) - */ - queuePotentialClick: function(evt) { - this.timerId = window.setTimeout( - OpenLayers.Function.bind(this.delayedCall, this, evt), - this.delay - ); - }, - - /** - * Method: passesTolerance - * Determine whether the event is within the optional pixel tolerance. Note - * that the pixel tolerance check only works if mousedown events get to - * the listeners registered here. If they are stopped by other elements, - * the <pixelTolerance> will have no effect here (this method will always - * return true). - * - * Returns: - * {Boolean} The click is within the pixel tolerance (if specified). - */ - passesTolerance: function(evt) { - var passes = true; - if (this.pixelTolerance != null && this.down && this.down.xy) { - passes = this.pixelTolerance >= this.down.xy.distanceTo(evt.xy); - // for touch environments, we also enforce that all touches - // start and end within the given tolerance to be considered a click - if (passes && this.touch && - this.down.touches.length === this.last.touches.length) { - // the touchend event doesn't come with touches, so we check - // down and last - for (var i=0, ii=this.down.touches.length; i<ii; ++i) { - if (this.getTouchDistance( - this.down.touches[i], - this.last.touches[i] - ) > this.pixelTolerance) { - passes = false; - break; - } - } - } - } - return passes; - }, - - /** - * Method: getTouchDistance - * - * Returns: - * {Boolean} The pixel displacement between two touches. - */ - getTouchDistance: function(from, to) { - return Math.sqrt( - Math.pow(from.clientX - to.clientX, 2) + - Math.pow(from.clientY - to.clientY, 2) - ); - }, - - /** - * Method: passesDblclickTolerance - * Determine whether the event is within the optional double-cick pixel - * tolerance. - * - * Returns: - * {Boolean} The click is within the double-click pixel tolerance. - */ - passesDblclickTolerance: function(evt) { - var passes = true; - if (this.down && this.first) { - passes = this.down.xy.distanceTo(this.first.xy) <= this.dblclickTolerance; - } - 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; - } - if (this.rightclickTimerId != null) { - window.clearTimeout(this.rightclickTimerId); - this.rightclickTimerId = null; - } - }, - - /** - * Method: delayedCall - * Sets <timerId> to null. And optionally triggers the click callback if - * evt is set. - */ - delayedCall: function(evt) { - this.timerId = null; - if (evt) { - this.callback("click", [evt]); - } - }, - - /** - * Method: getEventInfo - * This method allows us to store event information without storing the - * actual event. In touch devices (at least), the same event is - * modified between touchstart, touchmove, and touchend. - * - * Returns: - * {Object} An object with event related info. - */ - getEventInfo: function(evt) { - var touches; - if (evt.touches) { - var len = evt.touches.length; - touches = new Array(len); - var touch; - for (var i=0; i<len; i++) { - touch = evt.touches[i]; - touches[i] = { - clientX: touch.olClientX, - clientY: touch.olClientY - }; - } - } - return { - xy: evt.xy, - touches: touches - }; - }, - - /** - * 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(); - this.down = null; - this.first = null; - this.last = null; - deactivated = true; - } - return deactivated; - }, - - CLASS_NAME: "OpenLayers.Handler.Click" -}); -/* ====================================================================== - OpenLayers/Control/Navigation.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @requires OpenLayers/Control/ZoomBox.js - * @requires OpenLayers/Control/DragPan.js - * @requires OpenLayers/Handler/MouseWheel.js - * @requires OpenLayers/Handler/Click.js - */ - -/** - * Class: OpenLayers.Control.Navigation - * The navigation control handles map browsing with mouse events (dragging, - * double-clicking, and scrolling the wheel). Create a new navigation - * control with the <OpenLayers.Control.Navigation> control. - * - * Note that this control is added to the map by default (if no controls - * array is sent in the options object to the <OpenLayers.Map> - * constructor). - * - * Inherits: - * - <OpenLayers.Control> - */ -OpenLayers.Control.Navigation = OpenLayers.Class(OpenLayers.Control, { - - /** - * Property: dragPan - * {<OpenLayers.Control.DragPan>} - */ - dragPan: null, - - /** - * APIProperty: dragPanOptions - * {Object} Options passed to the DragPan control. - */ - dragPanOptions: null, - - /** - * Property: pinchZoom - * {<OpenLayers.Control.PinchZoom>} - */ - pinchZoom: null, - - /** - * APIProperty: pinchZoomOptions - * {Object} Options passed to the PinchZoom control. - */ - pinchZoomOptions: null, - - /** - * APIProperty: documentDrag - * {Boolean} Allow panning of the map by dragging outside map viewport. - * Default is false. - */ - documentDrag: false, - - /** - * Property: zoomBox - * {<OpenLayers.Control.ZoomBox>} - */ - zoomBox: null, - - /** - * APIProperty: zoomBoxEnabled - * {Boolean} Whether the user can draw a box to zoom - */ - zoomBoxEnabled: true, - - /** - * APIProperty: zoomWheelEnabled - * {Boolean} Whether the mousewheel should zoom the map - */ - zoomWheelEnabled: true, - - /** - * Property: mouseWheelOptions - * {Object} Options passed to the MouseWheel control (only useful if - * <zoomWheelEnabled> is set to true). Default is no options for maps - * with fractionalZoom set to true, otherwise - * {cumulative: false, interval: 50, maxDelta: 6} - */ - mouseWheelOptions: null, - - /** - * APIProperty: handleRightClicks - * {Boolean} Whether or not to handle right clicks. Default is false. - */ - handleRightClicks: false, - - /** - * APIProperty: zoomBoxKeyMask - * {Integer} <OpenLayers.Handler> key code of the key, which has to be - * pressed, while drawing the zoom box with the mouse on the screen. - * You should probably set handleRightClicks to true if you use this - * with MOD_CTRL, to disable the context menu for machines which use - * CTRL-Click as a right click. - * Default: <OpenLayers.Handler.MOD_SHIFT> - */ - zoomBoxKeyMask: OpenLayers.Handler.MOD_SHIFT, - - /** - * APIProperty: autoActivate - * {Boolean} Activate the control when it is added to a map. Default is - * true. - */ - autoActivate: true, - - /** - * Constructor: OpenLayers.Control.Navigation - * Create a new navigation control - * - * Parameters: - * options - {Object} An optional object whose properties will be set on - * the control - */ - initialize: function(options) { - this.handlers = {}; - OpenLayers.Control.prototype.initialize.apply(this, arguments); - }, - - /** - * Method: destroy - * The destroy method is used to perform any clean up before the control - * is dereferenced. Typically this is where event listeners are removed - * to prevent memory leaks. - */ - destroy: function() { - this.deactivate(); - - if (this.dragPan) { - this.dragPan.destroy(); - } - this.dragPan = null; - - if (this.zoomBox) { - this.zoomBox.destroy(); - } - this.zoomBox = null; - - if (this.pinchZoom) { - this.pinchZoom.destroy(); - } - this.pinchZoom = null; - - OpenLayers.Control.prototype.destroy.apply(this,arguments); - }, - - /** - * Method: activate - */ - activate: function() { - this.dragPan.activate(); - if (this.zoomWheelEnabled) { - this.handlers.wheel.activate(); - } - this.handlers.click.activate(); - if (this.zoomBoxEnabled) { - this.zoomBox.activate(); - } - if (this.pinchZoom) { - this.pinchZoom.activate(); - } - return OpenLayers.Control.prototype.activate.apply(this,arguments); - }, - - /** - * Method: deactivate - */ - deactivate: function() { - if (this.pinchZoom) { - this.pinchZoom.deactivate(); - } - this.zoomBox.deactivate(); - this.dragPan.deactivate(); - this.handlers.click.deactivate(); - this.handlers.wheel.deactivate(); - return OpenLayers.Control.prototype.deactivate.apply(this,arguments); - }, - - /** - * Method: draw - */ - draw: function() { - // disable right mouse context menu for support of right click events - if (this.handleRightClicks) { - this.map.viewPortDiv.oncontextmenu = OpenLayers.Function.False; - } - - var clickCallbacks = { - 'click': this.defaultClick, - '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, - documentDrag: this.documentDrag - }, this.dragPanOptions) - ); - this.zoomBox = new OpenLayers.Control.ZoomBox( - {map: this.map, keyMask: this.zoomBoxKeyMask}); - this.dragPan.draw(); - this.zoomBox.draw(); - var wheelOptions = this.map.fractionalZoom ? {} : { - cumulative: false, - interval: 50, - maxDelta: 6 - }; - this.handlers.wheel = new OpenLayers.Handler.MouseWheel( - this, {up : this.wheelUp, down: this.wheelDown}, - OpenLayers.Util.extend(wheelOptions, this.mouseWheelOptions) - ); - if (OpenLayers.Control.PinchZoom) { - this.pinchZoom = new OpenLayers.Control.PinchZoom( - OpenLayers.Util.extend( - {map: this.map}, this.pinchZoomOptions)); - } - }, - - /** - * Method: defaultClick - * - * Parameters: - * evt - {Event} - */ - defaultClick: function (evt) { - if (evt.lastTouches && evt.lastTouches.length == 2) { - this.map.zoomOut(); - } - }, - - /** - * Method: defaultDblClick - * - * Parameters: - * evt - {Event} - */ - defaultDblClick: function (evt) { - this.map.zoomTo(this.map.zoom + 1, evt.xy); - }, - - /** - * Method: defaultDblRightClick - * - * Parameters: - * evt - {Event} - */ - defaultDblRightClick: function (evt) { - this.map.zoomTo(this.map.zoom - 1, evt.xy); - }, - - /** - * Method: wheelChange - * - * Parameters: - * evt - {Event} - * deltaZ - {Integer} - */ - wheelChange: function(evt, deltaZ) { - if (!this.map.fractionalZoom) { - deltaZ = Math.round(deltaZ); - } - var currentZoom = this.map.getZoom(), - newZoom = currentZoom + deltaZ; - newZoom = Math.max(newZoom, 0); - newZoom = Math.min(newZoom, this.map.getNumZoomLevels()); - if (newZoom === currentZoom) { - return; - } - this.map.zoomTo(newZoom, evt.xy); - }, - - /** - * Method: wheelUp - * User spun scroll wheel up - * - * Parameters: - * evt - {Event} - * delta - {Integer} - */ - wheelUp: function(evt, delta) { - this.wheelChange(evt, delta || 1); - }, - - /** - * Method: wheelDown - * User spun scroll wheel down - * - * Parameters: - * evt - {Event} - * delta - {Integer} - */ - wheelDown: function(evt, delta) { - this.wheelChange(evt, delta || -1); - }, - - /** - * Method: disableZoomBox - */ - disableZoomBox : function() { - this.zoomBoxEnabled = false; - this.zoomBox.deactivate(); - }, - - /** - * Method: enableZoomBox - */ - enableZoomBox : function() { - this.zoomBoxEnabled = true; - if (this.active) { - this.zoomBox.activate(); - } - }, - - /** - * Method: disableZoomWheel - */ - - disableZoomWheel : function() { - this.zoomWheelEnabled = false; - this.handlers.wheel.deactivate(); - }, - - /** - * Method: enableZoomWheel - */ - - enableZoomWheel : function() { - this.zoomWheelEnabled = true; - if (this.active) { - this.handlers.wheel.activate(); - } - }, - - CLASS_NAME: "OpenLayers.Control.Navigation" -}); -/* ====================================================================== - OpenLayers/Layer/WMS.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - - -/** - * @requires OpenLayers/Layer/Grid.js - */ - -/** - * Class: OpenLayers.Layer.WMS - * Instances of OpenLayers.Layer.WMS are used to display data from OGC Web - * Mapping Services. Create a new WMS layer with the <OpenLayers.Layer.WMS> - * constructor. - * - * Inherits from: - * - <OpenLayers.Layer.Grid> - */ -OpenLayers.Layer.WMS = OpenLayers.Class(OpenLayers.Layer.Grid, { - - /** - * Constant: DEFAULT_PARAMS - * {Object} Hashtable of default parameter key/value pairs - */ - DEFAULT_PARAMS: { service: "WMS", - version: "1.1.1", - request: "GetMap", - styles: "", - format: "image/jpeg" - }, - - /** - * APIProperty: isBaseLayer - * {Boolean} Default is true for WMS layer - */ - isBaseLayer: 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: noMagic - * {Boolean} If true, the image format will not be automagicaly switched - * from image/jpeg to image/png or image/gif when using - * TRANSPARENT=TRUE. Also isBaseLayer will not changed by the - * constructor. Default false. - */ - noMagic: false, - - /** - * Property: yx - * {Object} Keys in this object are EPSG codes for which the axis order - * is to be reversed (yx instead of xy, LatLon instead of LonLat), with - * true as value. This is only relevant for WMS versions >= 1.3.0, and - * only if yx is not set in <OpenLayers.Projection.defaults> for the - * used projection. - */ - yx: {}, - - /** - * Constructor: OpenLayers.Layer.WMS - * Create a new WMS layer object - * - * Examples: - * - * The code below creates a simple WMS layer using the image/jpeg format. - * (code) - * var wms = new OpenLayers.Layer.WMS("NASA Global Mosaic", - * "http://wms.jpl.nasa.gov/wms.cgi", - * {layers: "modis,global_mosaic"}); - * (end) - * Note the 3rd argument (params). Properties added to this object will be - * added to the WMS GetMap requests used for this layer's tiles. The only - * mandatory parameter is "layers". Other common WMS params include - * "transparent", "styles" and "format". Note that the "srs" param will - * always be ignored. Instead, it will be derived from the baseLayer's or - * map's projection. - * - * The code below creates a transparent WMS layer with additional options. - * (code) - * var wms = new OpenLayers.Layer.WMS("NASA Global Mosaic", - * "http://wms.jpl.nasa.gov/wms.cgi", - * { - * layers: "modis,global_mosaic", - * transparent: true - * }, { - * opacity: 0.5, - * singleTile: true - * }); - * (end) - * Note that by default, a WMS layer is configured as baseLayer. Setting - * the "transparent" param to true will apply some magic (see <noMagic>). - * The default image format changes from image/jpeg to image/png, and the - * layer is not configured as baseLayer. - * - * Parameters: - * name - {String} A name for the layer - * url - {String} Base url for the WMS - * (e.g. http://wms.jpl.nasa.gov/wms.cgi) - * params - {Object} An object with key/value pairs representing the - * GetMap query string parameters and parameter values. - * options - {Object} Hashtable of extra options to tag onto the layer. - * These options include all properties listed above, plus the ones - * inherited from superclasses. - */ - initialize: function(name, url, params, options) { - var newArguments = []; - //uppercase params - params = OpenLayers.Util.upperCaseObject(params); - if (parseFloat(params.VERSION) >= 1.3 && !params.EXCEPTIONS) { - params.EXCEPTIONS = "INIMAGE"; - } - 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) - ); - - - //layer is transparent - if (!this.noMagic && this.params.TRANSPARENT && - this.params.TRANSPARENT.toString().toLowerCase() == "true") { - - // unless explicitly set in options, make layer an overlay - if ( (options == null) || (!options.isBaseLayer) ) { - this.isBaseLayer = false; - } - - // jpegs can never be transparent, so intelligently switch the - // format, depending on the browser's capabilities - if (this.params.FORMAT == "image/jpeg") { - this.params.FORMAT = OpenLayers.Util.alphaHack() ? "image/gif" - : "image/png"; - } - } - - }, - - /** - * Method: clone - * Create a clone of this layer - * - * Returns: - * {<OpenLayers.Layer.WMS>} An exact clone of this layer - */ - clone: function (obj) { - - if (obj == null) { - obj = new OpenLayers.Layer.WMS(this.name, - this.url, - this.params, - this.getOptions()); - } - - //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; - }, - - /** - * APIMethod: reverseAxisOrder - * Returns true if the axis order is reversed for the WMS version and - * projection of the layer. - * - * Returns: - * {Boolean} true if the axis order is reversed, false otherwise. - */ - reverseAxisOrder: function() { - var projCode = this.projection.getCode(); - return parseFloat(this.params.VERSION) >= 1.3 && - !!(this.yx[projCode] || (OpenLayers.Projection.defaults[projCode] && - OpenLayers.Projection.defaults[projCode].yx)); - }, - - /** - * Method: getURL - * Return a GetMap query string for this layer - * - * Parameters: - * bounds - {<OpenLayers.Bounds>} A bounds representing the bbox for the - * request. - * - * 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 imageSize = this.getImageSize(); - var newParams = {}; - // WMS 1.3 introduced axis order - var reverseAxisOrder = this.reverseAxisOrder(); - newParams.BBOX = this.encodeBBOX ? - bounds.toBBOX(null, reverseAxisOrder) : - bounds.toArray(reverseAxisOrder); - newParams.WIDTH = imageSize.w; - newParams.HEIGHT = imageSize.h; - var requestString = this.getFullRequestString(newParams); - return requestString; - }, - - /** - * APIMethod: mergeNewParams - * Catch changeParams and uppercase the new params to be merged in - * before calling changeParams on the super class. - * - * Once params have been changed, the tiles will be reloaded with - * the new parameters. - * - * Parameters: - * newParams - {Object} Hashtable of new params to use - */ - mergeNewParams:function(newParams) { - var upperParams = OpenLayers.Util.upperCaseObject(newParams); - var newArguments = [upperParams]; - return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(this, - newArguments); - }, - - /** - * 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 - * - * Returns: - * {String} - */ - getFullRequestString:function(newParams, altUrl) { - var mapProjection = this.map.getProjectionObject(); - var projectionCode = this.projection && this.projection.equals(mapProjection) ? - this.projection.getCode() : - mapProjection.getCode(); - var value = (projectionCode == "none") ? null : projectionCode; - if (parseFloat(this.params.VERSION) >= 1.3) { - this.params.CRS = value; - } else { - this.params.SRS = value; - } - - if (typeof this.params.TRANSPARENT == "boolean") { - newParams.TRANSPARENT = this.params.TRANSPARENT ? "TRUE" : "FALSE"; - } - - return OpenLayers.Layer.Grid.prototype.getFullRequestString.apply( - this, arguments); - }, - - CLASS_NAME: "OpenLayers.Layer.WMS" -}); -/* ====================================================================== - OpenLayers/Renderer/SVG.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository 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: symbolMetrics - * {Object} Cache for symbol metrics according to their svg coordinate - * space. This is an object keyed by the symbol's id, and values are - * an array of [width, centerX, centerY]. - */ - symbolMetrics: null, - - /** - * 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}; - - this.symbolMetrics = {}; - }, - - /** - * 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) { - var coordSysUnchanged = OpenLayers.Renderer.Elements.prototype.setExtent.apply(this, arguments); - - var resolution = this.getResolution(), - left = -extent.left / resolution, - 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(this.xOffset, 0); - return true; - } else { - var inRange = this.translate(left - this.left + this.xOffset, top - this.top); - if (!inRange) { - // recenter the coordinate system - this.setExtent(extent, true); - } - return coordSysUnchanged && inRange; - } - }, - - /** - * Method: translate - * Transforms the SVG coordinate system - * - * Parameters: - * x - {Float} - * y - {Float} - * - * Returns: - * {Boolean} true if the translation parameters are in the valid coordinates - * range, false otherwise. - */ - translate: function(x, y) { - if (!this.inValidRange(x, y, true)) { - return false; - } else { - var transformString = ""; - if (x || y) { - transformString = "translate(" + x + "," + y + ")"; - } - this.root.setAttributeNS(null, "transform", transformString); - this.translationParameters = {x: x, y: y}; - return true; - } - }, - - /** - * Method: setSize - * Sets the size of the drawing surface. - * - * Parameters: - * size - {<OpenLayers.Size>} The size of the drawing surface - */ - setSize: function(size) { - OpenLayers.Renderer.prototype.setSize.apply(this, arguments); - - this.rendererRoot.setAttributeNS(null, "width", this.size.w); - this.rendererRoot.setAttributeNS(null, "height", this.size.h); - }, - - /** - * Method: getNodeType - * - * Parameters: - * geometry - {<OpenLayers.Geometry>} - * style - {Object} - * - * Returns: - * {String} The corresponding node type for the specified geometry - */ - getNodeType: function(geometry, style) { - var nodeType = null; - switch (geometry.CLASS_NAME) { - case "OpenLayers.Geometry.Point": - if (style.externalGraphic) { - nodeType = "image"; - } else if (this.isComplexSymbol(style.graphicName)) { - nodeType = "svg"; - } 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": - 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 title = style.title || style.graphicTitle; - if (title) { - node.setAttributeNS(null, "title", title); - //Standards-conformant SVG - // Prevent duplicate nodes. See issue https://github.com/openlayers/openlayers/issues/92 - var titleNode = node.getElementsByTagName("title"); - if (titleNode.length > 0) { - titleNode[0].firstChild.textContent = title; - } else { - var label = this.nodeFactory(null, "title"); - label.textContent = title; - node.appendChild(label); - } - } - - var r = parseFloat(node.getAttributeNS(null, "r")); - var widthFactor = 1; - var pos; - if (node._geometryClass == "OpenLayers.Geometry.Point" && r) { - node.style.visibility = ""; - if (style.graphic === false) { - node.style.visibility = "hidden"; - } else 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, "xlink:href", style.externalGraphic); - node.setAttributeNS(null, "style", "opacity: "+opacity); - node.onclick = OpenLayers.Event.preventDefault; - } 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 src = this.importSymbol(style.graphicName); - pos = this.getPosition(node); - widthFactor = this.symbolMetrics[src.id][0] * 3 / size; - - // remove the node from the dom before we modify it. This - // prevents various rendering issues in Safari and FF - var parent = node.parentNode; - var nextSibling = node.nextSibling; - if(parent) { - parent.removeChild(node); - } - - // The more appropriate way to implement this would be use/defs, - // but due to various issues in several browsers, it is safer to - // copy the symbols instead of referencing them. - // See e.g. ticket http://trac.osgeo.org/openlayers/ticket/2985 - // and this email thread - // http://osgeo-org.1803224.n2.nabble.com/Select-Control-Ctrl-click-on-Feature-with-a-graphicName-opens-new-browser-window-tc5846039.html - node.firstChild && node.removeChild(node.firstChild); - node.appendChild(src.firstChild.cloneNode(true)); - node.setAttributeNS(null, "viewBox", src.getAttributeNS(null, "viewBox")); - - node.setAttributeNS(null, "width", size); - node.setAttributeNS(null, "height", size); - node.setAttributeNS(null, "x", pos.x - offset); - node.setAttributeNS(null, "y", pos.y - offset); - - // now that the node has all its new properties, insert it - // back into the dom where it was - if(nextSibling) { - parent.insertBefore(node, nextSibling); - } else if(parent) { - parent.appendChild(node); - } - } else { - node.setAttributeNS(null, "r", style.pointRadius); - } - - var rotation = style.rotation; - - if ((rotation !== undefined || node._rotation !== undefined) && pos) { - node._rotation = rotation; - rotation |= 0; - if (node.nodeName !== "svg") { - node.setAttributeNS(null, "transform", - "rotate(" + rotation + " " + pos.x + " " + - pos.y + ")"); - } else { - var metrics = this.symbolMetrics[src.id]; - node.firstChild.setAttributeNS(null, "transform", "rotate(" - + rotation + " " - + metrics[1] + " " - + metrics[2] + ")"); - } - } - } - - 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 || "round"); - // 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"); - style.strokeDashstyle && 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; - var str = style.strokeDashstyle; - switch (str) { - 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 OpenLayers.String.trim(str).replace(/\s+/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() { - var svg = this.nodeFactory(this.container.id + "_svgRoot", "svg"); - svg.style.display = "block"; - return svg; - }, - - /** - * Method: createRoot - * - * Parameters: - * suffix - {String} suffix to append to the id - * - * Returns: - * {DOMElement} - */ - createRoot: function(suffix) { - return this.nodeFactory(this.container.id + suffix, "g"); - }, - - /** - * Method: createDefs - * - * Returns: - * {DOMElement} The element to which we'll add the symbol definitions - */ - createDefs: function() { - var defs = this.nodeFactory(this.container.id + "_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 - this.featureDx) / 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 - this.featureDx) / 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: drawText - * This method is only called by the renderer itself. - * - * Parameters: - * featureId - {String} - * style - - * location - {<OpenLayers.Geometry.Point>} - */ - drawText: function(featureId, style, location) { - var drawOutline = (!!style.labelOutlineWidth); - // First draw text in halo color and size and overlay the - // normal text afterwards - if (drawOutline) { - var outlineStyle = OpenLayers.Util.extend({}, style); - outlineStyle.fontColor = outlineStyle.labelOutlineColor; - outlineStyle.fontStrokeColor = outlineStyle.labelOutlineColor; - outlineStyle.fontStrokeWidth = style.labelOutlineWidth; - if (style.labelOutlineOpacity) { - outlineStyle.fontOpacity = style.labelOutlineOpacity; - } - delete outlineStyle.labelOutlineWidth; - this.drawText(featureId, outlineStyle, location); - } - - var resolution = this.getResolution(); - - var x = ((location.x - this.featureDx) / resolution + this.left); - var y = (location.y / resolution - this.top); - - var suffix = (drawOutline)?this.LABEL_OUTLINE_SUFFIX:this.LABEL_ID_SUFFIX; - var label = this.nodeFactory(featureId + suffix, "text"); - - label.setAttributeNS(null, "x", x); - label.setAttributeNS(null, "y", -y); - - if (style.fontColor) { - label.setAttributeNS(null, "fill", style.fontColor); - } - if (style.fontStrokeColor) { - label.setAttributeNS(null, "stroke", style.fontStrokeColor); - } - if (style.fontStrokeWidth) { - label.setAttributeNS(null, "stroke-width", style.fontStrokeWidth); - } - if (style.fontOpacity) { - label.setAttributeNS(null, "opacity", style.fontOpacity); - } - if (style.fontFamily) { - label.setAttributeNS(null, "font-family", style.fontFamily); - } - if (style.fontSize) { - label.setAttributeNS(null, "font-size", style.fontSize); - } - if (style.fontWeight) { - label.setAttributeNS(null, "font-weight", style.fontWeight); - } - if (style.fontStyle) { - label.setAttributeNS(null, "font-style", style.fontStyle); - } - if (style.labelSelect === true) { - label.setAttributeNS(null, "pointer-events", "visible"); - label._featureId = featureId; - } else { - label.setAttributeNS(null, "pointer-events", "none"); - } - var align = style.labelAlign || OpenLayers.Renderer.defaultSymbolizer.labelAlign; - label.setAttributeNS(null, "text-anchor", - OpenLayers.Renderer.SVG.LABEL_ALIGN[align[0]] || "middle"); - - if (OpenLayers.IS_GECKO === true) { - label.setAttributeNS(null, "dominant-baseline", - OpenLayers.Renderer.SVG.LABEL_ALIGN[align[1]] || "central"); - } - - var labelRows = style.label.split('\n'); - var numRows = labelRows.length; - while (label.childNodes.length > numRows) { - label.removeChild(label.lastChild); - } - for (var i = 0; i < numRows; i++) { - var tspan = this.nodeFactory(featureId + suffix + "_tspan_" + i, "tspan"); - if (style.labelSelect === true) { - tspan._featureId = featureId; - tspan._geometry = location; - tspan._geometryClass = location.CLASS_NAME; - } - if (OpenLayers.IS_GECKO === false) { - tspan.setAttributeNS(null, "baseline-shift", - OpenLayers.Renderer.SVG.LABEL_VSHIFT[align[1]] || "-35%"); - } - tspan.setAttribute("x", x); - if (i == 0) { - var vfactor = OpenLayers.Renderer.SVG.LABEL_VFACTOR[align[1]]; - if (vfactor == null) { - vfactor = -.5; - } - tspan.setAttribute("dy", (vfactor*(numRows-1)) + "em"); - } else { - tspan.setAttribute("dy", "1em"); - } - tspan.textContent = (labelRows[i] === '') ? ' ' : labelRows[i]; - if (!tspan.parentNode) { - label.appendChild(tspan); - } - } - - if (!label.parentNode) { - this.textRoot.appendChild(label); - } - }, - - /** - * 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; - 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 - this.featureDx) / resolution + this.left; - var y1 = this.top - goodComponent.y / resolution; - var x2 = (badComponent.x - this.featureDx) / 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 - this.featureDx) / 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: - * {DOMElement} - 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 - var existing = document.getElementById(id); - if (existing != null) { - return existing; - } - - var symbol = OpenLayers.Renderer.symbol[graphicName]; - if (!symbol) { - throw new Error(graphicName + ' is not a valid symbol name'); - } - - 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.push(x, ",", y); - } - - node.setAttributeNS(null, "points", points.join(" ")); - - 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.symbolMetrics[id] = [ - Math.max(width, height), - symbolExtent.getCenterLonLat().lon, - symbolExtent.getCenterLonLat().lat - ]; - - this.defs.appendChild(symbolNode); - return symbolNode; - }, - - /** - * Method: getFeatureIdFromEvent - * - * Parameters: - * evt - {Object} An <OpenLayers.Event> object - * - * Returns: - * {String} A feature id or undefined. - */ - getFeatureIdFromEvent: function(evt) { - var featureId = OpenLayers.Renderer.Elements.prototype.getFeatureIdFromEvent.apply(this, arguments); - if(!featureId) { - var target = evt.target; - featureId = target.parentNode && target != this.rendererRoot ? - target.parentNode._featureId : undefined; - } - return featureId; - }, - - CLASS_NAME: "OpenLayers.Renderer.SVG" -}); - -/** - * Constant: OpenLayers.Renderer.SVG.LABEL_ALIGN - * {Object} - */ -OpenLayers.Renderer.SVG.LABEL_ALIGN = { - "l": "start", - "r": "end", - "b": "bottom", - "t": "hanging" -}; - -/** - * Constant: OpenLayers.Renderer.SVG.LABEL_VSHIFT - * {Object} - */ -OpenLayers.Renderer.SVG.LABEL_VSHIFT = { - // according to - // http://www.w3.org/Graphics/SVG/Test/20061213/htmlObjectHarness/full-text-align-02-b.html - // a baseline-shift of -70% shifts the text exactly from the - // bottom to the top of the baseline, so -35% moves the text to - // the center of the baseline. - "t": "-70%", - "b": "0" -}; - -/** - * Constant: OpenLayers.Renderer.SVG.LABEL_VFACTOR - * {Object} - */ -OpenLayers.Renderer.SVG.LABEL_VFACTOR = { - "t": 0, - "b": -1 -}; - -/** - * Function: OpenLayers.Renderer.SVG.preventDefault - * *Deprecated*. Use <OpenLayers.Event.preventDefault> method instead. - * Used to prevent default events (especially opening images in a new tab on - * ctrl-click) from being executed for externalGraphic symbols - */ -OpenLayers.Renderer.SVG.preventDefault = function(e) { - OpenLayers.Event.preventDefault(e); -}; -/* ====================================================================== - OpenLayers/Popup.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @requires OpenLayers/BaseTypes/Class.js - */ - - -/** - * 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, - - /** - * Property: disableFirefoxOverflowHack - * {Boolean} The hack for overflow in Firefox causes all elements - * to be re-drawn, which causes Flash elements to be - * re-initialized, which is troublesome. - * With this property the hack can be disabled. - */ - disableFirefoxOverflowHack: false, - - /** - * 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, - - /** - * APIProperty: keepInMap - * {Boolean} If panMapIfOutOfView is false, and this property is true, - * contrain the popup such that it always fits in the available map - * space. By default, this is not set on the base class. If you are - * creating popups that are near map edges and not allowing pannning, - * and especially if you have a popup which has a - * fixedRelativePosition, setting this to false may be a smart thing to - * do. Subclasses may want to override this setting. - * - * Default is false. - */ - keepInMap: false, - - /** - * APIProperty: closeOnMove - * {Boolean} When map pans, close the popup. - * Default is false. - */ - closeOnMove: 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; - - if (this.closeOnMove && this.map) { - this.map.events.unregister("movestart", this, this.hide); - } - - 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); - } - } - - // this assumes that this.map already exists, which is okay because - // this.draw is only called once the popup has been added to the map. - if (this.closeOnMove) { - this.map.events.register("movestart", this, this.hide); - } - - //listen to movestart, moveend to disable overflow (FF bug) - if (!this.disableFirefoxOverflowHack && OpenLayers.BROWSER_NAME == '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() { - this.div.style.display = ''; - - if (this.panMapIfOutOfView) { - this.panIntoView(); - } - }, - - /** - * Method: hide - * Makes the popup invisible. - */ - hide: function() { - this.div.style.display = 'none'; - }, - - /** - * 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.BROWSER_NAME == "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 containerElement = (this.map) ? this.map.div : document.body; - var realSize = OpenLayers.Util.getRenderedDimensions( - preparedHTML, null, { - displayClass: this.displayClass, - containerElement: containerElement - } - ); - - // 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 'size' object with the clipped dimensions - // set or null if not clipped. - var fixedSize = { - w: (safeSize.w < realSize.w) ? safeSize.w : null, - 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, - containerElement: containerElement - } - ); - - //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() { - if (this.popup.id === null) { // this.popup has been destroyed! - return; - } - this.popup.updateSize(); - - if ( this.popup.visible() && this.popup.panMapIfOutOfView ) { - this.popup.panIntoView(); - } - - OpenLayers.Event.stopObserving( - this.img, "load", this.img._onImgLoad - ); - - }; - - //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) { - - var extraX = 0, extraY = 0; - if (this.keepInMap && !this.panMapIfOutOfView) { - var px = this.map.getPixelFromLonLat(this.lonlat); - switch (this.relativePosition) { - case "tr": - extraX = px.x; - extraY = this.map.size.h - px.y; - break; - case "tl": - extraX = this.map.size.w - px.x; - extraY = this.map.size.h - px.y; - break; - case "bl": - extraX = this.map.size.w - px.x; - extraY = px.y; - break; - case "br": - extraX = px.x; - extraY = px.y; - break; - default: - extraX = px.x; - extraY = this.map.size.h - px.y; - break; - } - } - - var maxY = this.map.size.h - - this.map.paddingForPopups.top - - this.map.paddingForPopups.bottom - - hPadding - extraY; - - var maxX = this.map.size.w - - this.map.paddingForPopups.left - - this.map.paddingForPopups.right - - wPadding - extraX; - - 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) { - - if (this.div.parentNode == null) { - //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; - - if (this.div.parentNode == document.body) { - //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, {w: 17, h: 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, "touchend", - OpenLayers.Function.bindAsEventListener(closePopup, this)); - 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. The same procedure is needed for - * touchmove and touchend events. - * - * 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); - - function onTouchstart(evt) { - OpenLayers.Event.stop(evt, true); - } - this.events.on({ - "mousedown": this.onmousedown, - "mousemove": this.onmousemove, - "mouseup": this.onmouseup, - "click": this.onclick, - "mouseout": this.onmouseout, - "dblclick": this.ondblclick, - "touchstart": onTouchstart, - 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/Popup/Anchored.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository 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, { - - /** - * Property: relativePosition - * {String} Relative position of the popup ("br", "tr", "tl" or "bl"). - */ - relativePosition: null, - - /** - * APIProperty: keepInMap - * {Boolean} If panMapIfOutOfView is false, and this property is true, - * contrain the popup such that it always fits in the available map - * space. By default, this is set. If you are creating popups that are - * near map edges and not allowing pannning, and especially if you have - * a popup which has a fixedRelativePosition, setting this to false may - * be a smart thing to do. - * - * For anchored popups, default is true, since subclasses will - * usually want this functionality. - */ - keepInMap: true, - - /** - * Property: 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); - - OpenLayers.Popup.prototype.moveTo.call(this, this.calculateNewPx(px)); - - //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 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/Popup/Framed.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository 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 return the specified - // fixed 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 < 0 ? 0 : w) + 'px'; - block.div.style.height = (h < 0 ? 0 : 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/Format.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @requires OpenLayers/BaseTypes/Class.js - * @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({ - - /** - * Property: options - * {Object} A reference to options passed to the constructor. - */ - options: null, - - /** - * 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, - - /** - * APIProperty: data - * {Object} When <keepData> is true, this is the parsed string sent to - * <read>. - */ - data: null, - - /** - * APIProperty: keepData - * {Object} Maintain a reference (<data>) to the most recently read data. - * Default is false. - */ - keepData: false, - - /** - * 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 - * - * Valid options: - * keepData - {Boolean} If true, upon <read>, the data property will be - * set to the parsed object (e.g. the json or xml object). - * - * Returns: - * An instance of OpenLayers.Format - */ - initialize: function(options) { - OpenLayers.Util.extend(this, options); - this.options = options; - }, - - /** - * APIMethod: destroy - * Clean up. - */ - destroy: function() { - }, - - /** - * 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) { - throw new Error('Read not implemented.'); - }, - - /** - * 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) { - throw new Error('Write not implemented.'); - }, - - CLASS_NAME: "OpenLayers.Format" -}); -/* ====================================================================== - OpenLayers/Format/JSON.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository 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, - - /** - * Property: nativeJSON - * {Boolean} Does the browser support native json? - */ - nativeJSON: (function() { - return !!(window.JSON && typeof JSON.parse == "function" && typeof JSON.stringify == "function"); - })(), - - /** - * Constructor: OpenLayers.Format.JSON - * Create a new parser for JSON. - * - * Parameters: - * options - {Object} An optional object whose properties will be set on - * this instance. - */ - - /** - * 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) { - var object; - if (this.nativeJSON) { - object = JSON.parse(json, filter); - } else try { - /** - * 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. - */ - 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. - */ - 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); - } - } - } catch(e) { - // Fall through if the regexp test fails. - } - - if(this.keepData) { - this.data = object; - } - - return object; - }, - - /** - * 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]) { - try { - json = (!this.pretty && this.nativeJSON) ? - JSON.stringify(value) : - this.serialize[type].apply(this, [value]); - } catch(err) { - OpenLayers.Console.error("Trouble serializing: " + err); - } - } - 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/GeoJSON.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository 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 - * @requires OpenLayers/Console.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, { - - /** - * APIProperty: ignoreExtraDims - * {Boolean} Ignore dimensions higher than 2 when reading geometry - * coordinates. - */ - ignoreExtraDims: false, - - /** - * Constructor: OpenLayers.Format.GeoJSON - * Create a new parser for GeoJSON. - * - * Parameters: - * options - {Object} An optional object whose properties will be set on - * this instance. - */ - - /** - * 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, bbox; - attributes = (obj.properties) ? obj.properties : {}; - bbox = (obj.geometry && obj.geometry.bbox) || obj.bbox; - try { - geometry = this.parseGeometry(obj.geometry); - } catch(err) { - // deal with bad geometries - throw err; - } - feature = new OpenLayers.Feature.Vector(geometry, attributes); - if(bbox) { - feature.bounds = OpenLayers.Bounds.fromArray(bbox); - } - 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, collection = false; - if(obj.type == "GeometryCollection") { - if(!(OpenLayers.Util.isArray(obj.geometries))) { - 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); - collection = true; - } else { - if(!(OpenLayers.Util.isArray(obj.coordinates))) { - 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; - } - } - // We don't reproject collections because the children are reprojected - // for us when they are created. - if (this.internalProjection && this.externalProjection && !collection) { - 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 (this.ignoreExtraDims == false && - 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(OpenLayers.Util.isArray(obj)) { - 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": "name", - "properties": { - "name": "urn:ogc:def:crs:OGC:1.3:CRS84" - } - }; - } else { - crs = { - "type": "name", - "properties": { - "name": "EPSG:" + 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]); - var json = { - "type": "Feature", - "properties": feature.attributes, - "geometry": geom - }; - if (feature.fid != null) { - json.id = feature.fid; - } - return json; - }, - - /** - * 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: - * multilinestring - {<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/Layer/Google/v3.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - - -/** - * @requires OpenLayers/Layer/Google.js - */ - -/** - * Constant: OpenLayers.Layer.Google.v3 - * - * Mixin providing functionality specific to the Google Maps API v3. - * - * To use this layer, you must include the GMaps v3 API in your html. - * - * Note that this layer configures the google.maps.map object with the - * "disableDefaultUI" option set to true. Using UI controls that the Google - * Maps API provides is not supported by the OpenLayers API. - */ -OpenLayers.Layer.Google.v3 = { - - /** - * Constant: DEFAULTS - * {Object} It is not recommended to change the properties set here. Note - * that Google.v3 layers only work when sphericalMercator is set to true. - * - * (code) - * { - * sphericalMercator: true, - * projection: "EPSG:900913" - * } - * (end) - */ - DEFAULTS: { - sphericalMercator: true, - projection: "EPSG:900913" - }, - - /** - * APIProperty: animationEnabled - * {Boolean} If set to true, the transition between zoom levels will be - * animated (if supported by the GMaps API for the device used). Set to - * false to match the zooming experience of other layer types. Default - * is true. Note that the GMaps API does not give us control over zoom - * animation, so if set to false, when zooming, this will make the - * layer temporarily invisible, wait until GMaps reports the map being - * idle, and make it visible again. The result will be a blank layer - * for a few moments while zooming. - */ - animationEnabled: true, - - /** - * Method: loadMapObject - * Load the GMap and register appropriate event listeners. - */ - loadMapObject: function() { - if (!this.type) { - this.type = google.maps.MapTypeId.ROADMAP; - } - var mapObject; - var cache = OpenLayers.Layer.Google.cache[this.map.id]; - if (cache) { - // there are already Google layers added to this map - mapObject = cache.mapObject; - // increment the layer count - ++cache.count; - } else { - // this is the first Google layer for this map - // create GMap - var center = this.map.getCenter(); - var container = document.createElement('div'); - container.className = "olForeignContainer"; - container.style.width = '100%'; - container.style.height = '100%'; - mapObject = new google.maps.Map(container, { - center: center ? - new google.maps.LatLng(center.lat, center.lon) : - new google.maps.LatLng(0, 0), - zoom: this.map.getZoom() || 0, - mapTypeId: this.type, - disableDefaultUI: true, - keyboardShortcuts: false, - draggable: false, - disableDoubleClickZoom: true, - scrollwheel: false, - streetViewControl: false - }); - var googleControl = document.createElement('div'); - googleControl.style.width = '100%'; - googleControl.style.height = '100%'; - mapObject.controls[google.maps.ControlPosition.TOP_LEFT].push(googleControl); - - // cache elements for use by any other google layers added to - // this same map - cache = { - googleControl: googleControl, - mapObject: mapObject, - count: 1 - }; - OpenLayers.Layer.Google.cache[this.map.id] = cache; - } - this.mapObject = mapObject; - this.setGMapVisibility(this.visibility); - }, - - /** - * APIMethod: onMapResize - */ - onMapResize: function() { - if (this.visibility) { - google.maps.event.trigger(this.mapObject, "resize"); - } - }, - - /** - * Method: setGMapVisibility - * Display the GMap container and associated elements. - * - * Parameters: - * visible - {Boolean} Display the GMap elements. - */ - setGMapVisibility: function(visible) { - var cache = OpenLayers.Layer.Google.cache[this.map.id]; - var map = this.map; - if (cache) { - var type = this.type; - var layers = map.layers; - var layer; - for (var i=layers.length-1; i>=0; --i) { - layer = layers[i]; - if (layer instanceof OpenLayers.Layer.Google && - layer.visibility === true && layer.inRange === true) { - type = layer.type; - visible = true; - break; - } - } - var container = this.mapObject.getDiv(); - if (visible === true) { - if (container.parentNode !== map.div) { - if (!cache.rendered) { - var me = this; - google.maps.event.addListenerOnce(this.mapObject, 'tilesloaded', function() { - cache.rendered = true; - me.setGMapVisibility(me.getVisibility()); - me.moveTo(me.map.getCenter()); - }); - } else { - map.div.appendChild(container); - cache.googleControl.appendChild(map.viewPortDiv); - google.maps.event.trigger(this.mapObject, 'resize'); - } - } - this.mapObject.setMapTypeId(type); - } else if (cache.googleControl.hasChildNodes()) { - map.div.appendChild(map.viewPortDiv); - map.div.removeChild(container); - } - } - }, - - /** - * Method: getMapContainer - * - * Returns: - * {DOMElement} the GMap container's div - */ - getMapContainer: function() { - return this.mapObject.getDiv(); - }, - - // - // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds - // - - /** - * 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 google.maps.LatLngBounds( - new google.maps.LatLng(sw.lat, sw.lon), - new google.maps.LatLng(ne.lat, ne.lon) - ); - } - return moBounds; - }, - - - /************************************ - * * - * MapObject Interface Controls * - * * - ************************************/ - - - // LonLat - Pixel Translation - - /** - * APIMethod: getMapObjectLonLatFromMapObjectPixel - * - * Parameters: - * moPixel - {Object} MapObject Pixel format - * - * Returns: - * {Object} MapObject LonLat translated from MapObject Pixel - */ - getMapObjectLonLatFromMapObjectPixel: function(moPixel) { - var size = this.map.getSize(); - var lon = this.getLongitudeFromMapObjectLonLat(this.mapObject.center); - var lat = this.getLatitudeFromMapObjectLonLat(this.mapObject.center); - var res = this.map.getResolution(); - - var delta_x = moPixel.x - (size.w / 2); - var delta_y = moPixel.y - (size.h / 2); - - var lonlat = new OpenLayers.LonLat( - lon + delta_x * res, - lat - delta_y * res - ); - - if (this.wrapDateLine) { - lonlat = lonlat.wrapDateLine(this.maxExtent); - } - return this.getMapObjectLonLatFromLonLat(lonlat.lon, lonlat.lat); - }, - - /** - * APIMethod: getMapObjectPixelFromMapObjectLonLat - * - * Parameters: - * moLonLat - {Object} MapObject LonLat format - * - * Returns: - * {Object} MapObject Pixel transtlated from MapObject LonLat - */ - getMapObjectPixelFromMapObjectLonLat: function(moLonLat) { - var lon = this.getLongitudeFromMapObjectLonLat(moLonLat); - var lat = this.getLatitudeFromMapObjectLonLat(moLonLat); - var res = this.map.getResolution(); - var extent = this.map.getExtent(); - return this.getMapObjectPixelFromXY((1/res * (lon - extent.left)), - (1/res * (extent.top - lat))); - }, - - - /** - * 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) { - if (this.animationEnabled === false && zoom != this.mapObject.zoom) { - var mapContainer = this.getMapContainer(); - google.maps.event.addListenerOnce( - this.mapObject, - "idle", - function() { - mapContainer.style.visibility = ""; - } - ); - mapContainer.style.visibility = "hidden"; - } - this.mapObject.setOptions({ - center: center, - zoom: zoom - }); - }, - - - // 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: 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 google.maps.LatLng(lonlat.lat, lonlat.lon); - } else { - gLatLng = new google.maps.LatLng(lat, lon); - } - return gLatLng; - }, - - // Pixel - - /** - * APIMethod: getMapObjectPixelFromXY - * - * Parameters: - * x - {Integer} - * y - {Integer} - * - * Returns: - * {Object} MapObject Pixel from x and y parameters - */ - getMapObjectPixelFromXY: function(x, y) { - return new google.maps.Point(x, y); - } - -}; -/* ====================================================================== - OpenLayers/Request.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @requires OpenLayers/Events.js - * @requires OpenLayers/Request/XMLHttpRequest.js - */ - -/** - * TODO: deprecate me - * Use OpenLayers.Request.proxy instead. - */ -OpenLayers.ProxyHost = ""; - -/** - * 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. - */ -if (!OpenLayers.Request) { - /** - * This allows for OpenLayers/Request/XMLHttpRequest.js to be included - * before or after this script. - */ - OpenLayers.Request = {}; -} -OpenLayers.Util.extend(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 - }, - - /** - * Constant: URL_SPLIT_REGEX - */ - URL_SPLIT_REGEX: /([^:]*:)\/\/([^:]*:?[^@]*@)?([^:\/\?]*):?([^\/\?]*)/, - - /** - * APIProperty: events - * {<OpenLayers.Events>} An events object that handles all - * events on the {<OpenLayers.Request>} object. - * - * All event listeners will receive an event object with three properties: - * request - {<OpenLayers.Request.XMLHttpRequest>} The request object. - * config - {Object} The config object sent to the specific request method. - * requestUrl - {String} The request url. - * - * Supported event types: - * complete - Triggered when we have a response from the request, if a - * listener returns false, no further response processing will take - * place. - * success - Triggered when the HTTP response has a success code (200-299). - * failure - Triggered when the HTTP response does not have a success code. - */ - events: new OpenLayers.Events(this), - - /** - * Method: makeSameOrigin - * Using the specified proxy, returns a same origin url of the provided url. - * - * Parameters: - * url - {String} An arbitrary url - * proxy {String|Function} The proxy to use to make the provided url a - * same origin url. - * - * Returns - * {String} the same origin url. If no proxy is provided, the returned url - * will be the same as the provided url. - */ - makeSameOrigin: function(url, proxy) { - var sameOrigin = url.indexOf("http") !== 0; - var urlParts = !sameOrigin && url.match(this.URL_SPLIT_REGEX); - if (urlParts) { - var location = window.location; - sameOrigin = - urlParts[1] == location.protocol && - urlParts[3] == location.hostname; - var uPort = urlParts[4], lPort = location.port; - if (uPort != 80 && uPort != "" || lPort != "80" && lPort != "") { - sameOrigin = sameOrigin && uPort == lPort; - } - } - if (!sameOrigin) { - if (proxy) { - if (typeof proxy == "function") { - url = proxy(url); - } else { - url = proxy + encodeURIComponent(url); - } - } - } - return url; - }, - - /** - * APIMethod: issue - * Create a new XMLHttpRequest object, open it, set any headers, bind - * a callback to done state, and send any data. It is recommended that - * you use one <GET>, <POST>, <PUT>, <DELETE>, <OPTIONS>, or <HEAD>. - * This method is only documented to provide detail on the configuration - * options available to all request methods. - * - * Parameters: - * config - {Object} Object containing properties for configuring the - * 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. Typically, this is only appropriate for <GET> - * requests where the query string will be appended to the url. - * Parameter values that are arrays will be - * concatenated with a comma (note that this goes against form-encoding) - * as is done with <OpenLayers.Util.getParameterString>. - * headers - {Object} Object with header:value pairs to be set on - * the request. - * data - {String | Document} Optional data to send with the request. - * Typically, this is only used with <POST> and <PUT> requests. - * Make sure to provide the appropriate "Content-Type" header for your - * data. For <POST> and <PUT> requests, the content type defaults to - * "application-xml". If your data is a different content type, or - * if you are using a different HTTP method, set the "Content-Type" - * header to match your data type. - * callback - {Function} Function to call when request is done. - * To determine if the request failed, check request.status (200 - * indicates success). - * 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 = config || {}; - config.headers = config.headers || {}; - config = OpenLayers.Util.applyDefaults(config, defaultConfig); - config.headers = OpenLayers.Util.applyDefaults(config.headers, defaultConfig.headers); - // Always set the "X-Requested-With" header to signal that this request - // was issued through the XHR-object. Since header keys are case - // insensitive and we want to allow overriding of the "X-Requested-With" - // header through the user we cannot use applyDefaults, but have to - // check manually whether we were called with a "X-Requested-With" - // header. - var customRequestedWithHeader = false, - headerKey; - for(headerKey in config.headers) { - if (config.headers.hasOwnProperty( headerKey )) { - if (headerKey.toLowerCase() === 'x-requested-with') { - customRequestedWithHeader = true; - } - } - } - if (customRequestedWithHeader === false) { - // we did not have a custom "X-Requested-With" header - config.headers['X-Requested-With'] = 'XMLHttpRequest'; - } - - // create request, open, and set headers - var request = new OpenLayers.Request.XMLHttpRequest(); - var url = OpenLayers.Util.urlAppend(config.url, - OpenLayers.Util.getParameterString(config.params || {})); - url = OpenLayers.Request.makeSameOrigin(url, config.proxy); - request.open( - config.method, url, config.async, config.user, config.password - ); - for(var header in config.headers) { - request.setRequestHeader(header, config.headers[header]); - } - - var events = this.events; - - // we want to execute runCallbacks with "this" as the - // execution scope - var self = this; - - request.onreadystatechange = function() { - if(request.readyState == OpenLayers.Request.XMLHttpRequest.DONE) { - var proceed = events.triggerEvent( - "complete", - {request: request, config: config, requestUrl: url} - ); - if(proceed !== false) { - self.runCallbacks( - {request: request, config: config, requestUrl: url} - ); - } - } - }; - - // send request (optionally with data) and return - // call in a timeout for asynchronous requests so the return is - // available before readyState == 4 for cached docs - if(config.async === false) { - request.send(config.data); - } else { - window.setTimeout(function(){ - if (request.readyState !== 0) { // W3C: 0-UNSENT - request.send(config.data); - } - }, 0); - } - return request; - }, - - /** - * Method: runCallbacks - * Calls the complete, success and failure callbacks. Application - * can listen to the "complete" event, have the listener - * display a confirm window and always return false, and - * execute OpenLayers.Request.runCallbacks if the user - * hits "yes" in the confirm window. - * - * Parameters: - * options - {Object} Hash containing request, config and requestUrl keys - */ - runCallbacks: function(options) { - var request = options.request; - var config = options.config; - - // 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; - } - - if (OpenLayers.Util.createUrlObject(config.url).protocol == "file:" && - request.responseText) { - request.status = 200; - } - complete(request); - - if (!request.status || (request.status >= 200 && request.status < 300)) { - this.events.triggerEvent("success", options); - if(success) { - success(request); - } - } - if(request.status && (request.status < 200 || request.status >= 300)) { - this.events.triggerEvent("failure", options); - if(failure) { - failure(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/Request/XMLHttpRequest.js - ====================================================================== */ - -// XMLHttpRequest.js Copyright (C) 2010 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, - bIE7 = bIE && window.navigator.userAgent.match(/MSIE 7.0/); - - // Enables "XMLHttpRequest()" call next to "new XMLHttpReques()" - function fXMLHttpRequest() { - this._object = oXMLHttpRequest && !bIE7 ? new oXMLHttpRequest : new window.ActiveXObject("Microsoft.XMLHTTP"); - this._listeners = []; - }; - - // Constructor - function cXMLHttpRequest() { - return new fXMLHttpRequest; - }; - cXMLHttpRequest.prototype = fXMLHttpRequest.prototype; - - // 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 = ''; - - // Priority proposal - cXMLHttpRequest.prototype.priority = "NORMAL"; - - // 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) { - // Delete headers, required when object is reused - delete this._headers; - - // When bAsync parameter value is omitted, use true as default - if (arguments.length < 3) - bAsync = true; - - // 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, - fOnUnload; - - // BUGFIX: IE - memory leak on page unload (inter-page leak) - if (bIE && bAsync) { - fOnUnload = function() { - if (nState != cXMLHttpRequest.DONE) { - fCleanTransport(oRequest); - // Safe to abort here since onreadystatechange handler removed - oRequest.abort(); - } - }; - window.attachEvent("onunload", fOnUnload); - } - - // Add method sniffer - if (cXMLHttpRequest.onopen) - cXMLHttpRequest.onopen.apply(this, arguments); - - if (arguments.length > 4) - this._object.open(sMethod, sUrl, bAsync, sUser, sPassword); - else - if (arguments.length > 3) - this._object.open(sMethod, sUrl, bAsync, sUser); - else - this._object.open(sMethod, sUrl, bAsync); - - this.readyState = cXMLHttpRequest.OPENED; - fReadyStateChange(this); - - this._object.onreadystatechange = function() { - if (bGecko && !bAsync) - return; - - // Synchronize state - oRequest.readyState = oRequest._object.readyState; - - // - fSynchronizeValues(oRequest); - - // BUGFIX: Firefox fires unnecessary DONE when aborting - if (oRequest._aborted) { - // Reset readyState to UNSENT - oRequest.readyState = cXMLHttpRequest.UNSENT; - - // Return now - return; - } - - if (oRequest.readyState == cXMLHttpRequest.DONE) { - // Free up queue - delete oRequest._data; -/* if (bAsync) - fQueue_remove(oRequest);*/ - // - 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 - if (sUser) { - if (sPassword) - oRequest._object.open(sMethod, sUrl, bAsync, sUser, sPassword); - else - oRequest._object.open(sMethod, sUrl, bAsync, sUser); - } - else - oRequest._object.open(sMethod, sUrl, bAsync); - 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 until 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; - } - }; - function fXMLHttpRequest_send(oRequest) { - oRequest._object.send(oRequest._data); - - // BUGFIX: Gecko - missing readystatechange calls in synchronous requests - if (bGecko && !oRequest._async) { - oRequest.readyState = cXMLHttpRequest.OPENED; - - // Synchronize state - fSynchronizeValues(oRequest); - - // Simulate missing states - while (oRequest.readyState < cXMLHttpRequest.DONE) { - oRequest.readyState++; - fReadyStateChange(oRequest); - // Check if we are aborted - if (oRequest._aborted) - return; - } - } - }; - cXMLHttpRequest.prototype.send = function(vData) { - // Add method sniffer - if (cXMLHttpRequest.onsend) - cXMLHttpRequest.onsend.apply(this, arguments); - - if (!arguments.length) - vData = null; - - // 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._data = vData; -/* - // Add to queue - if (this._async) - fQueue_add(this); - else*/ - fXMLHttpRequest_send(this); - }; - cXMLHttpRequest.prototype.abort = function() { - // Add method sniffer - if (cXMLHttpRequest.onabort) - cXMLHttpRequest.onabort.apply(this, arguments); - - // BUGFIX: Gecko - unnecessary DONE when aborting - if (this.readyState > cXMLHttpRequest.UNSENT) - this._aborted = true; - - this._object.abort(); - - // BUGFIX: IE - memory leak - fCleanTransport(this); - - this.readyState = cXMLHttpRequest.UNSENT; - - delete this._data; -/* if (this._async) - fQueue_remove(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); - }; - - // EventTarget interface implementation - cXMLHttpRequest.prototype.addEventListener = function(sName, fHandler, bUseCapture) { - for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++) - if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture) - return; - // Add listener - this._listeners.push([sName, fHandler, bUseCapture]); - }; - - cXMLHttpRequest.prototype.removeEventListener = function(sName, fHandler, bUseCapture) { - for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++) - if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture) - break; - // Remove listener - if (oListener) - this._listeners.splice(nIndex, 1); - }; - - cXMLHttpRequest.prototype.dispatchEvent = function(oEvent) { - var oEventPseudo = { - 'type': oEvent.type, - 'target': this, - 'currentTarget':this, - 'eventPhase': 2, - 'bubbles': oEvent.bubbles, - 'cancelable': oEvent.cancelable, - 'timeStamp': oEvent.timeStamp, - 'stopPropagation': function() {}, // There is no flow - 'preventDefault': function() {}, // There is no default action - 'initEvent': function() {} // Original event object should be initialized - }; - - // Execute onreadystatechange - if (oEventPseudo.type == "readystatechange" && this.onreadystatechange) - (this.onreadystatechange.handleEvent || this.onreadystatechange).apply(this, [oEventPseudo]); - - // Execute listeners - for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++) - if (oListener[0] == oEventPseudo.type && !oListener[2]) - (oListener[1].handleEvent || oListener[1]).apply(this, [oEventPseudo]); - }; - - // - cXMLHttpRequest.prototype.toString = function() { - return '[' + "object" + ' ' + "XMLHttpRequest" + ']'; - }; - - cXMLHttpRequest.toString = function() { - return '[' + "XMLHttpRequest" + ']'; - }; - - // Helper function - function fReadyStateChange(oRequest) { - // Sniffing code - if (cXMLHttpRequest.onreadystatechange) - cXMLHttpRequest.onreadystatechange.apply(oRequest); - - // Fake event - oRequest.dispatchEvent({ - 'type': "readystatechange", - 'bubbles': false, - 'cancelable': false, - 'timeStamp': new Date + 0 - }); - }; - - function fGetDocument(oRequest) { - var oDocument = oRequest.responseXML, - sResponse = oRequest.responseText; - // Try parsing responseText - if (bIE && sResponse && oDocument && !oDocument.documentElement && oRequest.getResponseHeader("Content-Type").match(/[^\/]+\/[^\+]+\+xml/)) { - oDocument = new window.ActiveXObject("Microsoft.XMLDOM"); - oDocument.async = false; - oDocument.validateOnParse = false; - oDocument.loadXML(sResponse); - } - // Check if there is no error in document - if (oDocument) - if ((bIE && oDocument.parseError != 0) || !oDocument.documentElement || (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; - }; -/* - // Queue manager - var oQueuePending = {"CRITICAL":[],"HIGH":[],"NORMAL":[],"LOW":[],"LOWEST":[]}, - aQueueRunning = []; - function fQueue_add(oRequest) { - oQueuePending[oRequest.priority in oQueuePending ? oRequest.priority : "NORMAL"].push(oRequest); - // - setTimeout(fQueue_process); - }; - - function fQueue_remove(oRequest) { - for (var nIndex = 0, bFound = false; nIndex < aQueueRunning.length; nIndex++) - if (bFound) - aQueueRunning[nIndex - 1] = aQueueRunning[nIndex]; - else - if (aQueueRunning[nIndex] == oRequest) - bFound = true; - if (bFound) - aQueueRunning.length--; - // - setTimeout(fQueue_process); - }; - - function fQueue_process() { - if (aQueueRunning.length < 6) { - for (var sPriority in oQueuePending) { - if (oQueuePending[sPriority].length) { - var oRequest = oQueuePending[sPriority][0]; - oQueuePending[sPriority] = oQueuePending[sPriority].slice(1); - // - aQueueRunning.push(oRequest); - // Send request - fXMLHttpRequest_send(oRequest); - break; - } - } - } - }; -*/ - // 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/. - */ - if (!OpenLayers.Request) { - /** - * This allows for OpenLayers/Request.js to be included - * before or after this script. - */ - OpenLayers.Request = {}; - } - OpenLayers.Request.XMLHttpRequest = cXMLHttpRequest; -})(); -/* ====================================================================== - OpenLayers/Filter/Comparison.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository 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 = "~"; - * - OpenLayers.Filter.Comparison.IS_NULL = "NULL"; - */ - 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, - - /** - * Property: matchCase - * {Boolean} Force case sensitive searches for EQUAL_TO and NOT_EQUAL_TO - * comparisons. The Filter Encoding 1.1 specification added a matchCase - * attribute to ogc:PropertyIsEqualTo and ogc:PropertyIsNotEqualTo - * elements. This property will be serialized with those elements only - * if using the v1.1.0 filter format. However, when evaluating filters - * here, the matchCase property will always be respected (for EQUAL_TO - * and NOT_EQUAL_TO). Default is true. - */ - matchCase: true, - - /** - * 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]); - // since matchCase on PropertyIsLike is not schema compliant, we only - // want to use this if explicitly asked for - if (this.type === OpenLayers.Filter.Comparison.LIKE - && options.matchCase === undefined) { - this.matchCase = null; - } - }, - - /** - * APIMethod: evaluate - * Evaluates this filter in a specific context. - * - * Parameters: - * context - {Object} Context to use in evaluating the filter. If a vector - * feature is provided, the feature.attributes will be used as context. - * - * Returns: - * {Boolean} The filter applies. - */ - evaluate: function(context) { - if (context instanceof OpenLayers.Feature.Vector) { - context = context.attributes; - } - var result = false; - var got = context[this.property]; - var exp; - switch(this.type) { - case OpenLayers.Filter.Comparison.EQUAL_TO: - exp = this.value; - if(!this.matchCase && - typeof got == "string" && typeof exp == "string") { - result = (got.toUpperCase() == exp.toUpperCase()); - } else { - result = (got == exp); - } - break; - case OpenLayers.Filter.Comparison.NOT_EQUAL_TO: - exp = this.value; - if(!this.matchCase && - typeof got == "string" && typeof exp == "string") { - result = (got.toUpperCase() != exp.toUpperCase()); - } else { - result = (got != exp); - } - break; - case OpenLayers.Filter.Comparison.LESS_THAN: - result = got < this.value; - break; - case OpenLayers.Filter.Comparison.GREATER_THAN: - result = got > this.value; - break; - case OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO: - result = got <= this.value; - break; - case OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO: - result = got >= this.value; - break; - case OpenLayers.Filter.Comparison.BETWEEN: - result = (got >= this.lowerBoundary) && - (got <= this.upperBoundary); - break; - case OpenLayers.Filter.Comparison.LIKE: - var regexp = new RegExp(this.value, "gi"); - result = regexp.test(got); - break; - case OpenLayers.Filter.Comparison.IS_NULL: - result = (got === null); - break; - } - return result; - }, - - /** - * 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 "." - * escapeChar - {Char} escape character in the above value, default is - * "!" - * - * Returns: - * {String} regular expression string - */ - value2regex: function(wildCard, singleChar, escapeChar) { - if (wildCard == ".") { - throw new Error("'.' is an unsupported wildCard character for " + - "OpenLayers.Filter.Comparison"); - } - - - // 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; - }, - - /** - * APIMethod: clone - * Clones this filter. - * - * Returns: - * {<OpenLayers.Filter.Comparison>} Clone of this filter. - */ - clone: function() { - return OpenLayers.Util.extend(new OpenLayers.Filter.Comparison(), this); - }, - - 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.Comparison.IS_NULL = "NULL"; -/* ====================================================================== - OpenLayers/Popup/FramedCloud.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @requires OpenLayers/Popup/Framed.js - * @requires OpenLayers/Util.js - * @requires OpenLayers/BaseTypes/Bounds.js - * @requires OpenLayers/BaseTypes/Pixel.js - * @requires OpenLayers/BaseTypes/Size.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(1276, 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(-1238, 0) - }, - { //bottom-left - size: new OpenLayers.Size('auto', 19), - anchor: new OpenLayers.Bounds(0, 32, 22, null), - position: new OpenLayers.Pixel(0, -631) - }, - { //bottom-right - size: new OpenLayers.Size(22, 18), - anchor: new OpenLayers.Bounds(null, 32, 0, null), - position: new OpenLayers.Pixel(-1238, -632) - }, - { // stem - size: new OpenLayers.Size(81, 35), - anchor: new OpenLayers.Bounds(null, 0, 0, null), - position: new OpenLayers.Pixel(0, -688) - } - ] - }, - "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(-1238, 0) - }, - { //bottom-left - size: new OpenLayers.Size('auto', 19), - anchor: new OpenLayers.Bounds(0, 32, 22, null), - position: new OpenLayers.Pixel(0, -631) - }, - { //bottom-right - size: new OpenLayers.Size(22, 19), - anchor: new OpenLayers.Bounds(null, 32, 0, null), - position: new OpenLayers.Pixel(-1238, -631) - }, - { // stem - size: new OpenLayers.Size(81, 35), - anchor: new OpenLayers.Bounds(0, 0, null, null), - position: new OpenLayers.Pixel(-215, -687) - } - ] - }, - "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(-1238, 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(-1238, -629) - }, - { // stem - size: new OpenLayers.Size(81, 33), - 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(-1238, 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(-1238, -629) - }, - { // stem - size: new OpenLayers.Size(81, 33), - 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(1200, 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.getImageLocation('cloud-popup-relative.png'); - OpenLayers.Popup.Framed.prototype.initialize.apply(this, arguments); - this.contentDiv.className = this.contentDisplayClass; - }, - - CLASS_NAME: "OpenLayers.Popup.FramedCloud" -}); -/* ====================================================================== - OpenLayers/Rule.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - - -/** - * @requires OpenLayers/BaseTypes/Class.js - * @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: null, - - /** - * 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, - - /** - * Property: symbolizers - * {Array} Collection of symbolizers associated with this rule. If - * provided at construction, the symbolizers array has precedence - * over the deprecated symbolizer property. Note that multiple - * symbolizers are not currently supported by the vector renderers. - * Rules with multiple symbolizers are currently only useful for - * maintaining elements in an SLD document. - */ - symbolizers: 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.symbolizer = {}; - OpenLayers.Util.extend(this, options); - if (this.symbolizers) { - delete this.symbolizer; - } - this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_"); - }, - - /** - * 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; - delete this.symbolizers; - }, - - /** - * 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; - }, - - /** - * APIMethod: clone - * Clones this rule. - * - * Returns: - * {<OpenLayers.Rule>} Clone of this rule. - */ - clone: function() { - var options = OpenLayers.Util.extend({}, this); - if (this.symbolizers) { - // clone symbolizers - var len = this.symbolizers.length; - options.symbolizers = new Array(len); - for (var i=0; i<len; ++i) { - options.symbolizers[i] = this.symbolizers[i].clone(); - } - } else { - // clone symbolizer - options.symbolizer = {}; - var value, type; - for(var key in this.symbolizer) { - value = this.symbolizer[key]; - type = typeof value; - if(type === "object") { - options.symbolizer[key] = OpenLayers.Util.extend({}, value); - } else if(type === "string") { - options.symbolizer[key] = value; - } - } - } - // clone filter - options.filter = this.filter && this.filter.clone(); - // clone context - options.context = this.context && OpenLayers.Util.extend({}, this.context); - return new OpenLayers.Rule(options); - }, - - CLASS_NAME: "OpenLayers.Rule" -}); -/* ====================================================================== - OpenLayers/Renderer/VML.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository 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 (num | 0) to truncate a - * float value to an integer. This is done because it seems that VML doesn't - * support float values. - * - * Inherits from: - * - <OpenLayers.Renderer.Elements> - */ -OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, { - - /** - * Property: xmlns - * {String} XML Namespace URN - */ - xmlns: "urn:schemas-microsoft-com:vml", - - /** - * Property: symbolCache - * {DOMElement} node holding symbols. This hash is keyed by symbol name, - * and each value is a hash with a "path" and an "extent" property. - */ - symbolCache: {}, - - /** - * Property: offset - * {Object} Hash with "x" and "y" properties - */ - offset: null, - - /** - * Constructor: OpenLayers.Renderer.VML - * Create a new VML renderer. - * - * Parameters: - * containerID - {String} The id for the element that contains the renderer - */ - initialize: function(containerID) { - if (!this.supported()) { - return; - } - if (!document.namespaces.olv) { - document.namespaces.add("olv", this.xmlns); - var style = document.createStyleSheet(); - var shapes = ['shape','rect', 'oval', 'fill', 'stroke', 'imagedata', 'group','textbox']; - for (var i = 0, len = shapes.length; i < len; i++) { - - style.addRule('olv\\:' + shapes[i], "behavior: url(#default#VML); " + - "position: absolute; display: inline-block;"); - } - } - - OpenLayers.Renderer.Elements.prototype.initialize.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) { - var coordSysUnchanged = OpenLayers.Renderer.Elements.prototype.setExtent.apply(this, arguments); - var resolution = this.getResolution(); - - var left = (extent.left/resolution) | 0; - var top = (extent.top/resolution - this.size.h) | 0; - if (resolutionChanged || !this.offset) { - this.offset = {x: left, y: top}; - left = 0; - top = 0; - } else { - left = left - this.offset.x; - top = top - this.offset.y; - } - - - var org = (left - this.xOffset) + " " + top; - this.root.coordorigin = org; - var roots = [this.root, this.vectorRoot, this.textRoot]; - var root; - for(var i=0, len=roots.length; i<len; ++i) { - root = roots[i]; - - var size = this.size.w + " " + this.size.h; - root.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 coordSysUnchanged; - }, - - - /** - * 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); - - // setting width and height on all roots to avoid flicker which we - // would get with 100% width and height on child roots - var roots = [ - this.rendererRoot, - this.root, - this.vectorRoot, - this.textRoot - ]; - var w = this.size.w + "px"; - var h = this.size.h + "px"; - var root; - for(var i=0, len=roots.length; i<len; ++i) { - root = roots[i]; - root.style.width = w; - root.style.height = h; - } - }, - - /** - * 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": - 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 fillColor = style.fillColor; - - var title = style.title || style.graphicTitle; - if (title) { - node.title = title; - } - - if (node._geometryClass === "OpenLayers.Geometry.Point") { - if (style.externalGraphic) { - options.isFilled = true; - 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 - this.featureDx)/resolution - this.offset.x)+xOffset) | 0) + "px"; - node.style.top = (((geometry.y/resolution - this.offset.y)-(yOffset+height)) | 0) + "px"; - node.style.width = width + "px"; - node.style.height = height + "px"; - node.style.flip = "y"; - - // modify fillColor and options for stroke styling below - fillColor = "none"; - options.isStroked = false; - } else if (this.isComplexSymbol(style.graphicName)) { - var cache = this.importSymbol(style.graphicName); - node.path = cache.path; - node.coordorigin = cache.left + "," + cache.bottom; - var size = cache.size; - node.coordsize = size + "," + size; - this.drawCircle(node, geometry, style.pointRadius); - node.style.flip = "y"; - } else { - this.drawCircle(node, geometry, style.pointRadius); - } - } - - // fill - if (options.isFilled) { - node.fillcolor = fillColor; - } else { - node.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.opacity = style.fillOpacity; - - if (node._geometryClass === "OpenLayers.Geometry.Point" && - style.externalGraphic) { - - // override fillOpacity - if (style.graphicOpacity) { - fill.opacity = style.graphicOpacity; - } - - fill.src = style.externalGraphic; - fill.type = "frame"; - - if (!(style.graphicWidth && style.graphicHeight)) { - fill.aspect = "atmost"; - } - } - if (fill.parentNode != node) { - node.appendChild(fill); - } - } - - // additional rendering for rotated graphics or symbols - var rotation = style.rotation; - if ((rotation !== undefined || node._rotation !== undefined)) { - node._rotation = rotation; - if (style.externalGraphic) { - this.graphicRotate(node, xOffset, yOffset, style); - // 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.opacity = 0; - } else if(node._geometryClass === "OpenLayers.Geometry.Point") { - node.style.rotation = rotation || 0; - } - } - - // stroke - var strokes = node.getElementsByTagName("stroke"); - var stroke = (strokes.length == 0) ? null : strokes[0]; - if (!options.isStroked) { - node.stroked = false; - if (stroke) { - stroke.on = false; - } - } else { - if (!stroke) { - stroke = this.createNode('olv:stroke', node.id + "_stroke"); - node.appendChild(stroke); - } - stroke.on = true; - stroke.color = style.strokeColor; - stroke.weight = style.strokeWidth + "px"; - stroke.opacity = style.strokeOpacity; - stroke.endcap = style.strokeLinecap == 'butt' ? 'flat' : - (style.strokeLinecap || 'round'); - if (style.strokeDashstyle) { - stroke.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 - * style - {Object} - */ - graphicRotate: function(node, xOffset, yOffset, style) { - var style = style || node._style; - var rotation = style.rotation || 0; - - 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, style); - } - }, 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 rot = rotation * Math.PI / 180; - var sintheta = Math.sin(rot); - var costheta = Math.cos(rot); - - // 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 - * Does some node postprocessing to work around browser issues: - * - 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. - * - In some cases, a node won't become visible after being drawn. Setting - * style.visibility to "visible" works around that. - * - * Parameters: - * node - {DOMElement} - */ - postDraw: function(node) { - node.style.visibility = "visible"; - var fillColor = node._style.fillColor; - var strokeColor = node._style.strokeColor; - if (fillColor == "none" && - node.fillcolor != fillColor) { - node.fillcolor = fillColor; - } - if (strokeColor == "none" && - node.strokecolor != strokeColor) { - node.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 - this.featureDx)/resolution - this.offset.x) | 0, - (bbox.bottom/resolution - this.offset.y) | 0, - ((bbox.right - this.featureDx)/resolution - this.offset.x) | 0, - (bbox.top/resolution - this.offset.y) | 0); - - // 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.id = id; - } - - // IE hack to make elements unselectable, to prevent 'blue flash' - // while dragging vectors; #1410 - node.unselectable = 'on'; - node.onselectstart = OpenLayers.Function.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 - * - * Parameters: - * suffix - {String} suffix to append to the id - * - * Returns: - * {DOMElement} - */ - createRoot: function(suffix) { - return this.nodeFactory(this.container.id + suffix, "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 - this.featureDx) /resolution - this.offset.x) | 0) - radius) + "px"; - node.style.top = (((geometry.y /resolution - this.offset.y) | 0) - 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 - this.featureDx)/resolution - this.offset.x) | 0; - y = (comp.y/resolution - this.offset.y) | 0; - parts[i] = " " + x + "," + y + " 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 j, jj, points, area, first, second, i, ii, comp, pathComp, x, y; - for (j=0, jj=geometry.components.length; j<jj; j++) { - path.push("m"); - points = geometry.components[j].components; - // we only close paths of interior rings with area - area = (j === 0); - first = null; - second = null; - for (i=0, ii=points.length; i<ii; i++) { - comp = points[i]; - x = ((comp.x - this.featureDx) / resolution - this.offset.x) | 0; - y = (comp.y / resolution - this.offset.y) | 0; - pathComp = " " + x + "," + y; - path.push(pathComp); - if (i==0) { - path.push(" l"); - } - if (!area) { - // IE improperly renders sub-paths that have no area. - // Instead of checking the area of every ring, we confirm - // the ring has at least three distinct points. This does - // not catch all non-zero area cases, but it greatly improves - // interior ring digitizing and is a minor performance hit - // when rendering rings with many points. - if (!first) { - first = pathComp; - } else if (first != pathComp) { - if (!second) { - second = pathComp; - } else if (second != pathComp) { - // stop looking - area = true; - } - } - } - } - path.push(area ? " 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 - this.featureDx)/resolution - this.offset.x) | 0) + "px"; - node.style.top = ((geometry.y/resolution - this.offset.y) | 0) + "px"; - node.style.width = ((geometry.width/resolution) | 0) + "px"; - node.style.height = ((geometry.height/resolution) | 0) + "px"; - - return node; - }, - - /** - * Method: drawText - * This method is only called by the renderer itself. - * - * Parameters: - * featureId - {String} - * style - - * location - {<OpenLayers.Geometry.Point>} - */ - drawText: function(featureId, style, location) { - var label = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX, "olv:rect"); - var textbox = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX + "_textbox", "olv:textbox"); - - var resolution = this.getResolution(); - label.style.left = (((location.x - this.featureDx)/resolution - this.offset.x) | 0) + "px"; - label.style.top = ((location.y/resolution - this.offset.y) | 0) + "px"; - label.style.flip = "y"; - - textbox.innerText = style.label; - - if (style.cursor != "inherit" && style.cursor != null) { - textbox.style.cursor = style.cursor; - } - if (style.fontColor) { - textbox.style.color = style.fontColor; - } - if (style.fontOpacity) { - textbox.style.filter = 'alpha(opacity=' + (style.fontOpacity * 100) + ')'; - } - if (style.fontFamily) { - textbox.style.fontFamily = style.fontFamily; - } - if (style.fontSize) { - textbox.style.fontSize = style.fontSize; - } - if (style.fontWeight) { - textbox.style.fontWeight = style.fontWeight; - } - if (style.fontStyle) { - textbox.style.fontStyle = style.fontStyle; - } - if(style.labelSelect === true) { - label._featureId = featureId; - textbox._featureId = featureId; - textbox._geometry = location; - textbox._geometryClass = location.CLASS_NAME; - } - textbox.style.whiteSpace = "nowrap"; - // fun with IE: IE7 in standards compliant mode does not display any - // text with a left inset of 0. So we set this to 1px and subtract one - // pixel later when we set label.style.left - textbox.inset = "1px,0px,0px,0px"; - - if(!label.parentNode) { - label.appendChild(textbox); - this.textRoot.appendChild(label); - } - - var align = style.labelAlign || "cm"; - if (align.length == 1) { - align += "m"; - } - var xshift = textbox.clientWidth * - (OpenLayers.Renderer.VML.LABEL_SHIFT[align.substr(0,1)]); - var yshift = textbox.clientHeight * - (OpenLayers.Renderer.VML.LABEL_SHIFT[align.substr(1,1)]); - label.style.left = parseInt(label.style.left)-xshift-1+"px"; - label.style.top = parseInt(label.style.top)+yshift+"px"; - - }, - - /** - * Method: moveRoot - * moves this renderer's root to a different renderer. - * - * Parameters: - * renderer - {<OpenLayers.Renderer>} target renderer for the moved root - * root - {DOMElement} optional root node. To be used when this renderer - * holds roots from multiple layers to tell this method which one to - * detach - * - * Returns: - * {Boolean} true if successful, false otherwise - */ - moveRoot: function(renderer) { - var layer = this.map.getLayer(renderer.container.id); - if(layer instanceof OpenLayers.Layer.Vector.RootContainer) { - layer = this.map.getLayer(this.container.id); - } - layer && layer.renderer.clear(); - OpenLayers.Renderer.Elements.prototype.moveRoot.apply(this, arguments); - layer && layer.redraw(); - }, - - /** - * 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'); - } - - 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) { - var x = symbol[i]; - var 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(" "); - - var diff = (symbolExtent.getWidth() - symbolExtent.getHeight()) / 2; - if(diff > 0) { - symbolExtent.bottom = symbolExtent.bottom - diff; - symbolExtent.top = symbolExtent.top + diff; - } else { - symbolExtent.left = symbolExtent.left + diff; - symbolExtent.right = symbolExtent.right - diff; - } - - cache = { - path: path, - size: symbolExtent.getWidth(), // equals getHeight() now - left: symbolExtent.left, - bottom: symbolExtent.bottom - }; - this.symbolCache[id] = cache; - - return cache; - }, - - CLASS_NAME: "OpenLayers.Renderer.VML" -}); - -/** - * Constant: OpenLayers.Renderer.VML.LABEL_SHIFT - * {Object} - */ -OpenLayers.Renderer.VML.LABEL_SHIFT = { - "l": 0, - "c": .5, - "r": 1, - "t": 0, - "m": .5, - "b": 1 -}; -/* ====================================================================== - OpenLayers/Protocol.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @requires OpenLayers/BaseTypes/Class.js - */ - -/** - * 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>} The format used by this protocol. - */ - format: null, - - /** - * Property: options - * {Object} 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, - - /** - * Property: defaultFilter - * {<OpenLayers.Filter>} Optional default filter to read requests - */ - defaultFilter: null, - - /** - * 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; - }, - - /** - * Method: mergeWithDefaultFilter - * Merge filter passed to the read method with the default one - * - * Parameters: - * filter - {<OpenLayers.Filter>} - */ - mergeWithDefaultFilter: function(filter) { - var merged; - if (filter && this.defaultFilter) { - merged = new OpenLayers.Filter.Logical({ - type: OpenLayers.Filter.Logical.AND, - filters: [this.defaultFilter, filter] - }); - } else { - merged = filter || this.defaultFilter || undefined; - } - return merged; - }, - - /** - * APIMethod: destroy - * Clean up the protocol. - */ - destroy: function() { - this.options = null; - this.format = null; - }, - - /** - * APIMethod: 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(options) { - options = options || {}; - options.filter = this.mergeWithDefaultFilter(options.filter); - }, - - - /** - * APIMethod: 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() { - }, - - /** - * APIMethod: 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() { - }, - - /** - * APIMethod: 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() { - }, - - /** - * APIMethod: 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() { - }, - - /** - * Method: abort - * Abort an ongoing request. - * - * Parameters: - * response - {<OpenLayers.Protocol.Response>} - */ - abort: function(response) { - }, - - /** - * Method: createCallback - * Returns a function that applies the given public method with resp and - * options arguments. - * - * Parameters: - * method - {Function} The method to be applied by the callback. - * response - {<OpenLayers.Protocol.Response>} The protocol response object. - * options - {Object} Options sent to the protocol method - */ - createCallback: function(method, response, options) { - return OpenLayers.Function.bind(function() { - method.apply(this, [response, options]); - }, this); - }, - - 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. Depending on the - * protocol's read payload, either features or data will be populated. - */ - features: null, - - /** - * Property: data - * {Object} - * The data returned in the response by the server. Depending on the - * protocol's read payload, either features or data will be populated. - */ - data: 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, - - /** - * Property: error - * {Object} The error object in case a service exception was encountered. - */ - error: 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/Protocol/HTTP.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @requires OpenLayers/Protocol.js - * @requires OpenLayers/Request/XMLHttpRequest.js - */ - -/** - * if application uses the query string, for example, for BBOX parameters, - * OpenLayers/Format/QueryStringFilter.js should be included in the build config file - */ - -/** - * Class: OpenLayers.Protocol.HTTP - * A basic HTTP protocol for vector layers. Create a new instance with the - * <OpenLayers.Protocol.HTTP> constructor. - * - * Inherits from: - * - <OpenLayers.Protocol> - */ -OpenLayers.Protocol.HTTP = OpenLayers.Class(OpenLayers.Protocol, { - - /** - * Property: url - * {String} Service URL, read-only, set through the options - * passed to constructor. - */ - url: null, - - /** - * Property: headers - * {Object} HTTP request headers, read-only, set through the options - * passed to the constructor, - * Example: {'Content-Type': 'plain/text'} - */ - headers: null, - - /** - * Property: params - * {Object} Parameters of GET requests, read-only, set through the options - * passed to the constructor, - * Example: {'bbox': '5,5,5,5'} - */ - params: null, - - /** - * Property: callback - * {Object} Function to be called when the <read>, <create>, - * <update>, <delete> or <commit> operation completes, read-only, - * set through the options passed to the constructor. - */ - callback: null, - - /** - * Property: scope - * {Object} Callback execution scope, read-only, set through the - * options passed to the constructor. - */ - scope: null, - - /** - * APIProperty: readWithPOST - * {Boolean} true if read operations are done with POST requests - * instead of GET, defaults to false. - */ - readWithPOST: false, - - /** - * APIProperty: updateWithPOST - * {Boolean} true if update operations are done with POST requests - * defaults to false. - */ - updateWithPOST: false, - - /** - * APIProperty: deleteWithPOST - * {Boolean} true if delete operations are done with POST requests - * defaults to false. - * if true, POST data is set to output of format.write(). - */ - deleteWithPOST: false, - - /** - * Property: wildcarded. - * {Boolean} If true percent signs are added around values - * read from LIKE filters, for example if the protocol - * read method is passed a LIKE filter whose property - * is "foo" and whose value is "bar" the string - * "foo__ilike=%bar%" will be sent in the query string; - * defaults to false. - */ - wildcarded: false, - - /** - * APIProperty: srsInBBOX - * {Boolean} Include the SRS identifier in BBOX query string parameter. - * Default is false. If true and the layer has a projection object set, - * any BBOX filter will be serialized with a fifth item identifying the - * projection. E.g. bbox=-1000,-1000,1000,1000,EPSG:900913 - */ - srsInBBOX: false, - - /** - * Constructor: OpenLayers.Protocol.HTTP - * A class for giving layers generic HTTP protocol. - * - * Parameters: - * options - {Object} Optional object whose properties will be set on the - * instance. - * - * Valid options include: - * url - {String} - * headers - {Object} - * params - {Object} URL parameters for GET requests - * format - {<OpenLayers.Format>} - * callback - {Function} - * scope - {Object} - */ - initialize: function(options) { - options = options || {}; - this.params = {}; - this.headers = {}; - OpenLayers.Protocol.prototype.initialize.apply(this, arguments); - - if (!this.filterToParams && OpenLayers.Format.QueryStringFilter) { - var format = new OpenLayers.Format.QueryStringFilter({ - wildcarded: this.wildcarded, - srsInBBOX: this.srsInBBOX - }); - this.filterToParams = function(filter, params) { - return format.write(filter, params); - }; - } - }, - - /** - * APIMethod: destroy - * Clean up the protocol. - */ - destroy: function() { - this.params = null; - this.headers = null; - OpenLayers.Protocol.prototype.destroy.apply(this); - }, - - /** - * APIMethod: filterToParams - * Optional method to translate an <OpenLayers.Filter> object into an object - * that can be serialized as request query string provided. If a custom - * method is not provided, the filter will be serialized using the - * <OpenLayers.Format.QueryStringFilter> class. - * - * Parameters: - * filter - {<OpenLayers.Filter>} filter to convert. - * params - {Object} The parameters object. - * - * Returns: - * {Object} The resulting parameters object. - */ - - /** - * APIMethod: read - * Construct a request for reading new features. - * - * Parameters: - * options - {Object} Optional object for configuring the request. - * This object is modified and should not be reused. - * - * Valid options: - * url - {String} Url for the request. - * params - {Object} Parameters to get serialized as a query string. - * headers - {Object} Headers to be set on the request. - * filter - {<OpenLayers.Filter>} Filter to get serialized as a - * query string. - * readWithPOST - {Boolean} If the request should be done with POST. - * - * Returns: - * {<OpenLayers.Protocol.Response>} A response object, whose "priv" property - * references the HTTP request, this object is also passed to the - * callback function when the request completes, its "features" property - * is then populated with the features received from the server. - */ - read: function(options) { - OpenLayers.Protocol.prototype.read.apply(this, arguments); - options = options || {}; - options.params = OpenLayers.Util.applyDefaults( - options.params, this.options.params); - options = OpenLayers.Util.applyDefaults(options, this.options); - if (options.filter && this.filterToParams) { - options.params = this.filterToParams( - options.filter, options.params - ); - } - var readWithPOST = (options.readWithPOST !== undefined) ? - options.readWithPOST : this.readWithPOST; - var resp = new OpenLayers.Protocol.Response({requestType: "read"}); - if(readWithPOST) { - var headers = options.headers || {}; - headers["Content-Type"] = "application/x-www-form-urlencoded"; - resp.priv = OpenLayers.Request.POST({ - url: options.url, - callback: this.createCallback(this.handleRead, resp, options), - data: OpenLayers.Util.getParameterString(options.params), - headers: headers - }); - } else { - resp.priv = OpenLayers.Request.GET({ - url: options.url, - callback: this.createCallback(this.handleRead, resp, options), - params: options.params, - headers: options.headers - }); - } - return resp; - }, - - /** - * Method: handleRead - * Individual callbacks are created for read, create and update, should - * a subclass need to override each one separately. - * - * Parameters: - * resp - {<OpenLayers.Protocol.Response>} The response object to pass to - * the user callback. - * options - {Object} The user options passed to the read call. - */ - handleRead: function(resp, options) { - this.handleResponse(resp, options); - }, - - /** - * APIMethod: 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. - * This object is modified and should not be reused. - * - * Returns: - * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response> - * object, whose "priv" property references the HTTP request, this - * object is also passed to the callback function when the request - * completes, its "features" property is then populated with the - * the features received from the server. - */ - create: function(features, options) { - options = OpenLayers.Util.applyDefaults(options, this.options); - - var resp = new OpenLayers.Protocol.Response({ - reqFeatures: features, - requestType: "create" - }); - - resp.priv = OpenLayers.Request.POST({ - url: options.url, - callback: this.createCallback(this.handleCreate, resp, options), - headers: options.headers, - data: this.format.write(features) - }); - - return resp; - }, - - /** - * Method: handleCreate - * Called the the request issued by <create> is complete. May be overridden - * by subclasses. - * - * Parameters: - * resp - {<OpenLayers.Protocol.Response>} The response object to pass to - * any user callback. - * options - {Object} The user options passed to the create call. - */ - handleCreate: function(resp, options) { - this.handleResponse(resp, options); - }, - - /** - * APIMethod: update - * Construct a request updating modified feature. - * - * Parameters: - * feature - {<OpenLayers.Feature.Vector>} - * options - {Object} Optional object for configuring the request. - * This object is modified and should not be reused. - * - * Returns: - * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response> - * object, whose "priv" property references the HTTP request, this - * object is also passed to the callback function when the request - * completes, its "features" property is then populated with the - * the feature received from the server. - */ - update: function(feature, options) { - options = options || {}; - var url = options.url || - feature.url || - this.options.url + "/" + feature.fid; - options = OpenLayers.Util.applyDefaults(options, this.options); - - var resp = new OpenLayers.Protocol.Response({ - reqFeatures: feature, - requestType: "update" - }); - - var method = this.updateWithPOST ? "POST" : "PUT"; - resp.priv = OpenLayers.Request[method]({ - url: url, - callback: this.createCallback(this.handleUpdate, resp, options), - headers: options.headers, - data: this.format.write(feature) - }); - - return resp; - }, - - /** - * Method: handleUpdate - * Called the the request issued by <update> is complete. May be overridden - * by subclasses. - * - * Parameters: - * resp - {<OpenLayers.Protocol.Response>} The response object to pass to - * any user callback. - * options - {Object} The user options passed to the update call. - */ - handleUpdate: function(resp, options) { - this.handleResponse(resp, options); - }, - - /** - * APIMethod: delete - * Construct a request deleting a removed feature. - * - * Parameters: - * feature - {<OpenLayers.Feature.Vector>} - * options - {Object} Optional object for configuring the request. - * This object is modified and should not be reused. - * - * Returns: - * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response> - * object, whose "priv" property references the HTTP request, this - * object is also passed to the callback function when the request - * completes. - */ - "delete": function(feature, options) { - options = options || {}; - var url = options.url || - feature.url || - this.options.url + "/" + feature.fid; - options = OpenLayers.Util.applyDefaults(options, this.options); - - var resp = new OpenLayers.Protocol.Response({ - reqFeatures: feature, - requestType: "delete" - }); - - var method = this.deleteWithPOST ? "POST" : "DELETE"; - var requestOptions = { - url: url, - callback: this.createCallback(this.handleDelete, resp, options), - headers: options.headers - }; - if (this.deleteWithPOST) { - requestOptions.data = this.format.write(feature); - } - resp.priv = OpenLayers.Request[method](requestOptions); - - return resp; - }, - - /** - * Method: handleDelete - * Called the the request issued by <delete> is complete. May be overridden - * by subclasses. - * - * Parameters: - * resp - {<OpenLayers.Protocol.Response>} The response object to pass to - * any user callback. - * options - {Object} The user options passed to the delete call. - */ - handleDelete: function(resp, options) { - this.handleResponse(resp, options); - }, - - /** - * Method: handleResponse - * Called by CRUD specific handlers. - * - * Parameters: - * resp - {<OpenLayers.Protocol.Response>} The response object to pass to - * any user callback. - * options - {Object} The user options passed to the create, read, update, - * or delete call. - */ - handleResponse: function(resp, options) { - var request = resp.priv; - if(options.callback) { - if(request.status >= 200 && request.status < 300) { - // success - if(resp.requestType != "delete") { - resp.features = this.parseFeatures(request); - } - resp.code = OpenLayers.Protocol.Response.SUCCESS; - } else { - // failure - resp.code = OpenLayers.Protocol.Response.FAILURE; - } - options.callback.call(options.scope, resp); - } - }, - - /** - * Method: parseFeatures - * Read HTTP response body and return features. - * - * Parameters: - * request - {XMLHttpRequest} The request object - * - * Returns: - * {Array({<OpenLayers.Feature.Vector>})} or - * {<OpenLayers.Feature.Vector>} Array of features or a single feature. - */ - parseFeatures: function(request) { - var doc = request.responseXML; - if (!doc || !doc.documentElement) { - doc = request.responseText; - } - if (!doc || doc.length <= 0) { - return null; - } - return this.format.read(doc); - }, - - /** - * APIMethod: commit - * Iterate over each feature and take action based on the feature state. - * Possible actions are create, update and delete. - * - * Parameters: - * features - {Array({<OpenLayers.Feature.Vector>})} - * options - {Object} Optional object for setting up intermediate commit - * callbacks. - * - * Valid options: - * create - {Object} Optional object to be passed to the <create> method. - * update - {Object} Optional object to be passed to the <update> method. - * delete - {Object} Optional object to be passed to the <delete> method. - * callback - {Function} Optional function to be called when the commit - * is complete. - * scope - {Object} Optional object to be set as the scope of the callback. - * - * Returns: - * {Array(<OpenLayers.Protocol.Response>)} An array of response objects, - * one per request made to the server, each object's "priv" property - * references the corresponding HTTP request. - */ - commit: function(features, options) { - options = OpenLayers.Util.applyDefaults(options, this.options); - var resp = [], nResponses = 0; - - // Divide up features before issuing any requests. This properly - // counts requests in the event that any responses come in before - // all requests have been issued. - var types = {}; - types[OpenLayers.State.INSERT] = []; - types[OpenLayers.State.UPDATE] = []; - types[OpenLayers.State.DELETE] = []; - var feature, list, requestFeatures = []; - for(var i=0, len=features.length; i<len; ++i) { - feature = features[i]; - list = types[feature.state]; - if(list) { - list.push(feature); - requestFeatures.push(feature); - } - } - // tally up number of requests - var nRequests = (types[OpenLayers.State.INSERT].length > 0 ? 1 : 0) + - types[OpenLayers.State.UPDATE].length + - types[OpenLayers.State.DELETE].length; - - // This response will be sent to the final callback after all the others - // have been fired. - var success = true; - var finalResponse = new OpenLayers.Protocol.Response({ - reqFeatures: requestFeatures - }); - - function insertCallback(response) { - var len = response.features ? response.features.length : 0; - var fids = new Array(len); - for(var i=0; i<len; ++i) { - fids[i] = response.features[i].fid; - } - finalResponse.insertIds = fids; - callback.apply(this, [response]); - } - - function callback(response) { - this.callUserCallback(response, options); - success = success && response.success(); - nResponses++; - if (nResponses >= nRequests) { - if (options.callback) { - finalResponse.code = success ? - OpenLayers.Protocol.Response.SUCCESS : - OpenLayers.Protocol.Response.FAILURE; - options.callback.apply(options.scope, [finalResponse]); - } - } - } - - // start issuing requests - var queue = types[OpenLayers.State.INSERT]; - if(queue.length > 0) { - resp.push(this.create( - queue, OpenLayers.Util.applyDefaults( - {callback: insertCallback, scope: this}, options.create - ) - )); - } - queue = types[OpenLayers.State.UPDATE]; - for(var i=queue.length-1; i>=0; --i) { - resp.push(this.update( - queue[i], OpenLayers.Util.applyDefaults( - {callback: callback, scope: this}, options.update - )) - ); - } - queue = types[OpenLayers.State.DELETE]; - for(var i=queue.length-1; i>=0; --i) { - resp.push(this["delete"]( - queue[i], OpenLayers.Util.applyDefaults( - {callback: callback, scope: this}, options["delete"] - )) - ); - } - return resp; - }, - - /** - * APIMethod: abort - * Abort an ongoing request, the response object passed to - * this method must come from this HTTP protocol (as a result - * of a create, read, update, delete or commit operation). - * - * Parameters: - * response - {<OpenLayers.Protocol.Response>} - */ - abort: function(response) { - if (response) { - response.priv.abort(); - } - }, - - /** - * Method: callUserCallback - * This method is used from within the commit method each time an - * an HTTP response is received from the server, it is responsible - * for calling the user-supplied callbacks. - * - * Parameters: - * resp - {<OpenLayers.Protocol.Response>} - * options - {Object} The map of options passed to the commit call. - */ - callUserCallback: function(resp, options) { - var opt = options[resp.requestType]; - if(opt && opt.callback) { - opt.callback.call(opt.scope, resp); - } - }, - - CLASS_NAME: "OpenLayers.Protocol.HTTP" -}); -/* ====================================================================== - OpenLayers/Control/LayerSwitcher.js - ====================================================================== */ - -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for - * full list of contributors). Published under the 2-clause BSD license. - * See license.txt in the OpenLayers distribution or repository for the - * full text of the license. */ - -/** - * @requires OpenLayers/Control.js - * @requires OpenLayers/Lang.js - * @requires OpenLayers/Util.js - * @requires OpenLayers/Events/buttonclick.js - */ - -/** - * Class: OpenLayers.Control.LayerSwitcher - * The LayerSwitcher control displays a table of contents for the map. This - * allows the user interface to switch between BaseLasyers and to show or hide - * Overlays. By default the switcher is shown minimized on the right edge of - * the map, the user may expand it by clicking on the handle. - * - * To create the LayerSwitcher outside of the map, pass the Id of a html div - * as the first argument to the constructor. - * - * Inherits from: - * - <OpenLayers.Control> - */ -OpenLayers.Control.LayerSwitcher = OpenLayers.Class(OpenLayers.Control, { - - /** - * 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(Object)} - */ - baseLayers: null, - - - /** - * Property: dataLbl - * {DOMElement} - */ - dataLbl: null, - - /** - * Property: dataLayersDiv - * {DOMElement} - */ - dataLayersDiv: null, - - /** - * Property: dataLayers - * {Array(Object)} - */ - 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() { - - //clear out layers info and unregister their events - this.clearLayersArray("base"); - this.clearLayersArray("data"); - - this.map.events.un({ - buttonclick: this.onButtonClick, - addlayer: this.redraw, - changelayer: this.redraw, - removelayer: this.redraw, - changebaselayer: this.redraw, - scope: this - }); - this.events.unregister("buttonclick", this, this.onButtonClick); - - 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 - }); - if (this.outsideViewport) { - this.events.attachToElement(this.div); - this.events.register("buttonclick", this, this.onButtonClick); - } else { - this.map.events.register("buttonclick", this, this.onButtonClick); - } - }, - - /** - * 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: onButtonClick - * - * Parameters: - * evt - {Event} - */ - onButtonClick: function(evt) { - var button = evt.buttonElement; - if (button === this.minimizeDiv) { - this.minimizeControl(); - } else if (button === this.maximizeDiv) { - this.maximizeControl(); - } else if (button._layerSwitcher === this.id) { - if (button["for"]) { - button = document.getElementById(button["for"]); - } - if (!button.disabled) { - if (button.type == "radio") { - button.checked = true; - this.map.setBaseLayer(this.map.getLayer(button._layer)); - } else { - button.checked = !button.checked; - this.updateMap(); - } - } - } - }, - - /** - * 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) { - 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() { - if ( !this.layerStates.length || - (this.map.layers.length != this.layerStates.length) ) { - return true; - } - - 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) ) { - return true; - } - } - - return false; - }, - - /** - * 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"), - // The input shall have an id attribute so we can use - // labels to interact with them. - inputId = OpenLayers.Util.createUniqueID( - this.id + "_input_" - ); - - inputElem.id = inputId; - inputElem.name = (baseLayer) ? this.id + "_baseLayers" : layer.name; - inputElem.type = (baseLayer) ? "radio" : "checkbox"; - inputElem.value = layer.name; - inputElem.checked = checked; - inputElem.defaultChecked = checked; - inputElem.className = "olButton"; - inputElem._layer = layer.id; - inputElem._layerSwitcher = this.id; - - if (!baseLayer && !layer.inRange) { - inputElem.disabled = true; - } - - // create span - var labelSpan = document.createElement("label"); - // this isn't the DOM attribute 'for', but an arbitrary name we - // use to find the appropriate input element in <onButtonClick> - labelSpan["for"] = inputElem.id; - OpenLayers.Element.addClass(labelSpan, "labelSpan olButton"); - labelSpan._layer = layer.id; - labelSpan._layerSwitcher = this.id; - if (!baseLayer && !layer.inRange) { - labelSpan.style.color = "gray"; - } - labelSpan.innerHTML = layer.name; - labelSpan.style.verticalAlign = (baseLayer) ? "bottom" - : "baseline"; - // 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: 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) { - - // set the div's width and height to empty values, so - // the div dimensions can be controlled by CSS - this.div.style.width = ""; - 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) { - - // to minimize the control we set its div's width - // and height to 0px, we cannot just set "display" - // to "none" because it would hide the maximize - // div - 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() { - - // layers list div - this.layersDiv = document.createElement("div"); - this.layersDiv.id = this.id + "_layersDiv"; - OpenLayers.Element.addClass(this.layersDiv, "layersDiv"); - - this.baseLbl = document.createElement("div"); - this.baseLbl.innerHTML = OpenLayers.i18n("Base Layer"); - OpenLayers.Element.addClass(this.baseLbl, "baseLbl"); - - this.baseLayersDiv = document.createElement("div"); - OpenLayers.Element.addClass(this.baseLayersDiv, "baseLayersDiv"); - - this.dataLbl = document.createElement("div"); - this.dataLbl.innerHTML = OpenLayers.i18n("Overlays"); - OpenLayers.Element.addClass(this.dataLbl, "dataLbl"); - - this.dataLayersDiv = document.createElement("div"); - OpenLayers.Element.addClass(this.dataLayersDiv, "dataLayersDiv"); - - 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); - - // maximize button div - var img = OpenLayers.Util.getImageLocation('layer-switcher-maximize.png'); - this.maximizeDiv = OpenLayers.Util.createAlphaImageDiv( - "OpenLayers_Control_MaximizeDiv", - null, - null, - img, - "absolute"); - OpenLayers.Element.addClass(this.maximizeDiv, "maximizeDiv olButton"); - this.maximizeDiv.style.display = "none"; - - this.div.appendChild(this.maximizeDiv); - - // minimize button div - var img = OpenLayers.Util.getImageLocation('layer-switcher-minimize.png'); - this.minimizeDiv = OpenLayers.Util.createAlphaImageDiv( - "OpenLayers_Control_MinimizeDiv", - null, - null, - img, - "absolute"); - OpenLayers.Element.addClass(this.minimizeDiv, "minimizeDiv olButton"); - this.minimizeDiv.style.display = "none"; - - this.div.appendChild(this.minimizeDiv); - }, - - CLASS_NAME: "OpenLayers.Control.LayerSwitcher" -}); |