diff options
author | Chris Schlaeger <chris@linux.com> | 2015-10-17 21:36:38 +0200 |
---|---|---|
committer | Chris Schlaeger <chris@linux.com> | 2015-10-17 21:36:38 +0200 |
commit | e30f267181d990947e67909de4809fa941698c85 (patch) | |
tree | 46e9f94c2b3699ed378963b420b8a8d361286ea1 /misc/openlayers/OpenLayers.mobile.debug.js | |
parent | e763ceb183f389fcd314a4a6a712d87c9d4cdb32 (diff) | |
download | postrunner-e30f267181d990947e67909de4809fa941698c85.zip |
Upgrading openlayers to 3.x
Diffstat (limited to 'misc/openlayers/OpenLayers.mobile.debug.js')
-rw-r--r-- | misc/openlayers/OpenLayers.mobile.debug.js | 41831 |
1 files changed, 0 insertions, 41831 deletions
diff --git a/misc/openlayers/OpenLayers.mobile.debug.js b/misc/openlayers/OpenLayers.mobile.debug.js deleted file mode 100644 index 5cb37cd..0000000 --- a/misc/openlayers/OpenLayers.mobile.debug.js +++ /dev/null @@ -1,41831 +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/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/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/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/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/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/Canvas.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.Renderer.Canvas - * A renderer based on the 2D 'canvas' drawing element. - * - * Inherits: - * - <OpenLayers.Renderer> - */ -OpenLayers.Renderer.Canvas = OpenLayers.Class(OpenLayers.Renderer, { - - /** - * APIProperty: hitDetection - * {Boolean} Allow for hit detection of features. Default is true. - */ - hitDetection: true, - - /** - * Property: hitOverflow - * {Number} The method for converting feature identifiers to color values - * supports 16777215 sequential values. Two features cannot be - * predictably detected if their identifiers differ by more than this - * value. The hitOverflow allows for bigger numbers (but the - * difference in values is still limited). - */ - hitOverflow: 0, - - /** - * Property: canvas - * {Canvas} The canvas context object. - */ - canvas: null, - - /** - * Property: features - * {Object} Internal object of feature/style pairs for use in redrawing the layer. - */ - features: null, - - /** - * Property: pendingRedraw - * {Boolean} The renderer needs a redraw call to render features added while - * the renderer was locked. - */ - pendingRedraw: false, - - /** - * Property: cachedSymbolBounds - * {Object} Internal cache of calculated symbol extents. - */ - cachedSymbolBounds: {}, - - /** - * Constructor: OpenLayers.Renderer.Canvas - * - * Parameters: - * containerID - {<String>} - * options - {Object} Optional properties to be set on the renderer. - */ - initialize: function(containerID, options) { - OpenLayers.Renderer.prototype.initialize.apply(this, arguments); - this.root = document.createElement("canvas"); - this.container.appendChild(this.root); - this.canvas = this.root.getContext("2d"); - this.features = {}; - if (this.hitDetection) { - this.hitCanvas = document.createElement("canvas"); - this.hitContext = this.hitCanvas.getContext("2d"); - } - }, - - /** - * 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() { - OpenLayers.Renderer.prototype.setExtent.apply(this, arguments); - // always redraw features - return false; - }, - - /** - * Method: eraseGeometry - * Erase a geometry from the renderer. Because the Canvas renderer has - * 'memory' of the features that it has drawn, we have to remove the - * feature so it doesn't redraw. - * - * Parameters: - * geometry - {<OpenLayers.Geometry>} - * featureId - {String} - */ - eraseGeometry: function(geometry, featureId) { - this.eraseFeatures(this.features[featureId][0]); - }, - - /** - * APIMethod: supported - * - * Returns: - * {Boolean} Whether or not the browser supports the renderer class - */ - supported: function() { - return OpenLayers.CANVAS_SUPPORTED; - }, - - /** - * Method: setSize - * Sets the size of the drawing surface. - * - * Once the size is updated, redraw the canvas. - * - * Parameters: - * size - {<OpenLayers.Size>} - */ - setSize: function(size) { - this.size = size.clone(); - var root = this.root; - root.style.width = size.w + "px"; - root.style.height = size.h + "px"; - root.width = size.w; - root.height = size.h; - this.resolution = null; - if (this.hitDetection) { - var hitCanvas = this.hitCanvas; - hitCanvas.style.width = size.w + "px"; - hitCanvas.style.height = size.h + "px"; - hitCanvas.width = size.w; - hitCanvas.height = size.h; - } - }, - - /** - * Method: drawFeature - * Draw the feature. Stores the feature in the features list, - * then redraws the layer. - * - * Parameters: - * feature - {<OpenLayers.Feature.Vector>} - * style - {<Object>} - * - * Returns: - * {Boolean} The feature has been drawn completely. If the feature has no - * geometry, undefined will be returned. If the feature is not rendered - * for other reasons, false will be returned. - */ - drawFeature: function(feature, style) { - var rendered; - if (feature.geometry) { - style = this.applyDefaultSymbolizer(style || feature.style); - // don't render if display none or feature outside extent - var bounds = feature.geometry.getBounds(); - - var worldBounds; - if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) { - worldBounds = this.map.getMaxExtent(); - } - - var intersects = bounds && bounds.intersectsBounds(this.extent, {worldBounds: worldBounds}); - - rendered = (style.display !== "none") && !!bounds && intersects; - if (rendered) { - // keep track of what we have rendered for redraw - this.features[feature.id] = [feature, style]; - } - else { - // remove from features tracked for redraw - delete(this.features[feature.id]); - } - this.pendingRedraw = true; - } - if (this.pendingRedraw && !this.locked) { - this.redraw(); - this.pendingRedraw = false; - } - return rendered; - }, - - /** - * Method: drawGeometry - * Used when looping (in redraw) over the features; draws - * the canvas. - * - * Parameters: - * geometry - {<OpenLayers.Geometry>} - * style - {Object} - */ - drawGeometry: function(geometry, style, featureId) { - var className = geometry.CLASS_NAME; - if ((className == "OpenLayers.Geometry.Collection") || - (className == "OpenLayers.Geometry.MultiPoint") || - (className == "OpenLayers.Geometry.MultiLineString") || - (className == "OpenLayers.Geometry.MultiPolygon")) { - for (var i = 0; i < geometry.components.length; i++) { - this.drawGeometry(geometry.components[i], style, featureId); - } - return; - } - switch (geometry.CLASS_NAME) { - case "OpenLayers.Geometry.Point": - this.drawPoint(geometry, style, featureId); - break; - case "OpenLayers.Geometry.LineString": - this.drawLineString(geometry, style, featureId); - break; - case "OpenLayers.Geometry.LinearRing": - this.drawLinearRing(geometry, style, featureId); - break; - case "OpenLayers.Geometry.Polygon": - this.drawPolygon(geometry, style, featureId); - break; - default: - break; - } - }, - - /** - * Method: drawExternalGraphic - * Called to draw External graphics. - * - * Parameters: - * geometry - {<OpenLayers.Geometry>} - * style - {Object} - * featureId - {String} - */ - drawExternalGraphic: function(geometry, style, featureId) { - var img = new Image(); - - var title = style.title || style.graphicTitle; - if (title) { - img.title = title; - } - - var width = style.graphicWidth || style.graphicHeight; - var height = style.graphicHeight || style.graphicWidth; - width = width ? width : style.pointRadius * 2; - height = height ? height : style.pointRadius * 2; - var xOffset = (style.graphicXOffset != undefined) ? - style.graphicXOffset : -(0.5 * width); - var yOffset = (style.graphicYOffset != undefined) ? - style.graphicYOffset : -(0.5 * height); - - var opacity = style.graphicOpacity || style.fillOpacity; - - var onLoad = function() { - if(!this.features[featureId]) { - return; - } - var pt = this.getLocalXY(geometry); - var p0 = pt[0]; - var p1 = pt[1]; - if(!isNaN(p0) && !isNaN(p1)) { - var x = (p0 + xOffset) | 0; - var y = (p1 + yOffset) | 0; - var canvas = this.canvas; - canvas.globalAlpha = opacity; - var factor = OpenLayers.Renderer.Canvas.drawImageScaleFactor || - (OpenLayers.Renderer.Canvas.drawImageScaleFactor = - /android 2.1/.test(navigator.userAgent.toLowerCase()) ? - // 320 is the screen width of the G1 phone, for - // which drawImage works out of the box. - 320 / window.screen.width : 1 - ); - canvas.drawImage( - img, x*factor, y*factor, width*factor, height*factor - ); - if (this.hitDetection) { - this.setHitContextStyle("fill", featureId); - this.hitContext.fillRect(x, y, width, height); - } - } - }; - - img.onload = OpenLayers.Function.bind(onLoad, this); - img.src = style.externalGraphic; - }, - - /** - * Method: drawNamedSymbol - * Called to draw Well Known Graphic Symbol Name. - * This method is only called by the renderer itself. - * - * Parameters: - * geometry - {<OpenLayers.Geometry>} - * style - {Object} - * featureId - {String} - */ - drawNamedSymbol: function(geometry, style, featureId) { - var x, y, cx, cy, i, symbolBounds, scaling, angle; - var unscaledStrokeWidth; - var deg2rad = Math.PI / 180.0; - - var symbol = OpenLayers.Renderer.symbol[style.graphicName]; - - if (!symbol) { - throw new Error(style.graphicName + ' is not a valid symbol name'); - } - - if (!symbol.length || symbol.length < 2) return; - - var pt = this.getLocalXY(geometry); - var p0 = pt[0]; - var p1 = pt[1]; - - if (isNaN(p0) || isNaN(p1)) return; - - // Use rounded line caps - this.canvas.lineCap = "round"; - this.canvas.lineJoin = "round"; - - if (this.hitDetection) { - this.hitContext.lineCap = "round"; - this.hitContext.lineJoin = "round"; - } - - // Scale and rotate symbols, using precalculated bounds whenever possible. - if (style.graphicName in this.cachedSymbolBounds) { - symbolBounds = this.cachedSymbolBounds[style.graphicName]; - } else { - symbolBounds = new OpenLayers.Bounds(); - for(i = 0; i < symbol.length; i+=2) { - symbolBounds.extend(new OpenLayers.LonLat(symbol[i], symbol[i+1])); - } - this.cachedSymbolBounds[style.graphicName] = symbolBounds; - } - - // Push symbol scaling, translation and rotation onto the transformation stack in reverse order. - // Don't forget to apply all canvas transformations to the hitContext canvas as well(!) - this.canvas.save(); - if (this.hitDetection) { this.hitContext.save(); } - - // Step 3: place symbol at the desired location - this.canvas.translate(p0,p1); - if (this.hitDetection) { this.hitContext.translate(p0,p1); } - - // Step 2a. rotate the symbol if necessary - angle = deg2rad * style.rotation; // will be NaN when style.rotation is undefined. - if (!isNaN(angle)) { - this.canvas.rotate(angle); - if (this.hitDetection) { this.hitContext.rotate(angle); } - } - - // // Step 2: scale symbol such that pointRadius equals half the maximum symbol dimension. - scaling = 2.0 * style.pointRadius / Math.max(symbolBounds.getWidth(), symbolBounds.getHeight()); - this.canvas.scale(scaling,scaling); - if (this.hitDetection) { this.hitContext.scale(scaling,scaling); } - - // Step 1: center the symbol at the origin - cx = symbolBounds.getCenterLonLat().lon; - cy = symbolBounds.getCenterLonLat().lat; - this.canvas.translate(-cx,-cy); - if (this.hitDetection) { this.hitContext.translate(-cx,-cy); } - - // Don't forget to scale stroke widths, because they are affected by canvas scale transformations as well(!) - // Alternative: scale symbol coordinates manually, so stroke width scaling is not needed anymore. - unscaledStrokeWidth = style.strokeWidth; - style.strokeWidth = unscaledStrokeWidth / scaling; - - if (style.fill !== false) { - this.setCanvasStyle("fill", style); - this.canvas.beginPath(); - for (i=0; i<symbol.length; i=i+2) { - x = symbol[i]; - y = symbol[i+1]; - if (i == 0) this.canvas.moveTo(x,y); - this.canvas.lineTo(x,y); - } - this.canvas.closePath(); - this.canvas.fill(); - - if (this.hitDetection) { - this.setHitContextStyle("fill", featureId, style); - this.hitContext.beginPath(); - for (i=0; i<symbol.length; i=i+2) { - x = symbol[i]; - y = symbol[i+1]; - if (i == 0) this.canvas.moveTo(x,y); - this.hitContext.lineTo(x,y); - } - this.hitContext.closePath(); - this.hitContext.fill(); - } - } - - if (style.stroke !== false) { - this.setCanvasStyle("stroke", style); - this.canvas.beginPath(); - for (i=0; i<symbol.length; i=i+2) { - x = symbol[i]; - y = symbol[i+1]; - if (i == 0) this.canvas.moveTo(x,y); - this.canvas.lineTo(x,y); - } - this.canvas.closePath(); - this.canvas.stroke(); - - - if (this.hitDetection) { - this.setHitContextStyle("stroke", featureId, style, scaling); - this.hitContext.beginPath(); - for (i=0; i<symbol.length; i=i+2) { - x = symbol[i]; - y = symbol[i+1]; - if (i == 0) this.hitContext.moveTo(x,y); - this.hitContext.lineTo(x,y); - } - this.hitContext.closePath(); - this.hitContext.stroke(); - } - - } - - style.strokeWidth = unscaledStrokeWidth; - this.canvas.restore(); - if (this.hitDetection) { this.hitContext.restore(); } - this.setCanvasStyle("reset"); - }, - - /** - * Method: setCanvasStyle - * Prepare the canvas for drawing by setting various global settings. - * - * Parameters: - * type - {String} one of 'stroke', 'fill', or 'reset' - * style - {Object} Symbolizer hash - */ - setCanvasStyle: function(type, style) { - if (type === "fill") { - this.canvas.globalAlpha = style['fillOpacity']; - this.canvas.fillStyle = style['fillColor']; - } else if (type === "stroke") { - this.canvas.globalAlpha = style['strokeOpacity']; - this.canvas.strokeStyle = style['strokeColor']; - this.canvas.lineWidth = style['strokeWidth']; - } else { - this.canvas.globalAlpha = 0; - this.canvas.lineWidth = 1; - } - }, - - /** - * Method: featureIdToHex - * Convert a feature ID string into an RGB hex string. - * - * Parameters: - * featureId - {String} Feature id - * - * Returns: - * {String} RGB hex string. - */ - featureIdToHex: function(featureId) { - var id = Number(featureId.split("_").pop()) + 1; // zero for no feature - if (id >= 16777216) { - this.hitOverflow = id - 16777215; - id = id % 16777216 + 1; - } - var hex = "000000" + id.toString(16); - var len = hex.length; - hex = "#" + hex.substring(len-6, len); - return hex; - }, - - /** - * Method: setHitContextStyle - * Prepare the hit canvas for drawing by setting various global settings. - * - * Parameters: - * type - {String} one of 'stroke', 'fill', or 'reset' - * featureId - {String} The feature id. - * symbolizer - {<OpenLayers.Symbolizer>} The symbolizer. - */ - setHitContextStyle: function(type, featureId, symbolizer, strokeScaling) { - var hex = this.featureIdToHex(featureId); - if (type == "fill") { - this.hitContext.globalAlpha = 1.0; - this.hitContext.fillStyle = hex; - } else if (type == "stroke") { - this.hitContext.globalAlpha = 1.0; - this.hitContext.strokeStyle = hex; - // bump up stroke width to deal with antialiasing. If strokeScaling is defined, we're rendering a symbol - // on a transformed canvas, so the antialias width bump has to scale as well. - if (typeof strokeScaling === "undefined") { - this.hitContext.lineWidth = symbolizer.strokeWidth + 2; - } else { - if (!isNaN(strokeScaling)) { this.hitContext.lineWidth = symbolizer.strokeWidth + 2.0 / strokeScaling; } - } - } else { - this.hitContext.globalAlpha = 0; - this.hitContext.lineWidth = 1; - } - }, - - /** - * Method: drawPoint - * This method is only called by the renderer itself. - * - * Parameters: - * geometry - {<OpenLayers.Geometry>} - * style - {Object} - * featureId - {String} - */ - drawPoint: function(geometry, style, featureId) { - if(style.graphic !== false) { - if(style.externalGraphic) { - this.drawExternalGraphic(geometry, style, featureId); - } else if (style.graphicName && (style.graphicName != "circle")) { - this.drawNamedSymbol(geometry, style, featureId); - } else { - var pt = this.getLocalXY(geometry); - var p0 = pt[0]; - var p1 = pt[1]; - if(!isNaN(p0) && !isNaN(p1)) { - var twoPi = Math.PI*2; - var radius = style.pointRadius; - if(style.fill !== false) { - this.setCanvasStyle("fill", style); - this.canvas.beginPath(); - this.canvas.arc(p0, p1, radius, 0, twoPi, true); - this.canvas.fill(); - if (this.hitDetection) { - this.setHitContextStyle("fill", featureId, style); - this.hitContext.beginPath(); - this.hitContext.arc(p0, p1, radius, 0, twoPi, true); - this.hitContext.fill(); - } - } - - if(style.stroke !== false) { - this.setCanvasStyle("stroke", style); - this.canvas.beginPath(); - this.canvas.arc(p0, p1, radius, 0, twoPi, true); - this.canvas.stroke(); - if (this.hitDetection) { - this.setHitContextStyle("stroke", featureId, style); - this.hitContext.beginPath(); - this.hitContext.arc(p0, p1, radius, 0, twoPi, true); - this.hitContext.stroke(); - } - this.setCanvasStyle("reset"); - } - } - } - } - }, - - /** - * Method: drawLineString - * This method is only called by the renderer itself. - * - * Parameters: - * geometry - {<OpenLayers.Geometry>} - * style - {Object} - * featureId - {String} - */ - drawLineString: function(geometry, style, featureId) { - style = OpenLayers.Util.applyDefaults({fill: false}, style); - this.drawLinearRing(geometry, style, featureId); - }, - - /** - * Method: drawLinearRing - * This method is only called by the renderer itself. - * - * Parameters: - * geometry - {<OpenLayers.Geometry>} - * style - {Object} - * featureId - {String} - */ - drawLinearRing: function(geometry, style, featureId) { - if (style.fill !== false) { - this.setCanvasStyle("fill", style); - this.renderPath(this.canvas, geometry, style, featureId, "fill"); - if (this.hitDetection) { - this.setHitContextStyle("fill", featureId, style); - this.renderPath(this.hitContext, geometry, style, featureId, "fill"); - } - } - if (style.stroke !== false) { - this.setCanvasStyle("stroke", style); - this.renderPath(this.canvas, geometry, style, featureId, "stroke"); - if (this.hitDetection) { - this.setHitContextStyle("stroke", featureId, style); - this.renderPath(this.hitContext, geometry, style, featureId, "stroke"); - } - } - this.setCanvasStyle("reset"); - }, - - /** - * Method: renderPath - * Render a path with stroke and optional fill. - */ - renderPath: function(context, geometry, style, featureId, type) { - var components = geometry.components; - var len = components.length; - context.beginPath(); - var start = this.getLocalXY(components[0]); - var x = start[0]; - var y = start[1]; - if (!isNaN(x) && !isNaN(y)) { - context.moveTo(start[0], start[1]); - for (var i=1; i<len; ++i) { - var pt = this.getLocalXY(components[i]); - context.lineTo(pt[0], pt[1]); - } - if (type === "fill") { - context.fill(); - } else { - context.stroke(); - } - } - }, - - /** - * Method: drawPolygon - * This method is only called by the renderer itself. - * - * Parameters: - * geometry - {<OpenLayers.Geometry>} - * style - {Object} - * featureId - {String} - */ - drawPolygon: function(geometry, style, featureId) { - var components = geometry.components; - var len = components.length; - this.drawLinearRing(components[0], style, featureId); - // erase inner rings - for (var i=1; i<len; ++i) { - /** - * Note that this is overly agressive. Here we punch holes through - * all previously rendered features on the same canvas. A better - * solution for polygons with interior rings would be to draw the - * polygon on a sketch canvas first. We could erase all holes - * there and then copy the drawing to the layer canvas. - * TODO: http://trac.osgeo.org/openlayers/ticket/3130 - */ - this.canvas.globalCompositeOperation = "destination-out"; - if (this.hitDetection) { - this.hitContext.globalCompositeOperation = "destination-out"; - } - this.drawLinearRing( - components[i], - OpenLayers.Util.applyDefaults({stroke: false, fillOpacity: 1.0}, style), - featureId - ); - this.canvas.globalCompositeOperation = "source-over"; - if (this.hitDetection) { - this.hitContext.globalCompositeOperation = "source-over"; - } - this.drawLinearRing( - components[i], - OpenLayers.Util.applyDefaults({fill: false}, style), - featureId - ); - } - }, - - /** - * Method: drawText - * This method is only called by the renderer itself. - * - * Parameters: - * location - {<OpenLayers.Point>} - * style - {Object} - */ - drawText: function(location, style) { - var pt = this.getLocalXY(location); - - this.setCanvasStyle("reset"); - this.canvas.fillStyle = style.fontColor; - this.canvas.globalAlpha = style.fontOpacity || 1.0; - var fontStyle = [style.fontStyle ? style.fontStyle : "normal", - "normal", // "font-variant" not supported - style.fontWeight ? style.fontWeight : "normal", - style.fontSize ? style.fontSize : "1em", - style.fontFamily ? style.fontFamily : "sans-serif"].join(" "); - var labelRows = style.label.split('\n'); - var numRows = labelRows.length; - if (this.canvas.fillText) { - // HTML5 - this.canvas.font = fontStyle; - this.canvas.textAlign = - OpenLayers.Renderer.Canvas.LABEL_ALIGN[style.labelAlign[0]] || - "center"; - this.canvas.textBaseline = - OpenLayers.Renderer.Canvas.LABEL_ALIGN[style.labelAlign[1]] || - "middle"; - var vfactor = - OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[1]]; - if (vfactor == null) { - vfactor = -.5; - } - var lineHeight = - this.canvas.measureText('Mg').height || - this.canvas.measureText('xx').width; - pt[1] += lineHeight*vfactor*(numRows-1); - for (var i = 0; i < numRows; i++) { - if (style.labelOutlineWidth) { - this.canvas.save(); - this.canvas.globalAlpha = style.labelOutlineOpacity || style.fontOpacity || 1.0; - this.canvas.strokeStyle = style.labelOutlineColor; - this.canvas.lineWidth = style.labelOutlineWidth; - this.canvas.strokeText(labelRows[i], pt[0], pt[1] + (lineHeight*i) + 1); - this.canvas.restore(); - } - this.canvas.fillText(labelRows[i], pt[0], pt[1] + (lineHeight*i)); - } - } else if (this.canvas.mozDrawText) { - // Mozilla pre-Gecko1.9.1 (<FF3.1) - this.canvas.mozTextStyle = fontStyle; - // No built-in text alignment, so we measure and adjust the position - var hfactor = - OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[0]]; - if (hfactor == null) { - hfactor = -.5; - } - var vfactor = - OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[1]]; - if (vfactor == null) { - vfactor = -.5; - } - var lineHeight = this.canvas.mozMeasureText('xx'); - pt[1] += lineHeight*(1 + (vfactor*numRows)); - for (var i = 0; i < numRows; i++) { - var x = pt[0] + (hfactor*this.canvas.mozMeasureText(labelRows[i])); - var y = pt[1] + (i*lineHeight); - this.canvas.translate(x, y); - this.canvas.mozDrawText(labelRows[i]); - this.canvas.translate(-x, -y); - } - } - this.setCanvasStyle("reset"); - }, - - /** - * Method: getLocalXY - * transform geographic xy into pixel xy - * - * Parameters: - * point - {<OpenLayers.Geometry.Point>} - */ - getLocalXY: function(point) { - var resolution = this.getResolution(); - var extent = this.extent; - var x = ((point.x - this.featureDx) / resolution + (-extent.left / resolution)); - var y = ((extent.top / resolution) - point.y / resolution); - return [x, y]; - }, - - /** - * Method: clear - * Clear all vectors from the renderer. - */ - clear: function() { - var height = this.root.height; - var width = this.root.width; - this.canvas.clearRect(0, 0, width, height); - this.features = {}; - if (this.hitDetection) { - this.hitContext.clearRect(0, 0, width, height); - } - }, - - /** - * Method: getFeatureIdFromEvent - * Returns a feature id from an event on the renderer. - * - * Parameters: - * evt - {<OpenLayers.Event>} - * - * Returns: - * {<OpenLayers.Feature.Vector} A feature or undefined. This method returns a - * feature instead of a feature id to avoid an unnecessary lookup on the - * layer. - */ - getFeatureIdFromEvent: function(evt) { - var featureId, feature; - - if (this.hitDetection && this.root.style.display !== "none") { - // this dragging check should go in the feature handler - if (!this.map.dragging) { - var xy = evt.xy; - var x = xy.x | 0; - var y = xy.y | 0; - var data = this.hitContext.getImageData(x, y, 1, 1).data; - if (data[3] === 255) { // antialiased - var id = data[2] + (256 * (data[1] + (256 * data[0]))); - if (id) { - featureId = "OpenLayers_Feature_Vector_" + (id - 1 + this.hitOverflow); - try { - feature = this.features[featureId][0]; - } catch(err) { - // Because of antialiasing on the canvas, when the hit location is at a point where the edge of - // one symbol intersects the interior of another symbol, a wrong hit color (and therefore id) results. - // todo: set Antialiasing = 'off' on the hitContext as soon as browsers allow it. - } - } - } - } - } - return feature; - }, - - /** - * Method: eraseFeatures - * This is called by the layer to erase features; removes the feature from - * the list, then redraws the layer. - * - * Parameters: - * features - {Array(<OpenLayers.Feature.Vector>)} - */ - eraseFeatures: function(features) { - if(!(OpenLayers.Util.isArray(features))) { - features = [features]; - } - for(var i=0; i<features.length; ++i) { - delete this.features[features[i].id]; - } - this.redraw(); - }, - - /** - * Method: redraw - * The real 'meat' of the function: any time things have changed, - * redraw() can be called to loop over all the data and (you guessed - * it) redraw it. Unlike Elements-based Renderers, we can't interact - * with things once they're drawn, to remove them, for example, so - * instead we have to just clear everything and draw from scratch. - */ - redraw: function() { - if (!this.locked) { - var height = this.root.height; - var width = this.root.width; - this.canvas.clearRect(0, 0, width, height); - if (this.hitDetection) { - this.hitContext.clearRect(0, 0, width, height); - } - var labelMap = []; - var feature, geometry, style; - var worldBounds = (this.map.baseLayer && this.map.baseLayer.wrapDateLine) && this.map.getMaxExtent(); - for (var id in this.features) { - if (!this.features.hasOwnProperty(id)) { continue; } - feature = this.features[id][0]; - geometry = feature.geometry; - this.calculateFeatureDx(geometry.getBounds(), worldBounds); - style = this.features[id][1]; - this.drawGeometry(geometry, style, feature.id); - if(style.label) { - labelMap.push([feature, style]); - } - } - var item; - for (var i=0, len=labelMap.length; i<len; ++i) { - item = labelMap[i]; - this.drawText(item[0].geometry.getCentroid(), item[1]); - } - } - }, - - CLASS_NAME: "OpenLayers.Renderer.Canvas" -}); - -/** - * Constant: OpenLayers.Renderer.Canvas.LABEL_ALIGN - * {Object} - */ -OpenLayers.Renderer.Canvas.LABEL_ALIGN = { - "l": "left", - "r": "right", - "t": "top", - "b": "bottom" -}; - -/** - * Constant: OpenLayers.Renderer.Canvas.LABEL_FACTOR - * {Object} - */ -OpenLayers.Renderer.Canvas.LABEL_FACTOR = { - "l": 0, - "r": -1, - "t": 0, - "b": -1 -}; - -/** - * Constant: OpenLayers.Renderer.Canvas.drawImageScaleFactor - * {Number} Scale factor to apply to the canvas drawImage arguments. This - * is always 1 except for Android 2.1 devices, to work around - * http://code.google.com/p/android/issues/detail?id=5141. - */ -OpenLayers.Renderer.Canvas.drawImageScaleFactor = null; -/* ====================================================================== - 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/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/Keyboard.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/Events.js - */ - -/** - * Class: OpenLayers.handler.Keyboard - * A handler for keyboard events. Create a new instance with the - * <OpenLayers.Handler.Keyboard> constructor. - * - * Inherits from: - * - <OpenLayers.Handler> - */ -OpenLayers.Handler.Keyboard = OpenLayers.Class(OpenLayers.Handler, { - - /* http://www.quirksmode.org/js/keys.html explains key x-browser - key handling quirks in pretty nice detail */ - - /** - * Constant: KEY_EVENTS - * keydown, keypress, keyup - */ - KEY_EVENTS: ["keydown", "keyup"], - - /** - * Property: eventListener - * {Function} - */ - eventListener: null, - - /** - * Property: observeElement - * {DOMElement|String} The DOM element on which we listen for - * key events. Default to the document. - */ - observeElement: null, - - /** - * Constructor: OpenLayers.Handler.Keyboard - * Returns a new keyboard handler. - * - * Parameters: - * control - {<OpenLayers.Control>} The control that is making use of - * this handler. If a handler is being used without a control, the - * handlers setMap method must be overridden to deal properly with - * the map. - * callbacks - {Object} An object containing a single function to be - * called when the drag operation is finished. The callback should - * expect to recieve a single argument, the pixel location of the event. - * Callbacks for 'keydown', 'keypress', and 'keyup' are supported. - * options - {Object} Optional object whose properties will be set on the - * handler. - */ - initialize: function(control, callbacks, options) { - OpenLayers.Handler.prototype.initialize.apply(this, arguments); - // cache the bound event listener method so it can be unobserved later - this.eventListener = OpenLayers.Function.bindAsEventListener( - this.handleKeyEvent, this - ); - }, - - /** - * Method: destroy - */ - destroy: function() { - this.deactivate(); - this.eventListener = null; - OpenLayers.Handler.prototype.destroy.apply(this, arguments); - }, - - /** - * Method: activate - */ - activate: function() { - if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) { - this.observeElement = this.observeElement || document; - for (var i=0, len=this.KEY_EVENTS.length; i<len; i++) { - OpenLayers.Event.observe( - this.observeElement, this.KEY_EVENTS[i], this.eventListener); - } - return true; - } else { - return false; - } - }, - - /** - * Method: deactivate - */ - deactivate: function() { - var deactivated = false; - if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) { - for (var i=0, len=this.KEY_EVENTS.length; i<len; i++) { - OpenLayers.Event.stopObserving( - this.observeElement, this.KEY_EVENTS[i], this.eventListener); - } - deactivated = true; - } - return deactivated; - }, - - /** - * Method: handleKeyEvent - */ - handleKeyEvent: function (evt) { - if (this.checkModifiers(evt)) { - this.callback(evt.type, [evt]); - } - }, - - CLASS_NAME: "OpenLayers.Handler.Keyboard" -}); -/* ====================================================================== - OpenLayers/Control/ModifyFeature.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 - * @requires OpenLayers/Handler/Keyboard.js - */ - -/** - * Class: OpenLayers.Control.ModifyFeature - * Control to modify features. When activated, a click renders the vertices - * of a feature - these vertices can then be dragged. By default, the - * delete key will delete the vertex under the mouse. New features are - * added by dragging "virtual vertices" between vertices. Create a new - * control with the <OpenLayers.Control.ModifyFeature> constructor. - * - * Inherits From: - * - <OpenLayers.Control> - */ -OpenLayers.Control.ModifyFeature = OpenLayers.Class(OpenLayers.Control, { - - /** - * APIProperty: documentDrag - * {Boolean} If set to true, dragging vertices will continue even if the - * mouse cursor leaves the map viewport. Default is false. - */ - documentDrag: false, - - /** - * APIProperty: geometryTypes - * {Array(String)} To restrict modification to a limited set of geometry - * types, send a list of strings corresponding to the geometry class - * names. - */ - geometryTypes: null, - - /** - * APIProperty: clickout - * {Boolean} Unselect features when clicking outside any feature. - * Default is true. - */ - clickout: true, - - /** - * APIProperty: toggle - * {Boolean} Unselect a selected feature on click. - * Default is true. - */ - toggle: true, - - /** - * APIProperty: standalone - * {Boolean} Set to true to create a control without SelectFeature - * capabilities. Default is false. If standalone is true, to modify - * a feature, call the <selectFeature> method with the target feature. - * Note that you must call the <unselectFeature> method to finish - * feature modification in standalone mode (before starting to modify - * another feature). - */ - standalone: false, - - /** - * Property: layer - * {<OpenLayers.Layer.Vector>} - */ - layer: null, - - /** - * Property: feature - * {<OpenLayers.Feature.Vector>} Feature currently available for modification. - */ - feature: null, - - /** - * Property: vertex - * {<OpenLayers.Feature.Vector>} Vertex currently being modified. - */ - vertex: null, - - /** - * Property: vertices - * {Array(<OpenLayers.Feature.Vector>)} Verticies currently available - * for dragging. - */ - vertices: null, - - /** - * Property: virtualVertices - * {Array(<OpenLayers.Feature.Vector>)} Virtual vertices in the middle - * of each edge. - */ - virtualVertices: null, - - /** - * Property: handlers - * {Object} - */ - handlers: null, - - /** - * APIProperty: deleteCodes - * {Array(Integer)} Keycodes for deleting verticies. Set to null to disable - * vertex deltion by keypress. If non-null, keypresses with codes - * in this array will delete vertices under the mouse. Default - * is 46 and 68, the 'delete' and lowercase 'd' keys. - */ - deleteCodes: null, - - /** - * APIProperty: virtualStyle - * {Object} A symbolizer to be used for virtual vertices. - */ - virtualStyle: null, - - /** - * APIProperty: vertexRenderIntent - * {String} The renderIntent to use for vertices. If no <virtualStyle> is - * provided, this renderIntent will also be used for virtual vertices, with - * a fillOpacity and strokeOpacity of 0.3. Default is null, which means - * that the layer's default style will be used for vertices. - */ - vertexRenderIntent: null, - - /** - * APIProperty: mode - * {Integer} Bitfields specifying the modification mode. Defaults to - * OpenLayers.Control.ModifyFeature.RESHAPE. To set the mode to a - * combination of options, use the | operator. For example, to allow - * the control to both resize and rotate features, use the following - * syntax - * (code) - * control.mode = OpenLayers.Control.ModifyFeature.RESIZE | - * OpenLayers.Control.ModifyFeature.ROTATE; - * (end) - */ - mode: null, - - /** - * APIProperty: createVertices - * {Boolean} Create new vertices by dragging the virtual vertices - * in the middle of each edge. Default is true. - */ - createVertices: true, - - /** - * Property: modified - * {Boolean} The currently selected feature has been modified. - */ - modified: false, - - /** - * Property: radiusHandle - * {<OpenLayers.Feature.Vector>} A handle for rotating/resizing a feature. - */ - radiusHandle: null, - - /** - * Property: dragHandle - * {<OpenLayers.Feature.Vector>} A handle for dragging a feature. - */ - dragHandle: null, - - /** - * APIProperty: onModificationStart - * {Function} *Deprecated*. Register for "beforefeaturemodified" instead. - * The "beforefeaturemodified" event is triggered on the layer before - * any modification begins. - * - * Optional function to be called when a feature is selected - * to be modified. The function should expect to be called with a - * feature. This could be used for example to allow to lock the - * feature on server-side. - */ - onModificationStart: function() {}, - - /** - * APIProperty: onModification - * {Function} *Deprecated*. Register for "featuremodified" instead. - * The "featuremodified" event is triggered on the layer with each - * feature modification. - * - * Optional function to be called when a feature has been - * modified. The function should expect to be called with a feature. - */ - onModification: function() {}, - - /** - * APIProperty: onModificationEnd - * {Function} *Deprecated*. Register for "afterfeaturemodified" instead. - * The "afterfeaturemodified" event is triggered on the layer after - * a feature has been modified. - * - * Optional function to be called when a feature is finished - * being modified. The function should expect to be called with a - * feature. - */ - onModificationEnd: function() {}, - - /** - * Constructor: OpenLayers.Control.ModifyFeature - * Create a new modify feature control. - * - * Parameters: - * layer - {<OpenLayers.Layer.Vector>} Layer that contains features that - * will be modified. - * options - {Object} Optional object whose properties will be set on the - * control. - */ - initialize: function(layer, options) { - options = options || {}; - this.layer = layer; - this.vertices = []; - this.virtualVertices = []; - this.virtualStyle = OpenLayers.Util.extend({}, - this.layer.style || - this.layer.styleMap.createSymbolizer(null, options.vertexRenderIntent) - ); - this.virtualStyle.fillOpacity = 0.3; - this.virtualStyle.strokeOpacity = 0.3; - this.deleteCodes = [46, 68]; - this.mode = OpenLayers.Control.ModifyFeature.RESHAPE; - OpenLayers.Control.prototype.initialize.apply(this, [options]); - if(!(OpenLayers.Util.isArray(this.deleteCodes))) { - this.deleteCodes = [this.deleteCodes]; - } - - // configure the drag handler - var dragCallbacks = { - down: function(pixel) { - this.vertex = null; - var feature = this.layer.getFeatureFromEvent( - this.handlers.drag.evt); - if (feature) { - this.dragStart(feature); - } else if (this.clickout) { - this._unselect = this.feature; - } - }, - move: function(pixel) { - delete this._unselect; - if (this.vertex) { - this.dragVertex(this.vertex, pixel); - } - }, - up: function() { - this.handlers.drag.stopDown = false; - if (this._unselect) { - this.unselectFeature(this._unselect); - delete this._unselect; - } - }, - done: function(pixel) { - if (this.vertex) { - this.dragComplete(this.vertex); - } - } - }; - var dragOptions = { - documentDrag: this.documentDrag, - stopDown: false - }; - - // configure the keyboard handler - var keyboardOptions = { - keydown: this.handleKeypress - }; - this.handlers = { - keyboard: new OpenLayers.Handler.Keyboard(this, keyboardOptions), - drag: new OpenLayers.Handler.Drag(this, dragCallbacks, dragOptions) - }; - }, - - /** - * APIMethod: destroy - * Take care of things that are not handled in superclass. - */ - destroy: function() { - if (this.map) { - this.map.events.un({ - "removelayer": this.handleMapEvents, - "changelayer": this.handleMapEvents, - scope: this - }); - } - this.layer = null; - OpenLayers.Control.prototype.destroy.apply(this, []); - }, - - /** - * APIMethod: activate - * Activate the control. - * - * Returns: - * {Boolean} Successfully activated the control. - */ - activate: function() { - this.moveLayerToTop(); - this.map.events.on({ - "removelayer": this.handleMapEvents, - "changelayer": this.handleMapEvents, - scope: this - }); - return (this.handlers.keyboard.activate() && - this.handlers.drag.activate() && - OpenLayers.Control.prototype.activate.apply(this, arguments)); - }, - - /** - * APIMethod: deactivate - * Deactivate the control. - * - * Returns: - * {Boolean} Successfully deactivated the control. - */ - deactivate: function() { - var deactivated = false; - // the return from the controls is unimportant in this case - if(OpenLayers.Control.prototype.deactivate.apply(this, arguments)) { - this.moveLayerBack(); - this.map.events.un({ - "removelayer": this.handleMapEvents, - "changelayer": this.handleMapEvents, - scope: this - }); - this.layer.removeFeatures(this.vertices, {silent: true}); - this.layer.removeFeatures(this.virtualVertices, {silent: true}); - this.vertices = []; - this.handlers.drag.deactivate(); - this.handlers.keyboard.deactivate(); - var feature = this.feature; - if (feature && feature.geometry && feature.layer) { - this.unselectFeature(feature); - } - deactivated = true; - } - return deactivated; - }, - - /** - * Method: beforeSelectFeature - * Called before a feature is selected. - * - * Parameters: - * feature - {<OpenLayers.Feature.Vector>} The feature about to be selected. - */ - beforeSelectFeature: function(feature) { - return this.layer.events.triggerEvent( - "beforefeaturemodified", {feature: feature} - ); - }, - - /** - * APIMethod: selectFeature - * Select a feature for modification in standalone mode. In non-standalone - * mode, this method is called when a feature is selected by clicking. - * Register a listener to the beforefeaturemodified event and return false - * to prevent feature modification. - * - * Parameters: - * feature - {<OpenLayers.Feature.Vector>} the selected feature. - */ - selectFeature: function(feature) { - if (this.feature === feature || - (this.geometryTypes && OpenLayers.Util.indexOf(this.geometryTypes, - feature.geometry.CLASS_NAME) == -1)) { - return; - } - if (this.beforeSelectFeature(feature) !== false) { - if (this.feature) { - this.unselectFeature(this.feature); - } - this.feature = feature; - this.layer.selectedFeatures.push(feature); - this.layer.drawFeature(feature, 'select'); - this.modified = false; - this.resetVertices(); - this.onModificationStart(this.feature); - } - // keep track of geometry modifications - var modified = feature.modified; - if (feature.geometry && !(modified && modified.geometry)) { - this._originalGeometry = feature.geometry.clone(); - } - }, - - /** - * APIMethod: unselectFeature - * Called when the select feature control unselects a feature. - * - * Parameters: - * feature - {<OpenLayers.Feature.Vector>} The unselected feature. - */ - unselectFeature: function(feature) { - this.layer.removeFeatures(this.vertices, {silent: true}); - this.vertices = []; - this.layer.destroyFeatures(this.virtualVertices, {silent: true}); - this.virtualVertices = []; - if(this.dragHandle) { - this.layer.destroyFeatures([this.dragHandle], {silent: true}); - delete this.dragHandle; - } - if(this.radiusHandle) { - this.layer.destroyFeatures([this.radiusHandle], {silent: true}); - delete this.radiusHandle; - } - this.layer.drawFeature(this.feature, 'default'); - this.feature = null; - OpenLayers.Util.removeItem(this.layer.selectedFeatures, feature); - this.onModificationEnd(feature); - this.layer.events.triggerEvent("afterfeaturemodified", { - feature: feature, - modified: this.modified - }); - this.modified = false; - }, - - - /** - * Method: dragStart - * Called by the drag handler before a feature is dragged. This method is - * used to differentiate between points and vertices - * of higher order geometries. - * - * Parameters: - * feature - {<OpenLayers.Feature.Vector>} The point or vertex about to be - * dragged. - */ - dragStart: function(feature) { - var isPoint = feature.geometry.CLASS_NAME == - 'OpenLayers.Geometry.Point'; - if (!this.standalone && - ((!feature._sketch && isPoint) || !feature._sketch)) { - if (this.toggle && this.feature === feature) { - // mark feature for unselection - this._unselect = feature; - } - this.selectFeature(feature); - } - if (feature._sketch || isPoint) { - // feature is a drag or virtual handle or point - this.vertex = feature; - this.handlers.drag.stopDown = true; - } - }, - - /** - * Method: dragVertex - * Called by the drag handler with each drag move of a vertex. - * - * Parameters: - * vertex - {<OpenLayers.Feature.Vector>} The vertex being dragged. - * pixel - {<OpenLayers.Pixel>} Pixel location of the mouse event. - */ - dragVertex: function(vertex, pixel) { - var pos = this.map.getLonLatFromViewPortPx(pixel); - var geom = vertex.geometry; - geom.move(pos.lon - geom.x, pos.lat - geom.y); - this.modified = true; - /** - * Five cases: - * 1) dragging a simple point - * 2) dragging a virtual vertex - * 3) dragging a drag handle - * 4) dragging a real vertex - * 5) dragging a radius handle - */ - if(this.feature.geometry.CLASS_NAME == "OpenLayers.Geometry.Point") { - // dragging a simple point - this.layer.events.triggerEvent("vertexmodified", { - vertex: vertex.geometry, - feature: this.feature, - pixel: pixel - }); - } else { - if(vertex._index) { - // dragging a virtual vertex - vertex.geometry.parent.addComponent(vertex.geometry, - vertex._index); - // move from virtual to real vertex - delete vertex._index; - OpenLayers.Util.removeItem(this.virtualVertices, vertex); - this.vertices.push(vertex); - } else if(vertex == this.dragHandle) { - // dragging a drag handle - this.layer.removeFeatures(this.vertices, {silent: true}); - this.vertices = []; - if(this.radiusHandle) { - this.layer.destroyFeatures([this.radiusHandle], {silent: true}); - this.radiusHandle = null; - } - } else if(vertex !== this.radiusHandle) { - // dragging a real vertex - this.layer.events.triggerEvent("vertexmodified", { - vertex: vertex.geometry, - feature: this.feature, - pixel: pixel - }); - } - // dragging a radius handle - no special treatment - if(this.virtualVertices.length > 0) { - this.layer.destroyFeatures(this.virtualVertices, {silent: true}); - this.virtualVertices = []; - } - this.layer.drawFeature(this.feature, this.standalone ? undefined : - 'select'); - } - // keep the vertex on top so it gets the mouseout after dragging - // this should be removed in favor of an option to draw under or - // maintain node z-index - this.layer.drawFeature(vertex); - }, - - /** - * Method: dragComplete - * Called by the drag handler when the feature dragging is complete. - * - * Parameters: - * vertex - {<OpenLayers.Feature.Vector>} The vertex being dragged. - */ - dragComplete: function(vertex) { - this.resetVertices(); - this.setFeatureState(); - this.onModification(this.feature); - this.layer.events.triggerEvent("featuremodified", - {feature: this.feature}); - }, - - /** - * Method: setFeatureState - * Called when the feature is modified. If the current state is not - * INSERT or DELETE, the state is set to UPDATE. - */ - setFeatureState: function() { - if(this.feature.state != OpenLayers.State.INSERT && - this.feature.state != OpenLayers.State.DELETE) { - this.feature.state = OpenLayers.State.UPDATE; - if (this.modified && this._originalGeometry) { - var feature = this.feature; - feature.modified = OpenLayers.Util.extend(feature.modified, { - geometry: this._originalGeometry - }); - delete this._originalGeometry; - } - } - }, - - /** - * Method: resetVertices - */ - resetVertices: function() { - if(this.vertices.length > 0) { - this.layer.removeFeatures(this.vertices, {silent: true}); - this.vertices = []; - } - if(this.virtualVertices.length > 0) { - this.layer.removeFeatures(this.virtualVertices, {silent: true}); - this.virtualVertices = []; - } - if(this.dragHandle) { - this.layer.destroyFeatures([this.dragHandle], {silent: true}); - this.dragHandle = null; - } - if(this.radiusHandle) { - this.layer.destroyFeatures([this.radiusHandle], {silent: true}); - this.radiusHandle = null; - } - if(this.feature && - this.feature.geometry.CLASS_NAME != "OpenLayers.Geometry.Point") { - if((this.mode & OpenLayers.Control.ModifyFeature.DRAG)) { - this.collectDragHandle(); - } - if((this.mode & (OpenLayers.Control.ModifyFeature.ROTATE | - OpenLayers.Control.ModifyFeature.RESIZE))) { - this.collectRadiusHandle(); - } - if(this.mode & OpenLayers.Control.ModifyFeature.RESHAPE){ - // Don't collect vertices when we're resizing - if (!(this.mode & OpenLayers.Control.ModifyFeature.RESIZE)){ - this.collectVertices(); - } - } - } - }, - - /** - * Method: handleKeypress - * Called by the feature handler on keypress. This is used to delete - * vertices. If the <deleteCode> property is set, vertices will - * be deleted when a feature is selected for modification and - * the mouse is over a vertex. - * - * Parameters: - * evt - {Event} Keypress event. - */ - handleKeypress: function(evt) { - var code = evt.keyCode; - - // check for delete key - if(this.feature && - OpenLayers.Util.indexOf(this.deleteCodes, code) != -1) { - var vertex = this.layer.getFeatureFromEvent(this.handlers.drag.evt); - if (vertex && - OpenLayers.Util.indexOf(this.vertices, vertex) != -1 && - !this.handlers.drag.dragging && vertex.geometry.parent) { - // remove the vertex - vertex.geometry.parent.removeComponent(vertex.geometry); - this.layer.events.triggerEvent("vertexremoved", { - vertex: vertex.geometry, - feature: this.feature, - pixel: evt.xy - }); - this.layer.drawFeature(this.feature, this.standalone ? - undefined : 'select'); - this.modified = true; - this.resetVertices(); - this.setFeatureState(); - this.onModification(this.feature); - this.layer.events.triggerEvent("featuremodified", - {feature: this.feature}); - } - } - }, - - /** - * Method: collectVertices - * Collect the vertices from the modifiable feature's geometry and push - * them on to the control's vertices array. - */ - collectVertices: function() { - this.vertices = []; - this.virtualVertices = []; - var control = this; - function collectComponentVertices(geometry) { - var i, vertex, component, len; - if(geometry.CLASS_NAME == "OpenLayers.Geometry.Point") { - vertex = new OpenLayers.Feature.Vector(geometry); - vertex._sketch = true; - vertex.renderIntent = control.vertexRenderIntent; - control.vertices.push(vertex); - } else { - var numVert = geometry.components.length; - if(geometry.CLASS_NAME == "OpenLayers.Geometry.LinearRing") { - numVert -= 1; - } - for(i=0; i<numVert; ++i) { - component = geometry.components[i]; - if(component.CLASS_NAME == "OpenLayers.Geometry.Point") { - vertex = new OpenLayers.Feature.Vector(component); - vertex._sketch = true; - vertex.renderIntent = control.vertexRenderIntent; - control.vertices.push(vertex); - } else { - collectComponentVertices(component); - } - } - - // add virtual vertices in the middle of each edge - if (control.createVertices && geometry.CLASS_NAME != "OpenLayers.Geometry.MultiPoint") { - for(i=0, len=geometry.components.length; i<len-1; ++i) { - var prevVertex = geometry.components[i]; - var nextVertex = geometry.components[i + 1]; - if(prevVertex.CLASS_NAME == "OpenLayers.Geometry.Point" && - nextVertex.CLASS_NAME == "OpenLayers.Geometry.Point") { - var x = (prevVertex.x + nextVertex.x) / 2; - var y = (prevVertex.y + nextVertex.y) / 2; - var point = new OpenLayers.Feature.Vector( - new OpenLayers.Geometry.Point(x, y), - null, control.virtualStyle - ); - // set the virtual parent and intended index - point.geometry.parent = geometry; - point._index = i + 1; - point._sketch = true; - control.virtualVertices.push(point); - } - } - } - } - } - collectComponentVertices.call(this, this.feature.geometry); - this.layer.addFeatures(this.virtualVertices, {silent: true}); - this.layer.addFeatures(this.vertices, {silent: true}); - }, - - /** - * Method: collectDragHandle - * Collect the drag handle for the selected geometry. - */ - collectDragHandle: function() { - var geometry = this.feature.geometry; - var center = geometry.getBounds().getCenterLonLat(); - var originGeometry = new OpenLayers.Geometry.Point( - center.lon, center.lat - ); - var origin = new OpenLayers.Feature.Vector(originGeometry); - originGeometry.move = function(x, y) { - OpenLayers.Geometry.Point.prototype.move.call(this, x, y); - geometry.move(x, y); - }; - origin._sketch = true; - this.dragHandle = origin; - this.dragHandle.renderIntent = this.vertexRenderIntent; - this.layer.addFeatures([this.dragHandle], {silent: true}); - }, - - /** - * Method: collectRadiusHandle - * Collect the radius handle for the selected geometry. - */ - collectRadiusHandle: function() { - var geometry = this.feature.geometry; - var bounds = geometry.getBounds(); - var center = bounds.getCenterLonLat(); - var originGeometry = new OpenLayers.Geometry.Point( - center.lon, center.lat - ); - var radiusGeometry = new OpenLayers.Geometry.Point( - bounds.right, bounds.bottom - ); - var radius = new OpenLayers.Feature.Vector(radiusGeometry); - var resize = (this.mode & OpenLayers.Control.ModifyFeature.RESIZE); - var reshape = (this.mode & OpenLayers.Control.ModifyFeature.RESHAPE); - var rotate = (this.mode & OpenLayers.Control.ModifyFeature.ROTATE); - - radiusGeometry.move = function(x, y) { - OpenLayers.Geometry.Point.prototype.move.call(this, x, y); - var dx1 = this.x - originGeometry.x; - var dy1 = this.y - originGeometry.y; - var dx0 = dx1 - x; - var dy0 = dy1 - y; - if(rotate) { - var a0 = Math.atan2(dy0, dx0); - var a1 = Math.atan2(dy1, dx1); - var angle = a1 - a0; - angle *= 180 / Math.PI; - geometry.rotate(angle, originGeometry); - } - if(resize) { - var scale, ratio; - // 'resize' together with 'reshape' implies that the aspect - // ratio of the geometry will not be preserved whilst resizing - if (reshape) { - scale = dy1 / dy0; - ratio = (dx1 / dx0) / scale; - } else { - var l0 = Math.sqrt((dx0 * dx0) + (dy0 * dy0)); - var l1 = Math.sqrt((dx1 * dx1) + (dy1 * dy1)); - scale = l1 / l0; - } - geometry.resize(scale, originGeometry, ratio); - } - }; - radius._sketch = true; - this.radiusHandle = radius; - this.radiusHandle.renderIntent = this.vertexRenderIntent; - this.layer.addFeatures([this.radiusHandle], {silent: true}); - }, - - /** - * Method: setMap - * Set the map property for the control and all handlers. - * - * Parameters: - * map - {<OpenLayers.Map>} The control's map. - */ - setMap: function(map) { - this.handlers.drag.setMap(map); - OpenLayers.Control.prototype.setMap.apply(this, arguments); - }, - - /** - * 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.Control.ModifyFeature" -}); - -/** - * Constant: RESHAPE - * {Integer} Constant used to make the control work in reshape mode - */ -OpenLayers.Control.ModifyFeature.RESHAPE = 1; -/** - * Constant: RESIZE - * {Integer} Constant used to make the control work in resize mode - */ -OpenLayers.Control.ModifyFeature.RESIZE = 2; -/** - * Constant: ROTATE - * {Integer} Constant used to make the control work in rotate mode - */ -OpenLayers.Control.ModifyFeature.ROTATE = 4; -/** - * Constant: DRAG - * {Integer} Constant used to make the control work in drag mode - */ -OpenLayers.Control.ModifyFeature.DRAG = 8; -/* ====================================================================== - OpenLayers/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/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/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/XML.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.js - */ - -/** - * Class: OpenLayers.Format.XML - * Read and write XML. For cross-browser XML generation, use methods on an - * instance of the XML format class instead of on <code>document<end>. - * The DOM creation and traversing methods exposed here all mimic the - * W3C XML DOM methods. Create a new parser with the - * <OpenLayers.Format.XML> constructor. - * - * Inherits from: - * - <OpenLayers.Format> - */ -OpenLayers.Format.XML = OpenLayers.Class(OpenLayers.Format, { - - /** - * Property: namespaces - * {Object} Mapping of namespace aliases to namespace URIs. Properties - * of this object should not be set individually. Read-only. All - * XML subclasses should have their own namespaces object. Use - * <setNamespace> to add or set a namespace alias after construction. - */ - namespaces: null, - - /** - * Property: namespaceAlias - * {Object} Mapping of namespace URI to namespace alias. This object - * is read-only. Use <setNamespace> to add or set a namespace alias. - */ - namespaceAlias: null, - - /** - * Property: defaultPrefix - * {String} The default namespace alias for creating element nodes. - */ - defaultPrefix: null, - - /** - * Property: readers - * Contains public functions, grouped by namespace prefix, that will - * be applied when a namespaced node is found matching the function - * name. The function will be applied in the scope of this parser - * with two arguments: the node being read and a context object passed - * from the parent. - */ - readers: {}, - - /** - * Property: writers - * As a compliment to the <readers> property, this structure contains public - * writing functions grouped by namespace alias and named like the - * node names they produce. - */ - writers: {}, - - /** - * Property: xmldom - * {XMLDom} If this browser uses ActiveX, this will be set to a XMLDOM - * object. It is not intended to be a browser sniffing property. - * Instead, the xmldom property is used instead of <code>document<end> - * where namespaced node creation methods are not supported. In all - * other browsers, this remains null. - */ - xmldom: null, - - /** - * Constructor: OpenLayers.Format.XML - * Construct an XML parser. The parser is used to read and write XML. - * Reading XML from a string returns a DOM element. Writing XML from - * a DOM element returns a string. - * - * Parameters: - * options - {Object} Optional object whose properties will be set on - * the object. - */ - initialize: function(options) { - if(window.ActiveXObject) { - this.xmldom = new ActiveXObject("Microsoft.XMLDOM"); - } - OpenLayers.Format.prototype.initialize.apply(this, [options]); - // clone the namespace object and set all namespace aliases - this.namespaces = OpenLayers.Util.extend({}, this.namespaces); - this.namespaceAlias = {}; - for(var alias in this.namespaces) { - this.namespaceAlias[this.namespaces[alias]] = alias; - } - }, - - /** - * APIMethod: destroy - * Clean up. - */ - destroy: function() { - this.xmldom = null; - OpenLayers.Format.prototype.destroy.apply(this, arguments); - }, - - /** - * Method: setNamespace - * Set a namespace alias and URI for the format. - * - * Parameters: - * alias - {String} The namespace alias (prefix). - * uri - {String} The namespace URI. - */ - setNamespace: function(alias, uri) { - this.namespaces[alias] = uri; - this.namespaceAlias[uri] = alias; - }, - - /** - * APIMethod: read - * Deserialize a XML string and return a DOM node. - * - * Parameters: - * text - {String} A XML string - - * Returns: - * {DOMElement} A DOM node - */ - read: function(text) { - var index = text.indexOf('<'); - if(index > 0) { - text = text.substring(index); - } - var node = OpenLayers.Util.Try( - OpenLayers.Function.bind(( - function() { - var xmldom; - /** - * Since we want to be able to call this method on the prototype - * itself, this.xmldom may not exist even if in IE. - */ - if(window.ActiveXObject && !this.xmldom) { - xmldom = new ActiveXObject("Microsoft.XMLDOM"); - } else { - xmldom = this.xmldom; - - } - xmldom.loadXML(text); - return xmldom; - } - ), this), - function() { - return new DOMParser().parseFromString(text, 'text/xml'); - }, - function() { - var req = new XMLHttpRequest(); - req.open("GET", "data:" + "text/xml" + - ";charset=utf-8," + encodeURIComponent(text), false); - if(req.overrideMimeType) { - req.overrideMimeType("text/xml"); - } - req.send(null); - return req.responseXML; - } - ); - - if(this.keepData) { - this.data = node; - } - - return node; - }, - - /** - * APIMethod: write - * Serialize a DOM node into a XML string. - * - * Parameters: - * node - {DOMElement} A DOM node. - * - * Returns: - * {String} The XML string representation of the input node. - */ - write: function(node) { - var data; - if(this.xmldom) { - data = node.xml; - } else { - var serializer = new XMLSerializer(); - if (node.nodeType == 1) { - // Add nodes to a document before serializing. Everything else - // is serialized as is. This may need more work. See #1218 . - var doc = document.implementation.createDocument("", "", null); - if (doc.importNode) { - node = doc.importNode(node, true); - } - doc.appendChild(node); - data = serializer.serializeToString(doc); - } else { - data = serializer.serializeToString(node); - } - } - return data; - }, - - /** - * APIMethod: createElementNS - * Create a new element with namespace. This node can be appended to - * another node with the standard node.appendChild method. For - * cross-browser support, this method must be used instead of - * document.createElementNS. - * - * Parameters: - * uri - {String} Namespace URI for the element. - * name - {String} The qualified name of the element (prefix:localname). - * - * Returns: - * {Element} A DOM element with namespace. - */ - createElementNS: function(uri, name) { - var element; - if(this.xmldom) { - if(typeof uri == "string") { - element = this.xmldom.createNode(1, name, uri); - } else { - element = this.xmldom.createNode(1, name, ""); - } - } else { - element = document.createElementNS(uri, name); - } - return element; - }, - - /** - * APIMethod: createDocumentFragment - * Create a document fragment node that can be appended to another node - * created by createElementNS. This will call - * document.createDocumentFragment outside of IE. In IE, the ActiveX - * object's createDocumentFragment method is used. - * - * Returns: - * {Element} A document fragment. - */ - createDocumentFragment: function() { - var element; - if (this.xmldom) { - element = this.xmldom.createDocumentFragment(); - } else { - element = document.createDocumentFragment(); - } - return element; - }, - - /** - * APIMethod: createTextNode - * Create a text node. This node can be appended to another node with - * the standard node.appendChild method. For cross-browser support, - * this method must be used instead of document.createTextNode. - * - * Parameters: - * text - {String} The text of the node. - * - * Returns: - * {DOMElement} A DOM text node. - */ - createTextNode: function(text) { - var node; - if (typeof text !== "string") { - text = String(text); - } - if(this.xmldom) { - node = this.xmldom.createTextNode(text); - } else { - node = document.createTextNode(text); - } - return node; - }, - - /** - * APIMethod: getElementsByTagNameNS - * Get a list of elements on a node given the namespace URI and local name. - * To return all nodes in a given namespace, use '*' for the name - * argument. To return all nodes of a given (local) name, regardless - * of namespace, use '*' for the uri argument. - * - * Parameters: - * node - {Element} Node on which to search for other nodes. - * uri - {String} Namespace URI. - * name - {String} Local name of the tag (without the prefix). - * - * Returns: - * {NodeList} A node list or array of elements. - */ - getElementsByTagNameNS: function(node, uri, name) { - var elements = []; - if(node.getElementsByTagNameNS) { - elements = node.getElementsByTagNameNS(uri, name); - } else { - // brute force method - var allNodes = node.getElementsByTagName("*"); - var potentialNode, fullName; - for(var i=0, len=allNodes.length; i<len; ++i) { - potentialNode = allNodes[i]; - fullName = (potentialNode.prefix) ? - (potentialNode.prefix + ":" + name) : name; - if((name == "*") || (fullName == potentialNode.nodeName)) { - if((uri == "*") || (uri == potentialNode.namespaceURI)) { - elements.push(potentialNode); - } - } - } - } - return elements; - }, - - /** - * APIMethod: getAttributeNodeNS - * Get an attribute node given the namespace URI and local name. - * - * Parameters: - * node - {Element} Node on which to search for attribute nodes. - * uri - {String} Namespace URI. - * name - {String} Local name of the attribute (without the prefix). - * - * Returns: - * {DOMElement} An attribute node or null if none found. - */ - getAttributeNodeNS: function(node, uri, name) { - var attributeNode = null; - if(node.getAttributeNodeNS) { - attributeNode = node.getAttributeNodeNS(uri, name); - } else { - var attributes = node.attributes; - var potentialNode, fullName; - for(var i=0, len=attributes.length; i<len; ++i) { - potentialNode = attributes[i]; - if(potentialNode.namespaceURI == uri) { - fullName = (potentialNode.prefix) ? - (potentialNode.prefix + ":" + name) : name; - if(fullName == potentialNode.nodeName) { - attributeNode = potentialNode; - break; - } - } - } - } - return attributeNode; - }, - - /** - * APIMethod: getAttributeNS - * Get an attribute value given the namespace URI and local name. - * - * Parameters: - * node - {Element} Node on which to search for an attribute. - * uri - {String} Namespace URI. - * name - {String} Local name of the attribute (without the prefix). - * - * Returns: - * {String} An attribute value or and empty string if none found. - */ - getAttributeNS: function(node, uri, name) { - var attributeValue = ""; - if(node.getAttributeNS) { - attributeValue = node.getAttributeNS(uri, name) || ""; - } else { - var attributeNode = this.getAttributeNodeNS(node, uri, name); - if(attributeNode) { - attributeValue = attributeNode.nodeValue; - } - } - return attributeValue; - }, - - /** - * APIMethod: getChildValue - * Get the textual value of the node if it exists, or return an - * optional default string. Returns an empty string if no first child - * exists and no default value is supplied. - * - * Parameters: - * node - {DOMElement} The element used to look for a first child value. - * def - {String} Optional string to return in the event that no - * first child value exists. - * - * Returns: - * {String} The value of the first child of the given node. - */ - getChildValue: function(node, def) { - var value = def || ""; - if(node) { - for(var child=node.firstChild; child; child=child.nextSibling) { - switch(child.nodeType) { - case 3: // text node - case 4: // cdata section - value += child.nodeValue; - } - } - } - return value; - }, - - /** - * APIMethod: isSimpleContent - * Test if the given node has only simple content (i.e. no child element - * nodes). - * - * Parameters: - * node - {DOMElement} An element node. - * - * Returns: - * {Boolean} The node has no child element nodes (nodes of type 1). - */ - isSimpleContent: function(node) { - var simple = true; - for(var child=node.firstChild; child; child=child.nextSibling) { - if(child.nodeType === 1) { - simple = false; - break; - } - } - return simple; - }, - - /** - * APIMethod: contentType - * Determine the content type for a given node. - * - * Parameters: - * node - {DOMElement} - * - * Returns: - * {Integer} One of OpenLayers.Format.XML.CONTENT_TYPE.{EMPTY,SIMPLE,COMPLEX,MIXED} - * if the node has no, simple, complex, or mixed content. - */ - contentType: function(node) { - var simple = false, - complex = false; - - var type = OpenLayers.Format.XML.CONTENT_TYPE.EMPTY; - - for(var child=node.firstChild; child; child=child.nextSibling) { - switch(child.nodeType) { - case 1: // element - complex = true; - break; - case 8: // comment - break; - default: - simple = true; - } - if(complex && simple) { - break; - } - } - - if(complex && simple) { - type = OpenLayers.Format.XML.CONTENT_TYPE.MIXED; - } else if(complex) { - return OpenLayers.Format.XML.CONTENT_TYPE.COMPLEX; - } else if(simple) { - return OpenLayers.Format.XML.CONTENT_TYPE.SIMPLE; - } - return type; - }, - - /** - * APIMethod: hasAttributeNS - * Determine whether a node has a particular attribute matching the given - * name and namespace. - * - * Parameters: - * node - {Element} Node on which to search for an attribute. - * uri - {String} Namespace URI. - * name - {String} Local name of the attribute (without the prefix). - * - * Returns: - * {Boolean} The node has an attribute matching the name and namespace. - */ - hasAttributeNS: function(node, uri, name) { - var found = false; - if(node.hasAttributeNS) { - found = node.hasAttributeNS(uri, name); - } else { - found = !!this.getAttributeNodeNS(node, uri, name); - } - return found; - }, - - /** - * APIMethod: setAttributeNS - * Adds a new attribute or changes the value of an attribute with the given - * namespace and name. - * - * Parameters: - * node - {Element} Element node on which to set the attribute. - * uri - {String} Namespace URI for the attribute. - * name - {String} Qualified name (prefix:localname) for the attribute. - * value - {String} Attribute value. - */ - setAttributeNS: function(node, uri, name, value) { - if(node.setAttributeNS) { - node.setAttributeNS(uri, name, value); - } else { - if(this.xmldom) { - if(uri) { - var attribute = node.ownerDocument.createNode( - 2, name, uri - ); - attribute.nodeValue = value; - node.setAttributeNode(attribute); - } else { - node.setAttribute(name, value); - } - } else { - throw "setAttributeNS not implemented"; - } - } - }, - - /** - * Method: createElementNSPlus - * Shorthand for creating namespaced elements with optional attributes and - * child text nodes. - * - * Parameters: - * name - {String} The qualified node name. - * options - {Object} Optional object for node configuration. - * - * Valid options: - * uri - {String} Optional namespace uri for the element - supply a prefix - * instead if the namespace uri is a property of the format's namespace - * object. - * attributes - {Object} Optional attributes to be set using the - * <setAttributes> method. - * value - {String} Optional text to be appended as a text node. - * - * Returns: - * {Element} An element node. - */ - createElementNSPlus: function(name, options) { - options = options || {}; - // order of prefix preference - // 1. in the uri option - // 2. in the prefix option - // 3. in the qualified name - // 4. from the defaultPrefix - var uri = options.uri || this.namespaces[options.prefix]; - if(!uri) { - var loc = name.indexOf(":"); - uri = this.namespaces[name.substring(0, loc)]; - } - if(!uri) { - uri = this.namespaces[this.defaultPrefix]; - } - var node = this.createElementNS(uri, name); - if(options.attributes) { - this.setAttributes(node, options.attributes); - } - var value = options.value; - if(value != null) { - node.appendChild(this.createTextNode(value)); - } - return node; - }, - - /** - * Method: setAttributes - * Set multiple attributes given key value pairs from an object. - * - * Parameters: - * node - {Element} An element node. - * obj - {Object || Array} An object whose properties represent attribute - * names and values represent attribute values. If an attribute name - * is a qualified name ("prefix:local"), the prefix will be looked up - * in the parsers {namespaces} object. If the prefix is found, - * setAttributeNS will be used instead of setAttribute. - */ - setAttributes: function(node, obj) { - var value, uri; - for(var name in obj) { - if(obj[name] != null && obj[name].toString) { - value = obj[name].toString(); - // check for qualified attribute name ("prefix:local") - uri = this.namespaces[name.substring(0, name.indexOf(":"))] || null; - this.setAttributeNS(node, uri, name, value); - } - } - }, - - /** - * Method: readNode - * Shorthand for applying one of the named readers given the node - * namespace and local name. Readers take two args (node, obj) and - * generally extend or modify the second. - * - * Parameters: - * node - {DOMElement} The node to be read (required). - * obj - {Object} The object to be modified (optional). - * - * Returns: - * {Object} The input object, modified (or a new one if none was provided). - */ - readNode: function(node, obj) { - if(!obj) { - obj = {}; - } - var group = this.readers[node.namespaceURI ? this.namespaceAlias[node.namespaceURI]: this.defaultPrefix]; - if(group) { - var local = node.localName || node.nodeName.split(":").pop(); - var reader = group[local] || group["*"]; - if(reader) { - reader.apply(this, [node, obj]); - } - } - return obj; - }, - - /** - * Method: readChildNodes - * Shorthand for applying the named readers to all children of a node. - * For each child of type 1 (element), <readSelf> is called. - * - * Parameters: - * node - {DOMElement} The node to be read (required). - * obj - {Object} The object to be modified (optional). - * - * Returns: - * {Object} The input object, modified. - */ - readChildNodes: function(node, obj) { - if(!obj) { - obj = {}; - } - var children = node.childNodes; - var child; - for(var i=0, len=children.length; i<len; ++i) { - child = children[i]; - if(child.nodeType == 1) { - this.readNode(child, obj); - } - } - return obj; - }, - - /** - * Method: writeNode - * Shorthand for applying one of the named writers and appending the - * results to a node. If a qualified name is not provided for the - * second argument (and a local name is used instead), the namespace - * of the parent node will be assumed. - * - * Parameters: - * name - {String} The name of a node to generate. If a qualified name - * (e.g. "pre:Name") is used, the namespace prefix is assumed to be - * in the <writers> group. If a local name is used (e.g. "Name") then - * the namespace of the parent is assumed. If a local name is used - * and no parent is supplied, then the default namespace is assumed. - * obj - {Object} Structure containing data for the writer. - * parent - {DOMElement} Result will be appended to this node. If no parent - * is supplied, the node will not be appended to anything. - * - * Returns: - * {DOMElement} The child node. - */ - writeNode: function(name, obj, parent) { - var prefix, local; - var split = name.indexOf(":"); - if(split > 0) { - prefix = name.substring(0, split); - local = name.substring(split + 1); - } else { - if(parent) { - prefix = this.namespaceAlias[parent.namespaceURI]; - } else { - prefix = this.defaultPrefix; - } - local = name; - } - var child = this.writers[prefix][local].apply(this, [obj]); - if(parent) { - parent.appendChild(child); - } - return child; - }, - - /** - * APIMethod: getChildEl - * Get the first child element. Optionally only return the first child - * if it matches the given name and namespace URI. - * - * Parameters: - * node - {DOMElement} The parent node. - * name - {String} Optional node name (local) to search for. - * uri - {String} Optional namespace URI to search for. - * - * Returns: - * {DOMElement} The first child. Returns null if no element is found, if - * something significant besides an element is found, or if the element - * found does not match the optional name and uri. - */ - getChildEl: function(node, name, uri) { - return node && this.getThisOrNextEl(node.firstChild, name, uri); - }, - - /** - * APIMethod: getNextEl - * Get the next sibling element. Optionally get the first sibling only - * if it matches the given local name and namespace URI. - * - * Parameters: - * node - {DOMElement} The node. - * name - {String} Optional local name of the sibling to search for. - * uri - {String} Optional namespace URI of the sibling to search for. - * - * Returns: - * {DOMElement} The next sibling element. Returns null if no element is - * found, something significant besides an element is found, or the - * found element does not match the optional name and uri. - */ - getNextEl: function(node, name, uri) { - return node && this.getThisOrNextEl(node.nextSibling, name, uri); - }, - - /** - * Method: getThisOrNextEl - * Return this node or the next element node. Optionally get the first - * sibling with the given local name or namespace URI. - * - * Parameters: - * node - {DOMElement} The node. - * name - {String} Optional local name of the sibling to search for. - * uri - {String} Optional namespace URI of the sibling to search for. - * - * Returns: - * {DOMElement} The next sibling element. Returns null if no element is - * found, something significant besides an element is found, or the - * found element does not match the query. - */ - getThisOrNextEl: function(node, name, uri) { - outer: for(var sibling=node; sibling; sibling=sibling.nextSibling) { - switch(sibling.nodeType) { - case 1: // Element - if((!name || name === (sibling.localName || sibling.nodeName.split(":").pop())) && - (!uri || uri === sibling.namespaceURI)) { - // matches - break outer; - } - sibling = null; - break outer; - case 3: // Text - if(/^\s*$/.test(sibling.nodeValue)) { - break; - } - case 4: // CDATA - case 6: // ENTITY_NODE - case 12: // NOTATION_NODE - case 10: // DOCUMENT_TYPE_NODE - case 11: // DOCUMENT_FRAGMENT_NODE - sibling = null; - break outer; - } // ignore comments and processing instructions - } - return sibling || null; - }, - - /** - * APIMethod: lookupNamespaceURI - * Takes a prefix and returns the namespace URI associated with it on the given - * node if found (and null if not). Supplying null for the prefix will - * return the default namespace. - * - * For browsers that support it, this calls the native lookupNamesapceURI - * function. In other browsers, this is an implementation of - * http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-lookupNamespaceURI. - * - * For browsers that don't support the attribute.ownerElement property, this - * method cannot be called on attribute nodes. - * - * Parameters: - * node - {DOMElement} The node from which to start looking. - * prefix - {String} The prefix to lookup or null to lookup the default namespace. - * - * Returns: - * {String} The namespace URI for the given prefix. Returns null if the prefix - * cannot be found or the node is the wrong type. - */ - lookupNamespaceURI: function(node, prefix) { - var uri = null; - if(node) { - if(node.lookupNamespaceURI) { - uri = node.lookupNamespaceURI(prefix); - } else { - outer: switch(node.nodeType) { - case 1: // ELEMENT_NODE - if(node.namespaceURI !== null && node.prefix === prefix) { - uri = node.namespaceURI; - break outer; - } - var len = node.attributes.length; - if(len) { - var attr; - for(var i=0; i<len; ++i) { - attr = node.attributes[i]; - if(attr.prefix === "xmlns" && attr.name === "xmlns:" + prefix) { - uri = attr.value || null; - break outer; - } else if(attr.name === "xmlns" && prefix === null) { - uri = attr.value || null; - break outer; - } - } - } - uri = this.lookupNamespaceURI(node.parentNode, prefix); - break outer; - case 2: // ATTRIBUTE_NODE - uri = this.lookupNamespaceURI(node.ownerElement, prefix); - break outer; - case 9: // DOCUMENT_NODE - uri = this.lookupNamespaceURI(node.documentElement, prefix); - break outer; - case 6: // ENTITY_NODE - case 12: // NOTATION_NODE - case 10: // DOCUMENT_TYPE_NODE - case 11: // DOCUMENT_FRAGMENT_NODE - break outer; - default: - // TEXT_NODE (3), CDATA_SECTION_NODE (4), ENTITY_REFERENCE_NODE (5), - // PROCESSING_INSTRUCTION_NODE (7), COMMENT_NODE (8) - uri = this.lookupNamespaceURI(node.parentNode, prefix); - break outer; - } - } - } - return uri; - }, - - /** - * Method: getXMLDoc - * Get an XML document for nodes that are not supported in HTML (e.g. - * createCDATASection). On IE, this will either return an existing or - * create a new <xmldom> on the instance. On other browsers, this will - * either return an existing or create a new shared document (see - * <OpenLayers.Format.XML.document>). - * - * Returns: - * {XMLDocument} - */ - getXMLDoc: function() { - if (!OpenLayers.Format.XML.document && !this.xmldom) { - if (document.implementation && document.implementation.createDocument) { - OpenLayers.Format.XML.document = - document.implementation.createDocument("", "", null); - } else if (!this.xmldom && window.ActiveXObject) { - this.xmldom = new ActiveXObject("Microsoft.XMLDOM"); - } - } - return OpenLayers.Format.XML.document || this.xmldom; - }, - - CLASS_NAME: "OpenLayers.Format.XML" - -}); - -OpenLayers.Format.XML.CONTENT_TYPE = {EMPTY: 0, SIMPLE: 1, COMPLEX: 2, MIXED: 3}; - -/** - * APIFunction: OpenLayers.Format.XML.lookupNamespaceURI - * Takes a prefix and returns the namespace URI associated with it on the given - * node if found (and null if not). Supplying null for the prefix will - * return the default namespace. - * - * For browsers that support it, this calls the native lookupNamesapceURI - * function. In other browsers, this is an implementation of - * http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-lookupNamespaceURI. - * - * For browsers that don't support the attribute.ownerElement property, this - * method cannot be called on attribute nodes. - * - * Parameters: - * node - {DOMElement} The node from which to start looking. - * prefix - {String} The prefix to lookup or null to lookup the default namespace. - * - * Returns: - * {String} The namespace URI for the given prefix. Returns null if the prefix - * cannot be found or the node is the wrong type. - */ -OpenLayers.Format.XML.lookupNamespaceURI = OpenLayers.Function.bind( - OpenLayers.Format.XML.prototype.lookupNamespaceURI, - OpenLayers.Format.XML.prototype -); - -/** - * Property: OpenLayers.Format.XML.document - * {XMLDocument} XML document to reuse for creating non-HTML compliant nodes, - * like document.createCDATASection. - */ -OpenLayers.Format.XML.document = null; -/* ====================================================================== - OpenLayers/Format/OGCExceptionReport.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/XML.js - */ - -/** - * Class: OpenLayers.Format.OGCExceptionReport - * Class to read exception reports for various OGC services and versions. - * - * Inherits from: - * - <OpenLayers.Format.XML> - */ -OpenLayers.Format.OGCExceptionReport = OpenLayers.Class(OpenLayers.Format.XML, { - - /** - * Property: namespaces - * {Object} Mapping of namespace aliases to namespace URIs. - */ - namespaces: { - ogc: "http://www.opengis.net/ogc" - }, - - /** - * Property: regExes - * Compiled regular expressions for manipulating strings. - */ - regExes: { - trimSpace: (/^\s*|\s*$/g), - removeSpace: (/\s*/g), - splitSpace: (/\s+/), - trimComma: (/\s*,\s*/g) - }, - - /** - * Property: defaultPrefix - */ - defaultPrefix: "ogc", - - /** - * Constructor: OpenLayers.Format.OGCExceptionReport - * Create a new parser for OGC exception reports. - * - * Parameters: - * options - {Object} An optional object whose properties will be set on - * this instance. - */ - - /** - * APIMethod: read - * Read OGC exception report data from a string, and return an object with - * information about the exceptions. - * - * Parameters: - * data - {String} or {DOMElement} data to read/parse. - * - * Returns: - * {Object} Information about the exceptions that occurred. - */ - read: function(data) { - var result; - if(typeof data == "string") { - data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); - } - var root = data.documentElement; - var exceptionInfo = {exceptionReport: null}; - if (root) { - this.readChildNodes(data, exceptionInfo); - if (exceptionInfo.exceptionReport === null) { - // fall-back to OWSCommon since this is a common output format for exceptions - // we cannot easily use the ows readers directly since they differ for 1.0 and 1.1 - exceptionInfo = new OpenLayers.Format.OWSCommon().read(data); - } - } - return exceptionInfo; - }, - - /** - * Property: readers - * Contains public functions, grouped by namespace prefix, that will - * be applied when a namespaced node is found matching the function - * name. The function will be applied in the scope of this parser - * with two arguments: the node being read and a context object passed - * from the parent. - */ - readers: { - "ogc": { - "ServiceExceptionReport": function(node, obj) { - obj.exceptionReport = {exceptions: []}; - this.readChildNodes(node, obj.exceptionReport); - }, - "ServiceException": function(node, exceptionReport) { - var exception = { - code: node.getAttribute("code"), - locator: node.getAttribute("locator"), - text: this.getChildValue(node) - }; - exceptionReport.exceptions.push(exception); - } - } - }, - - CLASS_NAME: "OpenLayers.Format.OGCExceptionReport" - -}); -/* ====================================================================== - OpenLayers/Format/XML/VersionedOGC.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/XML.js - * @requires OpenLayers/Format/OGCExceptionReport.js - */ - -/** - * Class: OpenLayers.Format.XML.VersionedOGC - * Base class for versioned formats, i.e. a format which supports multiple - * versions. - * - * To enable checking if parsing succeeded, you will need to define a property - * called errorProperty on the parser you want to check. The parser will then - * check the returned object to see if that property is present. If it is, it - * assumes the parsing was successful. If it is not present (or is null), it will - * pass the document through an OGCExceptionReport parser. - * - * If errorProperty is undefined for the parser, this error checking mechanism - * will be disabled. - * - * - * - * Inherits from: - * - <OpenLayers.Format.XML> - */ -OpenLayers.Format.XML.VersionedOGC = OpenLayers.Class(OpenLayers.Format.XML, { - - /** - * APIProperty: defaultVersion - * {String} Version number to assume if none found. - */ - defaultVersion: null, - - /** - * APIProperty: version - * {String} Specify a version string if one is known. - */ - version: null, - - /** - * APIProperty: profile - * {String} If provided, use a custom profile. - */ - profile: null, - - /** - * APIProperty: allowFallback - * {Boolean} If a profiled parser cannot be found for the returned version, - * use a non-profiled parser as the fallback. Application code using this - * should take into account that the return object structure might be - * missing the specifics of the profile. Defaults to false. - */ - allowFallback: false, - - /** - * Property: name - * {String} The name of this parser, this is the part of the CLASS_NAME - * except for "OpenLayers.Format." - */ - name: null, - - /** - * APIProperty: stringifyOutput - * {Boolean} If true, write will return a string otherwise a DOMElement. - * Default is false. - */ - stringifyOutput: false, - - /** - * Property: parser - * {Object} Instance of the versioned parser. Cached for multiple read and - * write calls of the same version. - */ - parser: null, - - /** - * Constructor: OpenLayers.Format.XML.VersionedOGC. - * Constructor. - * - * Parameters: - * options - {Object} Optional object whose properties will be set on - * the object. - */ - initialize: function(options) { - OpenLayers.Format.XML.prototype.initialize.apply(this, [options]); - var className = this.CLASS_NAME; - this.name = className.substring(className.lastIndexOf(".")+1); - }, - - /** - * Method: getVersion - * Returns the version to use. Subclasses can override this function - * if a different version detection is needed. - * - * Parameters: - * root - {DOMElement} - * options - {Object} Optional configuration object. - * - * Returns: - * {String} The version to use. - */ - getVersion: function(root, options) { - var version; - // read - if (root) { - version = this.version; - if(!version) { - version = root.getAttribute("version"); - if(!version) { - version = this.defaultVersion; - } - } - } else { // write - version = (options && options.version) || - this.version || this.defaultVersion; - } - return version; - }, - - /** - * Method: getParser - * Get an instance of the cached parser if available, otherwise create one. - * - * Parameters: - * version - {String} - * - * Returns: - * {<OpenLayers.Format>} - */ - getParser: function(version) { - version = version || this.defaultVersion; - var profile = this.profile ? "_" + this.profile : ""; - if(!this.parser || this.parser.VERSION != version) { - var format = OpenLayers.Format[this.name][ - "v" + version.replace(/\./g, "_") + profile - ]; - if(!format) { - if (profile !== "" && this.allowFallback) { - // fallback to the non-profiled version of the parser - profile = ""; - format = OpenLayers.Format[this.name][ - "v" + version.replace(/\./g, "_") - ]; - } - if (!format) { - throw "Can't find a " + this.name + " parser for version " + - version + profile; - } - } - this.parser = new format(this.options); - } - return this.parser; - }, - - /** - * APIMethod: write - * Write a document. - * - * Parameters: - * obj - {Object} An object representing the document. - * options - {Object} Optional configuration object. - * - * Returns: - * {String} The document as a string - */ - write: function(obj, options) { - var version = this.getVersion(null, options); - this.parser = this.getParser(version); - var root = this.parser.write(obj, options); - if (this.stringifyOutput === false) { - return root; - } else { - return OpenLayers.Format.XML.prototype.write.apply(this, [root]); - } - }, - - /** - * APIMethod: read - * Read a doc and return an object representing the document. - * - * Parameters: - * data - {String | DOMElement} Data to read. - * options - {Object} Options for the reader. - * - * Returns: - * {Object} An object representing the document. - */ - read: function(data, options) { - if(typeof data == "string") { - data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); - } - var root = data.documentElement; - var version = this.getVersion(root); - this.parser = this.getParser(version); // Select the parser - var obj = this.parser.read(data, options); // Parse the data - - var errorProperty = this.parser.errorProperty || null; - if (errorProperty !== null && obj[errorProperty] === undefined) { - // an error must have happened, so parse it and report back - var format = new OpenLayers.Format.OGCExceptionReport(); - obj.error = format.read(data); - } - obj.version = version; - return obj; - }, - - CLASS_NAME: "OpenLayers.Format.XML.VersionedOGC" -}); -/* ====================================================================== - 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/FeatureId.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.FeatureId - * This class represents a ogc:FeatureId Filter, as being used for rule-based SLD - * styling - * - * Inherits from: - * - <OpenLayers.Filter> - */ -OpenLayers.Filter.FeatureId = OpenLayers.Class(OpenLayers.Filter, { - - /** - * APIProperty: fids - * {Array(String)} Feature Ids to evaluate this rule against. - * To be passed inside the params object. - */ - fids: null, - - /** - * Property: type - * {String} Type to identify this filter. - */ - type: "FID", - - /** - * Constructor: OpenLayers.Filter.FeatureId - * Creates an ogc:FeatureId rule. - * - * Parameters: - * options - {Object} An optional object with properties to set on the - * rule - * - * Returns: - * {<OpenLayers.Filter.FeatureId>} - */ - initialize: function(options) { - this.fids = []; - OpenLayers.Filter.prototype.initialize.apply(this, [options]); - }, - - /** - * APIMethod: evaluate - * evaluates this rule for a specific feature - * - * Parameters: - * feature - {<OpenLayers.Feature>} feature to apply the rule to. - * For vector features, the check is run against the fid, - * for plain features against the id. - * - * Returns: - * {Boolean} true if the rule applies, false if it does not - */ - evaluate: function(feature) { - for (var i=0, len=this.fids.length; i<len; i++) { - var fid = feature.fid || feature.id; - if (fid == this.fids[i]) { - return true; - } - } - return false; - }, - - /** - * APIMethod: clone - * Clones this filter. - * - * Returns: - * {<OpenLayers.Filter.FeatureId>} Clone of this filter. - */ - clone: function() { - var filter = new OpenLayers.Filter.FeatureId(); - OpenLayers.Util.extend(filter, this); - filter.fids = this.fids.slice(); - return filter; - }, - - CLASS_NAME: "OpenLayers.Filter.FeatureId" -}); -/* ====================================================================== - 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/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/Format/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/Format/XML/VersionedOGC.js - * @requires OpenLayers/Filter/FeatureId.js - * @requires OpenLayers/Filter/Logical.js - * @requires OpenLayers/Filter/Comparison.js - */ - -/** - * Class: OpenLayers.Format.Filter - * Read/Write ogc:Filter. Create a new instance with the <OpenLayers.Format.Filter> - * constructor. - * - * Inherits from: - * - <OpenLayers.Format.XML.VersionedOGC> - */ -OpenLayers.Format.Filter = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, { - - /** - * APIProperty: defaultVersion - * {String} Version number to assume if none found. Default is "1.0.0". - */ - defaultVersion: "1.0.0", - - /** - * APIMethod: write - * Write an ogc:Filter given a filter object. - * - * Parameters: - * filter - {<OpenLayers.Filter>} An filter. - * options - {Object} Optional configuration object. - * - * Returns: - * {Elment} An ogc:Filter element node. - */ - - /** - * APIMethod: read - * Read and Filter doc and return an object representing the Filter. - * - * Parameters: - * data - {String | DOMElement} Data to read. - * - * Returns: - * {<OpenLayers.Filter>} A filter object. - */ - - CLASS_NAME: "OpenLayers.Format.Filter" -}); -/* ====================================================================== - OpenLayers/Format/WFST.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.js - */ - -/** - * Function: OpenLayers.Format.WFST - * Used to create a versioned WFS protocol. Default version is 1.0.0. - * - * Returns: - * {<OpenLayers.Format>} A WFST format of the given version. - */ -OpenLayers.Format.WFST = function(options) { - options = OpenLayers.Util.applyDefaults( - options, OpenLayers.Format.WFST.DEFAULTS - ); - var cls = OpenLayers.Format.WFST["v"+options.version.replace(/\./g, "_")]; - if(!cls) { - throw "Unsupported WFST version: " + options.version; - } - return new cls(options); -}; - -/** - * Constant: OpenLayers.Format.WFST.DEFAULTS - * {Object} Default properties for the WFST format. - */ -OpenLayers.Format.WFST.DEFAULTS = { - "version": "1.0.0" -}; -/* ====================================================================== - 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/Format/WFST/v1.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/XML.js - * @requires OpenLayers/Format/WFST.js - * @requires OpenLayers/Filter/Spatial.js - * @requires OpenLayers/Filter/FeatureId.js - */ - -/** - * Class: OpenLayers.Format.WFST.v1 - * Superclass for WFST parsers. - * - * Inherits from: - * - <OpenLayers.Format.XML> - */ -OpenLayers.Format.WFST.v1 = OpenLayers.Class(OpenLayers.Format.XML, { - - /** - * Property: namespaces - * {Object} Mapping of namespace aliases to namespace URIs. - */ - namespaces: { - xlink: "http://www.w3.org/1999/xlink", - xsi: "http://www.w3.org/2001/XMLSchema-instance", - wfs: "http://www.opengis.net/wfs", - gml: "http://www.opengis.net/gml", - ogc: "http://www.opengis.net/ogc", - ows: "http://www.opengis.net/ows" - }, - - /** - * Property: defaultPrefix - */ - defaultPrefix: "wfs", - - /** - * Property: version - * {String} WFS version number. - */ - version: null, - - /** - * Property: schemaLocation - * {String} Schema location for a particular minor version. - */ - schemaLocations: null, - - /** - * APIProperty: srsName - * {String} URI for spatial reference system. - */ - srsName: null, - - /** - * APIProperty: extractAttributes - * {Boolean} Extract attributes from GML. Default is true. - */ - extractAttributes: true, - - /** - * APIProperty: xy - * {Boolean} Order of the GML coordinate true:(x,y) or false:(y,x) - * Changing is not recommended, a new Format should be instantiated. - */ - xy: true, - - /** - * Property: stateName - * {Object} Maps feature states to node names. - */ - stateName: null, - - /** - * Constructor: OpenLayers.Format.WFST.v1 - * Instances of this class are not created directly. Use the - * <OpenLayers.Format.WFST.v1_0_0> or <OpenLayers.Format.WFST.v1_1_0> - * constructor instead. - * - * Parameters: - * options - {Object} An optional object whose properties will be set on - * this instance. - */ - initialize: function(options) { - // set state name mapping - this.stateName = {}; - this.stateName[OpenLayers.State.INSERT] = "wfs:Insert"; - this.stateName[OpenLayers.State.UPDATE] = "wfs:Update"; - this.stateName[OpenLayers.State.DELETE] = "wfs:Delete"; - OpenLayers.Format.XML.prototype.initialize.apply(this, [options]); - }, - - /** - * Method: getSrsName - */ - getSrsName: function(feature, options) { - var srsName = options && options.srsName; - if(!srsName) { - if(feature && feature.layer) { - srsName = feature.layer.projection.getCode(); - } else { - srsName = this.srsName; - } - } - return srsName; - }, - - /** - * APIMethod: read - * Parse the response from a transaction. Because WFS is split into - * Transaction requests (create, update, and delete) and GetFeature - * requests (read), this method handles parsing of both types of - * responses. - * - * Parameters: - * data - {String | Document} The WFST document to read - * options - {Object} Options for the reader - * - * Valid options properties: - * output - {String} either "features" or "object". The default is - * "features", which means that the method will return an array of - * features. If set to "object", an object with a "features" property - * and other properties read by the parser will be returned. - * - * Returns: - * {Array | Object} Output depending on the output option. - */ - read: function(data, options) { - options = options || {}; - OpenLayers.Util.applyDefaults(options, { - output: "features" - }); - - if(typeof data == "string") { - data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); - } - if(data && data.nodeType == 9) { - data = data.documentElement; - } - var obj = {}; - if(data) { - this.readNode(data, obj, true); - } - if(obj.features && options.output === "features") { - obj = obj.features; - } - return obj; - }, - - /** - * Property: readers - * Contains public functions, grouped by namespace prefix, that will - * be applied when a namespaced node is found matching the function - * name. The function will be applied in the scope of this parser - * with two arguments: the node being read and a context object passed - * from the parent. - */ - readers: { - "wfs": { - "FeatureCollection": function(node, obj) { - obj.features = []; - this.readChildNodes(node, obj); - } - } - }, - - /** - * Method: write - * Given an array of features, write a WFS transaction. This assumes - * the features have a state property that determines the operation - * type - insert, update, or delete. - * - * Parameters: - * features - {Array(<OpenLayers.Feature.Vector>)} A list of features. See - * below for a more detailed description of the influence of the - * feature's *modified* property. - * options - {Object} - * - * feature.modified rules: - * If a feature has a modified property set, the following checks will be - * made before a feature's geometry or attribute is included in an Update - * transaction: - * - *modified* is not set at all: The geometry and all attributes will be - * included. - * - *modified.geometry* is set (null or a geometry): The geometry will be - * included. If *modified.attributes* is not set, all attributes will - * be included. - * - *modified.attributes* is set: Only the attributes set (i.e. to null or - * a value) in *modified.attributes* will be included. - * If *modified.geometry* is not set, the geometry will not be included. - * - * Valid options include: - * - *multi* {Boolean} If set to true, geometries will be casted to - * Multi geometries before writing. - * - * Returns: - * {String} A serialized WFS transaction. - */ - write: function(features, options) { - var node = this.writeNode("wfs:Transaction", { - features:features, - options: options - }); - var value = this.schemaLocationAttr(); - if(value) { - this.setAttributeNS( - node, this.namespaces["xsi"], "xsi:schemaLocation", value - ); - } - return OpenLayers.Format.XML.prototype.write.apply(this, [node]); - }, - - /** - * Property: writers - * As a compliment to the readers property, this structure contains public - * writing functions grouped by namespace alias and named like the - * node names they produce. - */ - writers: { - "wfs": { - "GetFeature": function(options) { - var node = this.createElementNSPlus("wfs:GetFeature", { - attributes: { - service: "WFS", - version: this.version, - handle: options && options.handle, - outputFormat: options && options.outputFormat, - maxFeatures: options && options.maxFeatures, - "xsi:schemaLocation": this.schemaLocationAttr(options) - } - }); - if (typeof this.featureType == "string") { - this.writeNode("Query", options, node); - } else { - for (var i=0,len = this.featureType.length; i<len; i++) { - options.featureType = this.featureType[i]; - this.writeNode("Query", options, node); - } - } - return node; - }, - "Transaction": function(obj) { - obj = obj || {}; - var options = obj.options || {}; - var node = this.createElementNSPlus("wfs:Transaction", { - attributes: { - service: "WFS", - version: this.version, - handle: options.handle - } - }); - var i, len; - var features = obj.features; - if(features) { - // temporarily re-assigning geometry types - if (options.multi === true) { - OpenLayers.Util.extend(this.geometryTypes, { - "OpenLayers.Geometry.Point": "MultiPoint", - "OpenLayers.Geometry.LineString": (this.multiCurve === true) ? "MultiCurve": "MultiLineString", - "OpenLayers.Geometry.Polygon": (this.multiSurface === true) ? "MultiSurface" : "MultiPolygon" - }); - } - var name, feature; - for(i=0, len=features.length; i<len; ++i) { - feature = features[i]; - name = this.stateName[feature.state]; - if(name) { - this.writeNode(name, { - feature: feature, - options: options - }, node); - } - } - // switch back to original geometry types assignment - if (options.multi === true) { - this.setGeometryTypes(); - } - } - if (options.nativeElements) { - for (i=0, len=options.nativeElements.length; i<len; ++i) { - this.writeNode("wfs:Native", - options.nativeElements[i], node); - } - } - return node; - }, - "Native": function(nativeElement) { - var node = this.createElementNSPlus("wfs:Native", { - attributes: { - vendorId: nativeElement.vendorId, - safeToIgnore: nativeElement.safeToIgnore - }, - value: nativeElement.value - }); - return node; - }, - "Insert": function(obj) { - var feature = obj.feature; - var options = obj.options; - var node = this.createElementNSPlus("wfs:Insert", { - attributes: { - handle: options && options.handle - } - }); - this.srsName = this.getSrsName(feature); - this.writeNode("feature:_typeName", feature, node); - return node; - }, - "Update": function(obj) { - var feature = obj.feature; - var options = obj.options; - var node = this.createElementNSPlus("wfs:Update", { - attributes: { - handle: options && options.handle, - typeName: (this.featureNS ? this.featurePrefix + ":" : "") + - this.featureType - } - }); - if(this.featureNS) { - node.setAttribute("xmlns:" + this.featurePrefix, this.featureNS); - } - - // add in geometry - var modified = feature.modified; - if (this.geometryName !== null && (!modified || modified.geometry !== undefined)) { - this.srsName = this.getSrsName(feature); - this.writeNode( - "Property", {name: this.geometryName, value: feature.geometry}, node - ); - } - - // add in attributes - for(var key in feature.attributes) { - if(feature.attributes[key] !== undefined && - (!modified || !modified.attributes || - (modified.attributes && modified.attributes[key] !== undefined))) { - this.writeNode( - "Property", {name: key, value: feature.attributes[key]}, node - ); - } - } - - // add feature id filter - this.writeNode("ogc:Filter", new OpenLayers.Filter.FeatureId({ - fids: [feature.fid] - }), node); - - return node; - }, - "Property": function(obj) { - var node = this.createElementNSPlus("wfs:Property"); - this.writeNode("Name", obj.name, node); - if(obj.value !== null) { - this.writeNode("Value", obj.value, node); - } - return node; - }, - "Name": function(name) { - return this.createElementNSPlus("wfs:Name", {value: name}); - }, - "Value": function(obj) { - var node; - if(obj instanceof OpenLayers.Geometry) { - node = this.createElementNSPlus("wfs:Value"); - var geom = this.writeNode("feature:_geometry", obj).firstChild; - node.appendChild(geom); - } else { - node = this.createElementNSPlus("wfs:Value", {value: obj}); - } - return node; - }, - "Delete": function(obj) { - var feature = obj.feature; - var options = obj.options; - var node = this.createElementNSPlus("wfs:Delete", { - attributes: { - handle: options && options.handle, - typeName: (this.featureNS ? this.featurePrefix + ":" : "") + - this.featureType - } - }); - if(this.featureNS) { - node.setAttribute("xmlns:" + this.featurePrefix, this.featureNS); - } - this.writeNode("ogc:Filter", new OpenLayers.Filter.FeatureId({ - fids: [feature.fid] - }), node); - return node; - } - } - }, - - /** - * Method: schemaLocationAttr - * Generate the xsi:schemaLocation attribute value. - * - * Returns: - * {String} The xsi:schemaLocation attribute or undefined if none. - */ - schemaLocationAttr: function(options) { - options = OpenLayers.Util.extend({ - featurePrefix: this.featurePrefix, - schema: this.schema - }, options); - var schemaLocations = OpenLayers.Util.extend({}, this.schemaLocations); - if(options.schema) { - schemaLocations[options.featurePrefix] = options.schema; - } - var parts = []; - var uri; - for(var key in schemaLocations) { - uri = this.namespaces[key]; - if(uri) { - parts.push(uri + " " + schemaLocations[key]); - } - } - var value = parts.join(" ") || undefined; - return value; - }, - - /** - * Method: setFilterProperty - * Set the property of each spatial filter. - * - * Parameters: - * filter - {<OpenLayers.Filter>} - */ - setFilterProperty: function(filter) { - if(filter.filters) { - for(var i=0, len=filter.filters.length; i<len; ++i) { - OpenLayers.Format.WFST.v1.prototype.setFilterProperty.call(this, filter.filters[i]); - } - } else { - if(filter instanceof OpenLayers.Filter.Spatial && !filter.property) { - // got a spatial filter without property, so set it - filter.property = this.geometryName; - } - } - }, - - CLASS_NAME: "OpenLayers.Format.WFST.v1" - -}); -/* ====================================================================== - 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/Format/GML.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/XML.js - * @requires OpenLayers/Feature/Vector.js - * @requires OpenLayers/Geometry/Point.js - * @requires OpenLayers/Geometry/MultiPoint.js - * @requires OpenLayers/Geometry/LineString.js - * @requires OpenLayers/Geometry/MultiLineString.js - * @requires OpenLayers/Geometry/Polygon.js - * @requires OpenLayers/Geometry/MultiPolygon.js - */ - -/** - * Class: OpenLayers.Format.GML - * Read/Write GML. Create a new instance with the <OpenLayers.Format.GML> - * constructor. Supports the GML simple features profile. - * - * Inherits from: - * - <OpenLayers.Format.XML> - */ -OpenLayers.Format.GML = OpenLayers.Class(OpenLayers.Format.XML, { - - /** - * APIProperty: featureNS - * {String} Namespace used for feature attributes. Default is - * "http://mapserver.gis.umn.edu/mapserver". - */ - featureNS: "http://mapserver.gis.umn.edu/mapserver", - - /** - * APIProperty: featurePrefix - * {String} Namespace alias (or prefix) for feature nodes. Default is - * "feature". - */ - featurePrefix: "feature", - - /** - * APIProperty: featureName - * {String} Element name for features. Default is "featureMember". - */ - featureName: "featureMember", - - /** - * APIProperty: layerName - * {String} Name of data layer. Default is "features". - */ - layerName: "features", - - /** - * APIProperty: geometryName - * {String} Name of geometry element. Defaults to "geometry". - */ - geometryName: "geometry", - - /** - * APIProperty: collectionName - * {String} Name of featureCollection element. - */ - collectionName: "FeatureCollection", - - /** - * APIProperty: gmlns - * {String} GML Namespace. - */ - gmlns: "http://www.opengis.net/gml", - - /** - * APIProperty: extractAttributes - * {Boolean} Extract attributes from GML. - */ - extractAttributes: true, - - /** - * APIProperty: xy - * {Boolean} Order of the GML coordinate true:(x,y) or false:(y,x) - * Changing is not recommended, a new Format should be instantiated. - */ - xy: true, - - /** - * Constructor: OpenLayers.Format.GML - * Create a new parser for GML. - * - * Parameters: - * options - {Object} An optional object whose properties will be set on - * this instance. - */ - initialize: function(options) { - // compile regular expressions once instead of every time they are used - this.regExes = { - trimSpace: (/^\s*|\s*$/g), - removeSpace: (/\s*/g), - splitSpace: (/\s+/), - trimComma: (/\s*,\s*/g) - }; - OpenLayers.Format.XML.prototype.initialize.apply(this, [options]); - }, - - /** - * APIMethod: read - * Read data from a string, and return a list of features. - * - * Parameters: - * data - {String} or {DOMElement} data to read/parse. - * - * Returns: - * {Array(<OpenLayers.Feature.Vector>)} An array of features. - */ - read: function(data) { - if(typeof data == "string") { - data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); - } - var featureNodes = this.getElementsByTagNameNS(data.documentElement, - this.gmlns, - this.featureName); - var features = []; - for(var i=0; i<featureNodes.length; i++) { - var feature = this.parseFeature(featureNodes[i]); - if(feature) { - features.push(feature); - } - } - return features; - }, - - /** - * Method: parseFeature - * This function is the core of the GML parsing code in OpenLayers. - * It creates the geometries that are then attached to the returned - * feature, and calls parseAttributes() to get attribute data out. - * - * Parameters: - * node - {DOMElement} A GML feature node. - */ - parseFeature: function(node) { - // only accept one geometry per feature - look for highest "order" - var order = ["MultiPolygon", "Polygon", - "MultiLineString", "LineString", - "MultiPoint", "Point", "Envelope"]; - // FIXME: In case we parse a feature with no geometry, but boundedBy an Envelope, - // this code creates a geometry derived from the Envelope. This is not correct. - var type, nodeList, geometry, parser; - for(var i=0; i<order.length; ++i) { - type = order[i]; - nodeList = this.getElementsByTagNameNS(node, this.gmlns, type); - if(nodeList.length > 0) { - // only deal with first geometry of this type - parser = this.parseGeometry[type.toLowerCase()]; - if(parser) { - geometry = parser.apply(this, [nodeList[0]]); - if (this.internalProjection && this.externalProjection) { - geometry.transform(this.externalProjection, - this.internalProjection); - } - } else { - throw new TypeError("Unsupported geometry type: " + type); - } - // stop looking for different geometry types - break; - } - } - - var bounds; - var boxNodes = this.getElementsByTagNameNS(node, this.gmlns, "Box"); - for(i=0; i<boxNodes.length; ++i) { - var boxNode = boxNodes[i]; - var box = this.parseGeometry["box"].apply(this, [boxNode]); - var parentNode = boxNode.parentNode; - var parentName = parentNode.localName || - parentNode.nodeName.split(":").pop(); - if(parentName === "boundedBy") { - bounds = box; - } else { - geometry = box.toGeometry(); - } - } - - // construct feature (optionally with attributes) - var attributes; - if(this.extractAttributes) { - attributes = this.parseAttributes(node); - } - var feature = new OpenLayers.Feature.Vector(geometry, attributes); - feature.bounds = bounds; - - feature.gml = { - featureType: node.firstChild.nodeName.split(":")[1], - featureNS: node.firstChild.namespaceURI, - featureNSPrefix: node.firstChild.prefix - }; - - // assign fid - this can come from a "fid" or "id" attribute - var childNode = node.firstChild; - var fid; - while(childNode) { - if(childNode.nodeType == 1) { - fid = childNode.getAttribute("fid") || - childNode.getAttribute("id"); - if(fid) { - break; - } - } - childNode = childNode.nextSibling; - } - feature.fid = fid; - return feature; - }, - - /** - * Property: parseGeometry - * Properties of this object are the functions that parse geometries based - * on their type. - */ - parseGeometry: { - - /** - * Method: parseGeometry.point - * Given a GML node representing a point geometry, create an OpenLayers - * point geometry. - * - * Parameters: - * node - {DOMElement} A GML node. - * - * Returns: - * {<OpenLayers.Geometry.Point>} A point geometry. - */ - point: function(node) { - /** - * Three coordinate variations to consider: - * 1) <gml:pos>x y z</gml:pos> - * 2) <gml:coordinates>x, y, z</gml:coordinates> - * 3) <gml:coord><gml:X>x</gml:X><gml:Y>y</gml:Y></gml:coord> - */ - var nodeList, coordString; - var coords = []; - - // look for <gml:pos> - var nodeList = this.getElementsByTagNameNS(node, this.gmlns, "pos"); - if(nodeList.length > 0) { - coordString = nodeList[0].firstChild.nodeValue; - coordString = coordString.replace(this.regExes.trimSpace, ""); - coords = coordString.split(this.regExes.splitSpace); - } - - // look for <gml:coordinates> - if(coords.length == 0) { - nodeList = this.getElementsByTagNameNS(node, this.gmlns, - "coordinates"); - if(nodeList.length > 0) { - coordString = nodeList[0].firstChild.nodeValue; - coordString = coordString.replace(this.regExes.removeSpace, - ""); - coords = coordString.split(","); - } - } - - // look for <gml:coord> - if(coords.length == 0) { - nodeList = this.getElementsByTagNameNS(node, this.gmlns, - "coord"); - if(nodeList.length > 0) { - var xList = this.getElementsByTagNameNS(nodeList[0], - this.gmlns, "X"); - var yList = this.getElementsByTagNameNS(nodeList[0], - this.gmlns, "Y"); - if(xList.length > 0 && yList.length > 0) { - coords = [xList[0].firstChild.nodeValue, - yList[0].firstChild.nodeValue]; - } - } - } - - // preserve third dimension - if(coords.length == 2) { - coords[2] = null; - } - - if (this.xy) { - return new OpenLayers.Geometry.Point(coords[0], coords[1], - coords[2]); - } - else{ - return new OpenLayers.Geometry.Point(coords[1], coords[0], - coords[2]); - } - }, - - /** - * Method: parseGeometry.multipoint - * Given a GML node representing a multipoint geometry, create an - * OpenLayers multipoint geometry. - * - * Parameters: - * node - {DOMElement} A GML node. - * - * Returns: - * {<OpenLayers.Geometry.MultiPoint>} A multipoint geometry. - */ - multipoint: function(node) { - var nodeList = this.getElementsByTagNameNS(node, this.gmlns, - "Point"); - var components = []; - if(nodeList.length > 0) { - var point; - for(var i=0; i<nodeList.length; ++i) { - point = this.parseGeometry.point.apply(this, [nodeList[i]]); - if(point) { - components.push(point); - } - } - } - return new OpenLayers.Geometry.MultiPoint(components); - }, - - /** - * Method: parseGeometry.linestring - * Given a GML node representing a linestring geometry, create an - * OpenLayers linestring geometry. - * - * Parameters: - * node - {DOMElement} A GML node. - * - * Returns: - * {<OpenLayers.Geometry.LineString>} A linestring geometry. - */ - linestring: function(node, ring) { - /** - * Two coordinate variations to consider: - * 1) <gml:posList dimension="d">x0 y0 z0 x1 y1 z1</gml:posList> - * 2) <gml:coordinates>x0, y0, z0 x1, y1, z1</gml:coordinates> - */ - var nodeList, coordString; - var coords = []; - var points = []; - - // look for <gml:posList> - nodeList = this.getElementsByTagNameNS(node, this.gmlns, "posList"); - if(nodeList.length > 0) { - coordString = this.getChildValue(nodeList[0]); - coordString = coordString.replace(this.regExes.trimSpace, ""); - coords = coordString.split(this.regExes.splitSpace); - var dim = parseInt(nodeList[0].getAttribute("dimension")); - var j, x, y, z; - for(var i=0; i<coords.length/dim; ++i) { - j = i * dim; - x = coords[j]; - y = coords[j+1]; - z = (dim == 2) ? null : coords[j+2]; - if (this.xy) { - points.push(new OpenLayers.Geometry.Point(x, y, z)); - } else { - points.push(new OpenLayers.Geometry.Point(y, x, z)); - } - } - } - - // look for <gml:coordinates> - if(coords.length == 0) { - nodeList = this.getElementsByTagNameNS(node, this.gmlns, - "coordinates"); - if(nodeList.length > 0) { - coordString = this.getChildValue(nodeList[0]); - coordString = coordString.replace(this.regExes.trimSpace, - ""); - coordString = coordString.replace(this.regExes.trimComma, - ","); - var pointList = coordString.split(this.regExes.splitSpace); - for(var i=0; i<pointList.length; ++i) { - coords = pointList[i].split(","); - if(coords.length == 2) { - coords[2] = null; - } - if (this.xy) { - points.push(new OpenLayers.Geometry.Point(coords[0], - coords[1], - coords[2])); - } else { - points.push(new OpenLayers.Geometry.Point(coords[1], - coords[0], - coords[2])); - } - } - } - } - - var line = null; - if(points.length != 0) { - if(ring) { - line = new OpenLayers.Geometry.LinearRing(points); - } else { - line = new OpenLayers.Geometry.LineString(points); - } - } - return line; - }, - - /** - * Method: parseGeometry.multilinestring - * Given a GML node representing a multilinestring geometry, create an - * OpenLayers multilinestring geometry. - * - * Parameters: - * node - {DOMElement} A GML node. - * - * Returns: - * {<OpenLayers.Geometry.MultiLineString>} A multilinestring geometry. - */ - multilinestring: function(node) { - var nodeList = this.getElementsByTagNameNS(node, this.gmlns, - "LineString"); - var components = []; - if(nodeList.length > 0) { - var line; - for(var i=0; i<nodeList.length; ++i) { - line = this.parseGeometry.linestring.apply(this, - [nodeList[i]]); - if(line) { - components.push(line); - } - } - } - return new OpenLayers.Geometry.MultiLineString(components); - }, - - /** - * Method: parseGeometry.polygon - * Given a GML node representing a polygon geometry, create an - * OpenLayers polygon geometry. - * - * Parameters: - * node - {DOMElement} A GML node. - * - * Returns: - * {<OpenLayers.Geometry.Polygon>} A polygon geometry. - */ - polygon: function(node) { - var nodeList = this.getElementsByTagNameNS(node, this.gmlns, - "LinearRing"); - var components = []; - if(nodeList.length > 0) { - // this assumes exterior ring first, inner rings after - var ring; - for(var i=0; i<nodeList.length; ++i) { - ring = this.parseGeometry.linestring.apply(this, - [nodeList[i], true]); - if(ring) { - components.push(ring); - } - } - } - return new OpenLayers.Geometry.Polygon(components); - }, - - /** - * Method: parseGeometry.multipolygon - * Given a GML node representing a multipolygon geometry, create an - * OpenLayers multipolygon geometry. - * - * Parameters: - * node - {DOMElement} A GML node. - * - * Returns: - * {<OpenLayers.Geometry.MultiPolygon>} A multipolygon geometry. - */ - multipolygon: function(node) { - var nodeList = this.getElementsByTagNameNS(node, this.gmlns, - "Polygon"); - var components = []; - if(nodeList.length > 0) { - var polygon; - for(var i=0; i<nodeList.length; ++i) { - polygon = this.parseGeometry.polygon.apply(this, - [nodeList[i]]); - if(polygon) { - components.push(polygon); - } - } - } - return new OpenLayers.Geometry.MultiPolygon(components); - }, - - envelope: function(node) { - var components = []; - var coordString; - var envelope; - - var lpoint = this.getElementsByTagNameNS(node, this.gmlns, "lowerCorner"); - if (lpoint.length > 0) { - var coords = []; - - if(lpoint.length > 0) { - coordString = lpoint[0].firstChild.nodeValue; - coordString = coordString.replace(this.regExes.trimSpace, ""); - coords = coordString.split(this.regExes.splitSpace); - } - - if(coords.length == 2) { - coords[2] = null; - } - if (this.xy) { - var lowerPoint = new OpenLayers.Geometry.Point(coords[0], coords[1],coords[2]); - } else { - var lowerPoint = new OpenLayers.Geometry.Point(coords[1], coords[0],coords[2]); - } - } - - var upoint = this.getElementsByTagNameNS(node, this.gmlns, "upperCorner"); - if (upoint.length > 0) { - var coords = []; - - if(upoint.length > 0) { - coordString = upoint[0].firstChild.nodeValue; - coordString = coordString.replace(this.regExes.trimSpace, ""); - coords = coordString.split(this.regExes.splitSpace); - } - - if(coords.length == 2) { - coords[2] = null; - } - if (this.xy) { - var upperPoint = new OpenLayers.Geometry.Point(coords[0], coords[1],coords[2]); - } else { - var upperPoint = new OpenLayers.Geometry.Point(coords[1], coords[0],coords[2]); - } - } - - if (lowerPoint && upperPoint) { - components.push(new OpenLayers.Geometry.Point(lowerPoint.x, lowerPoint.y)); - components.push(new OpenLayers.Geometry.Point(upperPoint.x, lowerPoint.y)); - components.push(new OpenLayers.Geometry.Point(upperPoint.x, upperPoint.y)); - components.push(new OpenLayers.Geometry.Point(lowerPoint.x, upperPoint.y)); - components.push(new OpenLayers.Geometry.Point(lowerPoint.x, lowerPoint.y)); - - var ring = new OpenLayers.Geometry.LinearRing(components); - envelope = new OpenLayers.Geometry.Polygon([ring]); - } - return envelope; - }, - - /** - * Method: parseGeometry.box - * Given a GML node representing a box geometry, create an - * OpenLayers.Bounds. - * - * Parameters: - * node - {DOMElement} A GML node. - * - * Returns: - * {<OpenLayers.Bounds>} A bounds representing the box. - */ - box: function(node) { - var nodeList = this.getElementsByTagNameNS(node, this.gmlns, - "coordinates"); - var coordString; - var coords, beginPoint = null, endPoint = null; - if (nodeList.length > 0) { - coordString = nodeList[0].firstChild.nodeValue; - coords = coordString.split(" "); - if (coords.length == 2) { - beginPoint = coords[0].split(","); - endPoint = coords[1].split(","); - } - } - if (beginPoint !== null && endPoint !== null) { - return new OpenLayers.Bounds(parseFloat(beginPoint[0]), - parseFloat(beginPoint[1]), - parseFloat(endPoint[0]), - parseFloat(endPoint[1]) ); - } - } - - }, - - /** - * Method: parseAttributes - * - * Parameters: - * node - {DOMElement} - * - * Returns: - * {Object} An attributes object. - */ - parseAttributes: function(node) { - var attributes = {}; - // assume attributes are children of the first type 1 child - var childNode = node.firstChild; - var children, i, child, grandchildren, grandchild, name, value; - while(childNode) { - if(childNode.nodeType == 1) { - // attributes are type 1 children with one type 3 child - children = childNode.childNodes; - for(i=0; i<children.length; ++i) { - child = children[i]; - if(child.nodeType == 1) { - grandchildren = child.childNodes; - if(grandchildren.length == 1) { - grandchild = grandchildren[0]; - if(grandchild.nodeType == 3 || - grandchild.nodeType == 4) { - name = (child.prefix) ? - child.nodeName.split(":")[1] : - child.nodeName; - value = grandchild.nodeValue.replace( - this.regExes.trimSpace, ""); - attributes[name] = value; - } - } else { - // If child has no childNodes (grandchildren), - // set an attribute with null value. - // e.g. <prefix:fieldname/> becomes - // {fieldname: null} - attributes[child.nodeName.split(":").pop()] = null; - } - } - } - break; - } - childNode = childNode.nextSibling; - } - return attributes; - }, - - /** - * APIMethod: write - * Generate a GML document string given a list of features. - * - * Parameters: - * features - {Array(<OpenLayers.Feature.Vector>)} List of features to - * serialize into a string. - * - * Returns: - * {String} A string representing the GML document. - */ - write: function(features) { - if(!(OpenLayers.Util.isArray(features))) { - features = [features]; - } - var gml = this.createElementNS("http://www.opengis.net/wfs", - "wfs:" + this.collectionName); - for(var i=0; i<features.length; i++) { - gml.appendChild(this.createFeatureXML(features[i])); - } - return OpenLayers.Format.XML.prototype.write.apply(this, [gml]); - }, - - /** - * Method: createFeatureXML - * Accept an OpenLayers.Feature.Vector, and build a GML node for it. - * - * Parameters: - * feature - {<OpenLayers.Feature.Vector>} The feature to be built as GML. - * - * Returns: - * {DOMElement} A node reprensting the feature in GML. - */ - createFeatureXML: function(feature) { - var geometry = feature.geometry; - var geometryNode = this.buildGeometryNode(geometry); - var geomContainer = this.createElementNS(this.featureNS, - this.featurePrefix + ":" + - this.geometryName); - geomContainer.appendChild(geometryNode); - var featureNode = this.createElementNS(this.gmlns, - "gml:" + this.featureName); - var featureContainer = this.createElementNS(this.featureNS, - this.featurePrefix + ":" + - this.layerName); - var fid = feature.fid || feature.id; - featureContainer.setAttribute("fid", fid); - featureContainer.appendChild(geomContainer); - for(var attr in feature.attributes) { - var attrText = this.createTextNode(feature.attributes[attr]); - var nodename = attr.substring(attr.lastIndexOf(":") + 1); - var attrContainer = this.createElementNS(this.featureNS, - this.featurePrefix + ":" + - nodename); - attrContainer.appendChild(attrText); - featureContainer.appendChild(attrContainer); - } - featureNode.appendChild(featureContainer); - return featureNode; - }, - - /** - * APIMethod: buildGeometryNode - */ - buildGeometryNode: function(geometry) { - if (this.externalProjection && this.internalProjection) { - geometry = geometry.clone(); - geometry.transform(this.internalProjection, - this.externalProjection); - } - var className = geometry.CLASS_NAME; - var type = className.substring(className.lastIndexOf(".") + 1); - var builder = this.buildGeometry[type.toLowerCase()]; - return builder.apply(this, [geometry]); - }, - - /** - * Property: buildGeometry - * Object containing methods to do the actual geometry node building - * based on geometry type. - */ - buildGeometry: { - // TBD retrieve the srs from layer - // srsName is non-standard, so not including it until it's right. - // gml.setAttribute("srsName", - // "http://www.opengis.net/gml/srs/epsg.xml#4326"); - - /** - * Method: buildGeometry.point - * Given an OpenLayers point geometry, create a GML point. - * - * Parameters: - * geometry - {<OpenLayers.Geometry.Point>} A point geometry. - * - * Returns: - * {DOMElement} A GML point node. - */ - point: function(geometry) { - var gml = this.createElementNS(this.gmlns, "gml:Point"); - gml.appendChild(this.buildCoordinatesNode(geometry)); - return gml; - }, - - /** - * Method: buildGeometry.multipoint - * Given an OpenLayers multipoint geometry, create a GML multipoint. - * - * Parameters: - * geometry - {<OpenLayers.Geometry.MultiPoint>} A multipoint geometry. - * - * Returns: - * {DOMElement} A GML multipoint node. - */ - multipoint: function(geometry) { - var gml = this.createElementNS(this.gmlns, "gml:MultiPoint"); - var points = geometry.components; - var pointMember, pointGeom; - for(var i=0; i<points.length; i++) { - pointMember = this.createElementNS(this.gmlns, - "gml:pointMember"); - pointGeom = this.buildGeometry.point.apply(this, - [points[i]]); - pointMember.appendChild(pointGeom); - gml.appendChild(pointMember); - } - return gml; - }, - - /** - * Method: buildGeometry.linestring - * Given an OpenLayers linestring geometry, create a GML linestring. - * - * Parameters: - * geometry - {<OpenLayers.Geometry.LineString>} A linestring geometry. - * - * Returns: - * {DOMElement} A GML linestring node. - */ - linestring: function(geometry) { - var gml = this.createElementNS(this.gmlns, "gml:LineString"); - gml.appendChild(this.buildCoordinatesNode(geometry)); - return gml; - }, - - /** - * Method: buildGeometry.multilinestring - * Given an OpenLayers multilinestring geometry, create a GML - * multilinestring. - * - * Parameters: - * geometry - {<OpenLayers.Geometry.MultiLineString>} A multilinestring - * geometry. - * - * Returns: - * {DOMElement} A GML multilinestring node. - */ - multilinestring: function(geometry) { - var gml = this.createElementNS(this.gmlns, "gml:MultiLineString"); - var lines = geometry.components; - var lineMember, lineGeom; - for(var i=0; i<lines.length; ++i) { - lineMember = this.createElementNS(this.gmlns, - "gml:lineStringMember"); - lineGeom = this.buildGeometry.linestring.apply(this, - [lines[i]]); - lineMember.appendChild(lineGeom); - gml.appendChild(lineMember); - } - return gml; - }, - - /** - * Method: buildGeometry.linearring - * Given an OpenLayers linearring geometry, create a GML linearring. - * - * Parameters: - * geometry - {<OpenLayers.Geometry.LinearRing>} A linearring geometry. - * - * Returns: - * {DOMElement} A GML linearring node. - */ - linearring: function(geometry) { - var gml = this.createElementNS(this.gmlns, "gml:LinearRing"); - gml.appendChild(this.buildCoordinatesNode(geometry)); - return gml; - }, - - /** - * Method: buildGeometry.polygon - * Given an OpenLayers polygon geometry, create a GML polygon. - * - * Parameters: - * geometry - {<OpenLayers.Geometry.Polygon>} A polygon geometry. - * - * Returns: - * {DOMElement} A GML polygon node. - */ - polygon: function(geometry) { - var gml = this.createElementNS(this.gmlns, "gml:Polygon"); - var rings = geometry.components; - var ringMember, ringGeom, type; - for(var i=0; i<rings.length; ++i) { - type = (i==0) ? "outerBoundaryIs" : "innerBoundaryIs"; - ringMember = this.createElementNS(this.gmlns, - "gml:" + type); - ringGeom = this.buildGeometry.linearring.apply(this, - [rings[i]]); - ringMember.appendChild(ringGeom); - gml.appendChild(ringMember); - } - return gml; - }, - - /** - * Method: buildGeometry.multipolygon - * Given an OpenLayers multipolygon geometry, create a GML multipolygon. - * - * Parameters: - * geometry - {<OpenLayers.Geometry.MultiPolygon>} A multipolygon - * geometry. - * - * Returns: - * {DOMElement} A GML multipolygon node. - */ - multipolygon: function(geometry) { - var gml = this.createElementNS(this.gmlns, "gml:MultiPolygon"); - var polys = geometry.components; - var polyMember, polyGeom; - for(var i=0; i<polys.length; ++i) { - polyMember = this.createElementNS(this.gmlns, - "gml:polygonMember"); - polyGeom = this.buildGeometry.polygon.apply(this, - [polys[i]]); - polyMember.appendChild(polyGeom); - gml.appendChild(polyMember); - } - return gml; - - }, - - /** - * Method: buildGeometry.bounds - * Given an OpenLayers bounds, create a GML box. - * - * Parameters: - * bounds - {<OpenLayers.Geometry.Bounds>} A bounds object. - * - * Returns: - * {DOMElement} A GML box node. - */ - bounds: function(bounds) { - var gml = this.createElementNS(this.gmlns, "gml:Box"); - gml.appendChild(this.buildCoordinatesNode(bounds)); - return gml; - } - }, - - /** - * Method: buildCoordinates - * builds the coordinates XmlNode - * (code) - * <gml:coordinates decimal="." cs="," ts=" ">...</gml:coordinates> - * (end) - * - * Parameters: - * geometry - {<OpenLayers.Geometry>} - * - * Returns: - * {XmlNode} created xmlNode - */ - buildCoordinatesNode: function(geometry) { - var coordinatesNode = this.createElementNS(this.gmlns, - "gml:coordinates"); - coordinatesNode.setAttribute("decimal", "."); - coordinatesNode.setAttribute("cs", ","); - coordinatesNode.setAttribute("ts", " "); - - var parts = []; - - if(geometry instanceof OpenLayers.Bounds){ - parts.push(geometry.left + "," + geometry.bottom); - parts.push(geometry.right + "," + geometry.top); - } else { - var points = (geometry.components) ? geometry.components : [geometry]; - for(var i=0; i<points.length; i++) { - parts.push(points[i].x + "," + points[i].y); - } - } - - var txtNode = this.createTextNode(parts.join(" ")); - coordinatesNode.appendChild(txtNode); - - return coordinatesNode; - }, - - CLASS_NAME: "OpenLayers.Format.GML" -}); -/* ====================================================================== - OpenLayers/Format/GML/Base.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/XML.js - * @requires OpenLayers/Format/GML.js - */ - -/** - * Though required in the full build, if the GML format is excluded, we set - * the namespace here. - */ -if(!OpenLayers.Format.GML) { - OpenLayers.Format.GML = {}; -} - -/** - * Class: OpenLayers.Format.GML.Base - * Superclass for GML parsers. - * - * Inherits from: - * - <OpenLayers.Format.XML> - */ -OpenLayers.Format.GML.Base = OpenLayers.Class(OpenLayers.Format.XML, { - - /** - * Property: namespaces - * {Object} Mapping of namespace aliases to namespace URIs. - */ - namespaces: { - gml: "http://www.opengis.net/gml", - xlink: "http://www.w3.org/1999/xlink", - xsi: "http://www.w3.org/2001/XMLSchema-instance", - wfs: "http://www.opengis.net/wfs" // this is a convenience for reading wfs:FeatureCollection - }, - - /** - * Property: defaultPrefix - */ - defaultPrefix: "gml", - - /** - * Property: schemaLocation - * {String} Schema location for a particular minor version. - */ - schemaLocation: null, - - /** - * APIProperty: featureType - * {Array(String) or String} The local (without prefix) feature typeName(s). - */ - featureType: null, - - /** - * APIProperty: featureNS - * {String} The feature namespace. Must be set in the options at - * construction. - */ - featureNS: null, - - /** - * APIProperty: geometry - * {String} Name of geometry element. Defaults to "geometry". If null, it - * will be set on <read> when the first geometry is parsed. - */ - geometryName: "geometry", - - /** - * APIProperty: extractAttributes - * {Boolean} Extract attributes from GML. Default is true. - */ - extractAttributes: true, - - /** - * APIProperty: srsName - * {String} URI for spatial reference system. This is optional for - * single part geometries and mandatory for collections and multis. - * If set, the srsName attribute will be written for all geometries. - * Default is null. - */ - srsName: null, - - /** - * APIProperty: xy - * {Boolean} Order of the GML coordinate true:(x,y) or false:(y,x) - * Changing is not recommended, a new Format should be instantiated. - */ - xy: true, - - /** - * Property: geometryTypes - * {Object} Maps OpenLayers geometry class names to GML element names. - * Use <setGeometryTypes> before accessing this property. - */ - geometryTypes: null, - - /** - * Property: singleFeatureType - * {Boolean} True if there is only 1 featureType, and not an array - * of featuretypes. - */ - singleFeatureType: null, - - /** - * Property: autoConfig - * {Boolean} Indicates if the format was configured without a <featureNS>, - * but auto-configured <featureNS> and <featureType> during read. - * Subclasses making use of <featureType> auto-configuration should make - * the first call to the <readNode> method (usually in the read method) - * with true as 3rd argument, so the auto-configured featureType can be - * reset and the format can be reused for subsequent reads with data from - * different featureTypes. Set to false after read if you want to keep the - * auto-configured values. - */ - - /** - * Property: regExes - * Compiled regular expressions for manipulating strings. - */ - regExes: { - trimSpace: (/^\s*|\s*$/g), - removeSpace: (/\s*/g), - splitSpace: (/\s+/), - trimComma: (/\s*,\s*/g), - featureMember: (/^(.*:)?featureMembers?$/) - }, - - /** - * Constructor: OpenLayers.Format.GML.Base - * Instances of this class are not created directly. Use the - * <OpenLayers.Format.GML.v2> or <OpenLayers.Format.GML.v3> constructor - * instead. - * - * Parameters: - * options - {Object} An optional object whose properties will be set on - * this instance. - * - * Valid options properties: - * featureType - {Array(String) or String} Local (without prefix) feature - * typeName(s) (required for write). - * featureNS - {String} Feature namespace (required for write). - * geometryName - {String} Geometry element name (required for write). - */ - initialize: function(options) { - OpenLayers.Format.XML.prototype.initialize.apply(this, [options]); - this.setGeometryTypes(); - if(options && options.featureNS) { - this.setNamespace("feature", options.featureNS); - } - this.singleFeatureType = !options || (typeof options.featureType === "string"); - }, - - /** - * Method: read - * - * Parameters: - * data - {DOMElement} A gml:featureMember element, a gml:featureMembers - * element, or an element containing either of the above at any level. - * - * Returns: - * {Array(<OpenLayers.Feature.Vector>)} An array of features. - */ - read: function(data) { - if(typeof data == "string") { - data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); - } - if(data && data.nodeType == 9) { - data = data.documentElement; - } - var features = []; - this.readNode(data, {features: features}, true); - if(features.length == 0) { - // look for gml:featureMember elements - var elements = this.getElementsByTagNameNS( - data, this.namespaces.gml, "featureMember" - ); - if(elements.length) { - for(var i=0, len=elements.length; i<len; ++i) { - this.readNode(elements[i], {features: features}, true); - } - } else { - // look for gml:featureMembers elements (this is v3, but does no harm here) - var elements = this.getElementsByTagNameNS( - data, this.namespaces.gml, "featureMembers" - ); - if(elements.length) { - // there can be only one - this.readNode(elements[0], {features: features}, true); - } - } - } - return features; - }, - - /** - * Method: readNode - * Shorthand for applying one of the named readers given the node - * namespace and local name. Readers take two args (node, obj) and - * generally extend or modify the second. - * - * Parameters: - * node - {DOMElement} The node to be read (required). - * obj - {Object} The object to be modified (optional). - * first - {Boolean} Should be set to true for the first node read. This - * is usually the readNode call in the read method. Without this being - * set, auto-configured properties will stick on subsequent reads. - * - * Returns: - * {Object} The input object, modified (or a new one if none was provided). - */ - readNode: function(node, obj, first) { - // on subsequent calls of format.read(), we want to reset auto- - // configured properties and auto-configure again. - if (first === true && this.autoConfig === true) { - this.featureType = null; - delete this.namespaceAlias[this.featureNS]; - delete this.namespaces["feature"]; - this.featureNS = null; - } - // featureType auto-configuration - if (!this.featureNS && (!(node.prefix in this.namespaces) && - node.parentNode.namespaceURI == this.namespaces["gml"] && - this.regExes.featureMember.test(node.parentNode.nodeName))) { - this.featureType = node.nodeName.split(":").pop(); - this.setNamespace("feature", node.namespaceURI); - this.featureNS = node.namespaceURI; - this.autoConfig = true; - } - return OpenLayers.Format.XML.prototype.readNode.apply(this, [node, obj]); - }, - - /** - * Property: readers - * Contains public functions, grouped by namespace prefix, that will - * be applied when a namespaced node is found matching the function - * name. The function will be applied in the scope of this parser - * with two arguments: the node being read and a context object passed - * from the parent. - */ - readers: { - "gml": { - "_inherit": function(node, obj, container) { - // To be implemented by version specific parsers - }, - "featureMember": function(node, obj) { - this.readChildNodes(node, obj); - }, - "featureMembers": function(node, obj) { - this.readChildNodes(node, obj); - }, - "name": function(node, obj) { - obj.name = this.getChildValue(node); - }, - "boundedBy": function(node, obj) { - var container = {}; - this.readChildNodes(node, container); - if(container.components && container.components.length > 0) { - obj.bounds = container.components[0]; - } - }, - "Point": function(node, container) { - var obj = {points: []}; - this.readChildNodes(node, obj); - if(!container.components) { - container.components = []; - } - container.components.push(obj.points[0]); - }, - "coordinates": function(node, obj) { - var str = this.getChildValue(node).replace( - this.regExes.trimSpace, "" - ); - str = str.replace(this.regExes.trimComma, ","); - var pointList = str.split(this.regExes.splitSpace); - var coords; - var numPoints = pointList.length; - var points = new Array(numPoints); - for(var i=0; i<numPoints; ++i) { - coords = pointList[i].split(","); - if (this.xy) { - points[i] = new OpenLayers.Geometry.Point( - coords[0], coords[1], coords[2] - ); - } else { - points[i] = new OpenLayers.Geometry.Point( - coords[1], coords[0], coords[2] - ); - } - } - obj.points = points; - }, - "coord": function(node, obj) { - var coord = {}; - this.readChildNodes(node, coord); - if(!obj.points) { - obj.points = []; - } - obj.points.push(new OpenLayers.Geometry.Point( - coord.x, coord.y, coord.z - )); - }, - "X": function(node, coord) { - coord.x = this.getChildValue(node); - }, - "Y": function(node, coord) { - coord.y = this.getChildValue(node); - }, - "Z": function(node, coord) { - coord.z = this.getChildValue(node); - }, - "MultiPoint": function(node, container) { - var obj = {components: []}; - this.readers.gml._inherit.apply(this, [node, obj, container]); - this.readChildNodes(node, obj); - container.components = [ - new OpenLayers.Geometry.MultiPoint(obj.components) - ]; - }, - "pointMember": function(node, obj) { - this.readChildNodes(node, obj); - }, - "LineString": function(node, container) { - var obj = {}; - this.readers.gml._inherit.apply(this, [node, obj, container]); - this.readChildNodes(node, obj); - if(!container.components) { - container.components = []; - } - container.components.push( - new OpenLayers.Geometry.LineString(obj.points) - ); - }, - "MultiLineString": function(node, container) { - var obj = {components: []}; - this.readers.gml._inherit.apply(this, [node, obj, container]); - this.readChildNodes(node, obj); - container.components = [ - new OpenLayers.Geometry.MultiLineString(obj.components) - ]; - }, - "lineStringMember": function(node, obj) { - this.readChildNodes(node, obj); - }, - "Polygon": function(node, container) { - var obj = {outer: null, inner: []}; - this.readers.gml._inherit.apply(this, [node, obj, container]); - this.readChildNodes(node, obj); - obj.inner.unshift(obj.outer); - if(!container.components) { - container.components = []; - } - container.components.push( - new OpenLayers.Geometry.Polygon(obj.inner) - ); - }, - "LinearRing": function(node, obj) { - var container = {}; - this.readers.gml._inherit.apply(this, [node, container]); - this.readChildNodes(node, container); - obj.components = [new OpenLayers.Geometry.LinearRing( - container.points - )]; - }, - "MultiPolygon": function(node, container) { - var obj = {components: []}; - this.readers.gml._inherit.apply(this, [node, obj, container]); - this.readChildNodes(node, obj); - container.components = [ - new OpenLayers.Geometry.MultiPolygon(obj.components) - ]; - }, - "polygonMember": function(node, obj) { - this.readChildNodes(node, obj); - }, - "GeometryCollection": function(node, container) { - var obj = {components: []}; - this.readers.gml._inherit.apply(this, [node, obj, container]); - this.readChildNodes(node, obj); - container.components = [ - new OpenLayers.Geometry.Collection(obj.components) - ]; - }, - "geometryMember": function(node, obj) { - this.readChildNodes(node, obj); - } - }, - "feature": { - "*": function(node, obj) { - // The node can either be named like the featureType, or it - // can be a child of the feature:featureType. Children can be - // geometry or attributes. - var name; - var local = node.localName || node.nodeName.split(":").pop(); - // Since an attribute can have the same name as the feature type - // we only want to read the node as a feature if the parent - // node can have feature nodes as children. In this case, the - // obj.features property is set. - if (obj.features) { - if (!this.singleFeatureType && - (OpenLayers.Util.indexOf(this.featureType, local) !== -1)) { - name = "_typeName"; - } else if(local === this.featureType) { - name = "_typeName"; - } - } else { - // Assume attribute elements have one child node and that the child - // is a text node. Otherwise assume it is a geometry node. - if(node.childNodes.length == 0 || - (node.childNodes.length == 1 && node.firstChild.nodeType == 3)) { - if(this.extractAttributes) { - name = "_attribute"; - } - } else { - name = "_geometry"; - } - } - if(name) { - this.readers.feature[name].apply(this, [node, obj]); - } - }, - "_typeName": function(node, obj) { - var container = {components: [], attributes: {}}; - this.readChildNodes(node, container); - // look for common gml namespaced elements - if(container.name) { - container.attributes.name = container.name; - } - var feature = new OpenLayers.Feature.Vector( - container.components[0], container.attributes - ); - if (!this.singleFeatureType) { - feature.type = node.nodeName.split(":").pop(); - feature.namespace = node.namespaceURI; - } - var fid = node.getAttribute("fid") || - this.getAttributeNS(node, this.namespaces["gml"], "id"); - if(fid) { - feature.fid = fid; - } - if(this.internalProjection && this.externalProjection && - feature.geometry) { - feature.geometry.transform( - this.externalProjection, this.internalProjection - ); - } - if(container.bounds) { - feature.bounds = container.bounds; - } - obj.features.push(feature); - }, - "_geometry": function(node, obj) { - if (!this.geometryName) { - this.geometryName = node.nodeName.split(":").pop(); - } - this.readChildNodes(node, obj); - }, - "_attribute": function(node, obj) { - var local = node.localName || node.nodeName.split(":").pop(); - var value = this.getChildValue(node); - obj.attributes[local] = value; - } - }, - "wfs": { - "FeatureCollection": function(node, obj) { - this.readChildNodes(node, obj); - } - } - }, - - /** - * Method: write - * - * Parameters: - * features - {Array(<OpenLayers.Feature.Vector>) | OpenLayers.Feature.Vector} - * An array of features or a single feature. - * - * Returns: - * {String} Given an array of features, a doc with a gml:featureMembers - * element will be returned. Given a single feature, a doc with a - * gml:featureMember element will be returned. - */ - write: function(features) { - var name; - if(OpenLayers.Util.isArray(features)) { - name = "featureMembers"; - } else { - name = "featureMember"; - } - var root = this.writeNode("gml:" + name, features); - this.setAttributeNS( - root, this.namespaces["xsi"], - "xsi:schemaLocation", this.schemaLocation - ); - - return OpenLayers.Format.XML.prototype.write.apply(this, [root]); - }, - - /** - * Property: writers - * As a compliment to the readers property, this structure contains public - * writing functions grouped by namespace alias and named like the - * node names they produce. - */ - writers: { - "gml": { - "featureMember": function(feature) { - var node = this.createElementNSPlus("gml:featureMember"); - this.writeNode("feature:_typeName", feature, node); - return node; - }, - "MultiPoint": function(geometry) { - var node = this.createElementNSPlus("gml:MultiPoint"); - var components = geometry.components || [geometry]; - for(var i=0, ii=components.length; i<ii; ++i) { - this.writeNode("pointMember", components[i], node); - } - return node; - }, - "pointMember": function(geometry) { - var node = this.createElementNSPlus("gml:pointMember"); - this.writeNode("Point", geometry, node); - return node; - }, - "MultiLineString": function(geometry) { - var node = this.createElementNSPlus("gml:MultiLineString"); - var components = geometry.components || [geometry]; - for(var i=0, ii=components.length; i<ii; ++i) { - this.writeNode("lineStringMember", components[i], node); - } - return node; - }, - "lineStringMember": function(geometry) { - var node = this.createElementNSPlus("gml:lineStringMember"); - this.writeNode("LineString", geometry, node); - return node; - }, - "MultiPolygon": function(geometry) { - var node = this.createElementNSPlus("gml:MultiPolygon"); - var components = geometry.components || [geometry]; - for(var i=0, ii=components.length; i<ii; ++i) { - this.writeNode( - "polygonMember", components[i], node - ); - } - return node; - }, - "polygonMember": function(geometry) { - var node = this.createElementNSPlus("gml:polygonMember"); - this.writeNode("Polygon", geometry, node); - return node; - }, - "GeometryCollection": function(geometry) { - var node = this.createElementNSPlus("gml:GeometryCollection"); - for(var i=0, len=geometry.components.length; i<len; ++i) { - this.writeNode("geometryMember", geometry.components[i], node); - } - return node; - }, - "geometryMember": function(geometry) { - var node = this.createElementNSPlus("gml:geometryMember"); - var child = this.writeNode("feature:_geometry", geometry); - node.appendChild(child.firstChild); - return node; - } - }, - "feature": { - "_typeName": function(feature) { - var node = this.createElementNSPlus("feature:" + this.featureType, { - attributes: {fid: feature.fid} - }); - if(feature.geometry) { - this.writeNode("feature:_geometry", feature.geometry, node); - } - for(var name in feature.attributes) { - var value = feature.attributes[name]; - if(value != null) { - this.writeNode( - "feature:_attribute", - {name: name, value: value}, node - ); - } - } - return node; - }, - "_geometry": function(geometry) { - if(this.externalProjection && this.internalProjection) { - geometry = geometry.clone().transform( - this.internalProjection, this.externalProjection - ); - } - var node = this.createElementNSPlus( - "feature:" + this.geometryName - ); - var type = this.geometryTypes[geometry.CLASS_NAME]; - var child = this.writeNode("gml:" + type, geometry, node); - if(this.srsName) { - child.setAttribute("srsName", this.srsName); - } - return node; - }, - "_attribute": function(obj) { - return this.createElementNSPlus("feature:" + obj.name, { - value: obj.value - }); - } - }, - "wfs": { - "FeatureCollection": function(features) { - /** - * This is only here because GML2 only describes abstract - * feature collections. Typically, you would not be using - * the GML format to write wfs elements. This just provides - * some way to write out lists of features. GML3 defines the - * featureMembers element, so that is used by default instead. - */ - var node = this.createElementNSPlus("wfs:FeatureCollection"); - for(var i=0, len=features.length; i<len; ++i) { - this.writeNode("gml:featureMember", features[i], node); - } - return node; - } - } - }, - - /** - * Method: setGeometryTypes - * Sets the <geometryTypes> mapping. - */ - setGeometryTypes: function() { - this.geometryTypes = { - "OpenLayers.Geometry.Point": "Point", - "OpenLayers.Geometry.MultiPoint": "MultiPoint", - "OpenLayers.Geometry.LineString": "LineString", - "OpenLayers.Geometry.MultiLineString": "MultiLineString", - "OpenLayers.Geometry.Polygon": "Polygon", - "OpenLayers.Geometry.MultiPolygon": "MultiPolygon", - "OpenLayers.Geometry.Collection": "GeometryCollection" - }; - }, - - CLASS_NAME: "OpenLayers.Format.GML.Base" - -}); -/* ====================================================================== - OpenLayers/Format/GML/v2.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/GML/Base.js - */ - -/** - * Class: OpenLayers.Format.GML.v2 - * Parses GML version 2. - * - * Inherits from: - * - <OpenLayers.Format.GML.Base> - */ -OpenLayers.Format.GML.v2 = OpenLayers.Class(OpenLayers.Format.GML.Base, { - - /** - * Property: schemaLocation - * {String} Schema location for a particular minor version. - */ - schemaLocation: "http://www.opengis.net/gml http://schemas.opengis.net/gml/2.1.2/feature.xsd", - - /** - * Constructor: OpenLayers.Format.GML.v2 - * Create a parser for GML v2. - * - * Parameters: - * options - {Object} An optional object whose properties will be set on - * this instance. - * - * Valid options properties: - * featureType - {String} Local (without prefix) feature typeName (required). - * featureNS - {String} Feature namespace (required). - * geometryName - {String} Geometry element name. - */ - initialize: function(options) { - OpenLayers.Format.GML.Base.prototype.initialize.apply(this, [options]); - }, - - /** - * Property: readers - * Contains public functions, grouped by namespace prefix, that will - * be applied when a namespaced node is found matching the function - * name. The function will be applied in the scope of this parser - * with two arguments: the node being read and a context object passed - * from the parent. - */ - readers: { - "gml": OpenLayers.Util.applyDefaults({ - "outerBoundaryIs": function(node, container) { - var obj = {}; - this.readChildNodes(node, obj); - container.outer = obj.components[0]; - }, - "innerBoundaryIs": function(node, container) { - var obj = {}; - this.readChildNodes(node, obj); - container.inner.push(obj.components[0]); - }, - "Box": function(node, container) { - var obj = {}; - this.readChildNodes(node, obj); - if(!container.components) { - container.components = []; - } - var min = obj.points[0]; - var max = obj.points[1]; - container.components.push( - new OpenLayers.Bounds(min.x, min.y, max.x, max.y) - ); - } - }, OpenLayers.Format.GML.Base.prototype.readers["gml"]), - "feature": OpenLayers.Format.GML.Base.prototype.readers["feature"], - "wfs": OpenLayers.Format.GML.Base.prototype.readers["wfs"] - }, - - /** - * Method: write - * - * Parameters: - * features - {Array(<OpenLayers.Feature.Vector>) | OpenLayers.Feature.Vector} - * An array of features or a single feature. - * - * Returns: - * {String} Given an array of features, a doc with a gml:featureMembers - * element will be returned. Given a single feature, a doc with a - * gml:featureMember element will be returned. - */ - write: function(features) { - var name; - if(OpenLayers.Util.isArray(features)) { - // GML2 only has abstract feature collections - // wfs provides a feature collection from a well-known schema - name = "wfs:FeatureCollection"; - } else { - name = "gml:featureMember"; - } - var root = this.writeNode(name, features); - this.setAttributeNS( - root, this.namespaces["xsi"], - "xsi:schemaLocation", this.schemaLocation - ); - - return OpenLayers.Format.XML.prototype.write.apply(this, [root]); - }, - - /** - * Property: writers - * As a compliment to the readers property, this structure contains public - * writing functions grouped by namespace alias and named like the - * node names they produce. - */ - writers: { - "gml": OpenLayers.Util.applyDefaults({ - "Point": function(geometry) { - var node = this.createElementNSPlus("gml:Point"); - this.writeNode("coordinates", [geometry], node); - return node; - }, - "coordinates": function(points) { - var numPoints = points.length; - var parts = new Array(numPoints); - var point; - for(var i=0; i<numPoints; ++i) { - point = points[i]; - if(this.xy) { - parts[i] = point.x + "," + point.y; - } else { - parts[i] = point.y + "," + point.x; - } - if(point.z != undefined) { // allow null or undefined - parts[i] += "," + point.z; - } - } - return this.createElementNSPlus("gml:coordinates", { - attributes: { - decimal: ".", cs: ",", ts: " " - }, - value: (numPoints == 1) ? parts[0] : parts.join(" ") - }); - }, - "LineString": function(geometry) { - var node = this.createElementNSPlus("gml:LineString"); - this.writeNode("coordinates", geometry.components, node); - return node; - }, - "Polygon": function(geometry) { - var node = this.createElementNSPlus("gml:Polygon"); - this.writeNode("outerBoundaryIs", geometry.components[0], node); - for(var i=1; i<geometry.components.length; ++i) { - this.writeNode( - "innerBoundaryIs", geometry.components[i], node - ); - } - return node; - }, - "outerBoundaryIs": function(ring) { - var node = this.createElementNSPlus("gml:outerBoundaryIs"); - this.writeNode("LinearRing", ring, node); - return node; - }, - "innerBoundaryIs": function(ring) { - var node = this.createElementNSPlus("gml:innerBoundaryIs"); - this.writeNode("LinearRing", ring, node); - return node; - }, - "LinearRing": function(ring) { - var node = this.createElementNSPlus("gml:LinearRing"); - this.writeNode("coordinates", ring.components, node); - return node; - }, - "Box": function(bounds) { - var node = this.createElementNSPlus("gml:Box"); - this.writeNode("coordinates", [ - {x: bounds.left, y: bounds.bottom}, - {x: bounds.right, y: bounds.top} - ], node); - // srsName attribute is optional for gml:Box - if(this.srsName) { - node.setAttribute("srsName", this.srsName); - } - return node; - } - }, OpenLayers.Format.GML.Base.prototype.writers["gml"]), - "feature": OpenLayers.Format.GML.Base.prototype.writers["feature"], - "wfs": OpenLayers.Format.GML.Base.prototype.writers["wfs"] - }, - - CLASS_NAME: "OpenLayers.Format.GML.v2" - -}); -/* ====================================================================== - OpenLayers/Filter/Function.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.Function - * This class represents a filter function. - * We are using this class for creation of complex - * filters that can contain filter functions as values. - * Nesting function as other functions parameter is supported. - * - * Inherits from: - * - <OpenLayers.Filter> - */ -OpenLayers.Filter.Function = OpenLayers.Class(OpenLayers.Filter, { - - /** - * APIProperty: name - * {String} Name of the function. - */ - name: null, - - /** - * APIProperty: params - * {Array(<OpenLayers.Filter.Function> || String || Number)} Function parameters - * For now support only other Functions, String or Number - */ - params: null, - - /** - * Constructor: OpenLayers.Filter.Function - * Creates a filter function. - * - * Parameters: - * options - {Object} An optional object with properties to set on the - * function. - * - * Returns: - * {<OpenLayers.Filter.Function>} - */ - - CLASS_NAME: "OpenLayers.Filter.Function" -}); - -/* ====================================================================== - OpenLayers/BaseTypes/Date.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 - */ - -/** - * Namespace: OpenLayers.Date - * Contains implementations of Date.parse and date.toISOString that match the - * ECMAScript 5 specification for parsing RFC 3339 dates. - * http://tools.ietf.org/html/rfc3339 - */ -OpenLayers.Date = { - - /** - * APIProperty: dateRegEx - * The regex to be used for validating dates. You can provide your own - * regex for instance for adding support for years before BC. Default - * value is: /^(?:(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?)?(?:(?:T(\d{1,2}):(\d{2}):(\d{2}(?:\.\d+)?)(Z|(?:[+-]\d{1,2}(?::(\d{2}))?)))|Z)?$/ - */ - dateRegEx: /^(?:(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?)?(?:(?:T(\d{1,2}):(\d{2}):(\d{2}(?:\.\d+)?)(Z|(?:[+-]\d{1,2}(?::(\d{2}))?)))|Z)?$/, - - /** - * APIMethod: toISOString - * Generates a string representing a date. The format of the string follows - * the profile of ISO 8601 for date and time on the Internet (see - * http://tools.ietf.org/html/rfc3339). If the toISOString method is - * available on the Date prototype, that is used. The toISOString - * method for Date instances is defined in ECMA-262. - * - * Parameters: - * date - {Date} A date object. - * - * Returns: - * {String} A string representing the date (e.g. - * "2010-08-07T16:58:23.123Z"). If the date does not have a valid time - * (i.e. isNaN(date.getTime())) this method returns the string "Invalid - * Date". The ECMA standard says the toISOString method should throw - * RangeError in this case, but Firefox returns a string instead. For - * best results, use isNaN(date.getTime()) to determine date validity - * before generating date strings. - */ - toISOString: (function() { - if ("toISOString" in Date.prototype) { - return function(date) { - return date.toISOString(); - }; - } else { - return function(date) { - var str; - if (isNaN(date.getTime())) { - // ECMA-262 says throw RangeError, Firefox returns - // "Invalid Date" - str = "Invalid Date"; - } else { - str = - date.getUTCFullYear() + "-" + - OpenLayers.Number.zeroPad(date.getUTCMonth() + 1, 2) + "-" + - OpenLayers.Number.zeroPad(date.getUTCDate(), 2) + "T" + - OpenLayers.Number.zeroPad(date.getUTCHours(), 2) + ":" + - OpenLayers.Number.zeroPad(date.getUTCMinutes(), 2) + ":" + - OpenLayers.Number.zeroPad(date.getUTCSeconds(), 2) + "." + - OpenLayers.Number.zeroPad(date.getUTCMilliseconds(), 3) + "Z"; - } - return str; - }; - } - - })(), - - /** - * APIMethod: parse - * Generate a date object from a string. The format for the string follows - * the profile of ISO 8601 for date and time on the Internet (see - * http://tools.ietf.org/html/rfc3339). We don't call the native - * Date.parse because of inconsistency between implmentations. In - * Chrome, calling Date.parse with a string that doesn't contain any - * indication of the timezone (e.g. "2011"), the date is interpreted - * in local time. On Firefox, the assumption is UTC. - * - * Parameters: - * str - {String} A string representing the date (e.g. - * "2010", "2010-08", "2010-08-07", "2010-08-07T16:58:23.123Z", - * "2010-08-07T11:58:23.123-06"). - * - * Returns: - * {Date} A date object. If the string could not be parsed, an invalid - * date is returned (i.e. isNaN(date.getTime())). - */ - parse: function(str) { - var date; - var match = str.match(this.dateRegEx); - if (match && (match[1] || match[7])) { // must have at least year or time - var year = parseInt(match[1], 10) || 0; - var month = (parseInt(match[2], 10) - 1) || 0; - var day = parseInt(match[3], 10) || 1; - date = new Date(Date.UTC(year, month, day)); - // optional time - var type = match[7]; - if (type) { - var hours = parseInt(match[4], 10); - var minutes = parseInt(match[5], 10); - var secFrac = parseFloat(match[6]); - var seconds = secFrac | 0; - var milliseconds = Math.round(1000 * (secFrac - seconds)); - date.setUTCHours(hours, minutes, seconds, milliseconds); - // check offset - if (type !== "Z") { - var hoursOffset = parseInt(type, 10); - var minutesOffset = parseInt(match[8], 10) || 0; - var offset = -1000 * (60 * (hoursOffset * 60) + minutesOffset * 60); - date = new Date(date.getTime() + offset); - } - } - } else { - date = new Date("invalid"); - } - return date; - } -}; -/* ====================================================================== - OpenLayers/Format/Filter/v1.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/Filter.js - * @requires OpenLayers/Format/XML.js - * @requires OpenLayers/Filter/Function.js - * @requires OpenLayers/BaseTypes/Date.js - */ - -/** - * Class: OpenLayers.Format.Filter.v1 - * Superclass for Filter version 1 parsers. - * - * Inherits from: - * - <OpenLayers.Format.XML> - */ -OpenLayers.Format.Filter.v1 = OpenLayers.Class(OpenLayers.Format.XML, { - - /** - * Property: namespaces - * {Object} Mapping of namespace aliases to namespace URIs. - */ - namespaces: { - ogc: "http://www.opengis.net/ogc", - gml: "http://www.opengis.net/gml", - xlink: "http://www.w3.org/1999/xlink", - xsi: "http://www.w3.org/2001/XMLSchema-instance" - }, - - /** - * Property: defaultPrefix - */ - defaultPrefix: "ogc", - - /** - * Property: schemaLocation - * {String} Schema location for a particular minor version. - */ - schemaLocation: null, - - /** - * Constructor: OpenLayers.Format.Filter.v1 - * Instances of this class are not created directly. Use the - * <OpenLayers.Format.Filter> constructor instead. - * - * Parameters: - * options - {Object} An optional object whose properties will be set on - * this instance. - */ - initialize: function(options) { - OpenLayers.Format.XML.prototype.initialize.apply(this, [options]); - }, - - /** - * Method: read - * - * Parameters: - * data - {DOMElement} A Filter document element. - * - * Returns: - * {<OpenLayers.Filter>} A filter object. - */ - read: function(data) { - var obj = {}; - this.readers.ogc["Filter"].apply(this, [data, obj]); - return obj.filter; - }, - - /** - * Property: readers - * Contains public functions, grouped by namespace prefix, that will - * be applied when a namespaced node is found matching the function - * name. The function will be applied in the scope of this parser - * with two arguments: the node being read and a context object passed - * from the parent. - */ - readers: { - "ogc": { - "_expression": function(node) { - // only the simplest of ogc:expression handled - // "some text and an <PropertyName>attribute</PropertyName>"} - var obj, value = ""; - for(var child=node.firstChild; child; child=child.nextSibling) { - switch(child.nodeType) { - case 1: - obj = this.readNode(child); - if (obj.property) { - value += "${" + obj.property + "}"; - } else if (obj.value !== undefined) { - value += obj.value; - } - break; - case 3: // text node - case 4: // cdata section - value += child.nodeValue; - } - } - return value; - }, - "Filter": function(node, parent) { - // Filters correspond to subclasses of OpenLayers.Filter. - // Since they contain information we don't persist, we - // create a temporary object and then pass on the filter - // (ogc:Filter) to the parent obj. - var obj = { - fids: [], - filters: [] - }; - this.readChildNodes(node, obj); - if(obj.fids.length > 0) { - parent.filter = new OpenLayers.Filter.FeatureId({ - fids: obj.fids - }); - } else if(obj.filters.length > 0) { - parent.filter = obj.filters[0]; - } - }, - "FeatureId": function(node, obj) { - var fid = node.getAttribute("fid"); - if(fid) { - obj.fids.push(fid); - } - }, - "And": function(node, obj) { - var filter = new OpenLayers.Filter.Logical({ - type: OpenLayers.Filter.Logical.AND - }); - this.readChildNodes(node, filter); - obj.filters.push(filter); - }, - "Or": function(node, obj) { - var filter = new OpenLayers.Filter.Logical({ - type: OpenLayers.Filter.Logical.OR - }); - this.readChildNodes(node, filter); - obj.filters.push(filter); - }, - "Not": function(node, obj) { - var filter = new OpenLayers.Filter.Logical({ - type: OpenLayers.Filter.Logical.NOT - }); - this.readChildNodes(node, filter); - obj.filters.push(filter); - }, - "PropertyIsLessThan": function(node, obj) { - var filter = new OpenLayers.Filter.Comparison({ - type: OpenLayers.Filter.Comparison.LESS_THAN - }); - this.readChildNodes(node, filter); - obj.filters.push(filter); - }, - "PropertyIsGreaterThan": function(node, obj) { - var filter = new OpenLayers.Filter.Comparison({ - type: OpenLayers.Filter.Comparison.GREATER_THAN - }); - this.readChildNodes(node, filter); - obj.filters.push(filter); - }, - "PropertyIsLessThanOrEqualTo": function(node, obj) { - var filter = new OpenLayers.Filter.Comparison({ - type: OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO - }); - this.readChildNodes(node, filter); - obj.filters.push(filter); - }, - "PropertyIsGreaterThanOrEqualTo": function(node, obj) { - var filter = new OpenLayers.Filter.Comparison({ - type: OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO - }); - this.readChildNodes(node, filter); - obj.filters.push(filter); - }, - "PropertyIsBetween": function(node, obj) { - var filter = new OpenLayers.Filter.Comparison({ - type: OpenLayers.Filter.Comparison.BETWEEN - }); - this.readChildNodes(node, filter); - obj.filters.push(filter); - }, - "Literal": function(node, obj) { - obj.value = OpenLayers.String.numericIf( - this.getChildValue(node), true); - }, - "PropertyName": function(node, filter) { - filter.property = this.getChildValue(node); - }, - "LowerBoundary": function(node, filter) { - filter.lowerBoundary = OpenLayers.String.numericIf( - this.readers.ogc._expression.call(this, node), true); - }, - "UpperBoundary": function(node, filter) { - filter.upperBoundary = OpenLayers.String.numericIf( - this.readers.ogc._expression.call(this, node), true); - }, - "Intersects": function(node, obj) { - this.readSpatial(node, obj, OpenLayers.Filter.Spatial.INTERSECTS); - }, - "Within": function(node, obj) { - this.readSpatial(node, obj, OpenLayers.Filter.Spatial.WITHIN); - }, - "Contains": function(node, obj) { - this.readSpatial(node, obj, OpenLayers.Filter.Spatial.CONTAINS); - }, - "DWithin": function(node, obj) { - this.readSpatial(node, obj, OpenLayers.Filter.Spatial.DWITHIN); - }, - "Distance": function(node, obj) { - obj.distance = parseInt(this.getChildValue(node)); - obj.distanceUnits = node.getAttribute("units"); - }, - "Function": function(node, obj) { - //TODO write decoder for it - return; - }, - "PropertyIsNull": function(node, obj) { - var filter = new OpenLayers.Filter.Comparison({ - type: OpenLayers.Filter.Comparison.IS_NULL - }); - this.readChildNodes(node, filter); - obj.filters.push(filter); - } - } - }, - - /** - * Method: readSpatial - * - * Read a {<OpenLayers.Filter.Spatial>} filter. - * - * Parameters: - * node - {DOMElement} A DOM element that contains an ogc:expression. - * obj - {Object} The target object. - * type - {String} One of the OpenLayers.Filter.Spatial.* constants. - * - * Returns: - * {<OpenLayers.Filter.Spatial>} The created filter. - */ - readSpatial: function(node, obj, type) { - var filter = new OpenLayers.Filter.Spatial({ - type: type - }); - this.readChildNodes(node, filter); - filter.value = filter.components[0]; - delete filter.components; - obj.filters.push(filter); - }, - - /** - * APIMethod: encodeLiteral - * Generates the string representation of a value for use in <Literal> - * elements. The default encoder writes Date values as ISO 8601 - * strings. - * - * Parameters: - * value - {Object} Literal value to encode - * - * Returns: - * {String} String representation of the provided value. - */ - encodeLiteral: function(value) { - if (value instanceof Date) { - value = OpenLayers.Date.toISOString(value); - } - return value; - }, - - /** - * Method: writeOgcExpression - * Limited support for writing OGC expressions. Currently it supports - * (<OpenLayers.Filter.Function> || String || Number) - * - * Parameters: - * value - (<OpenLayers.Filter.Function> || String || Number) - * node - {DOMElement} A parent DOM element - * - * Returns: - * {DOMElement} Updated node element. - */ - writeOgcExpression: function(value, node) { - if (value instanceof OpenLayers.Filter.Function){ - this.writeNode("Function", value, node); - } else { - this.writeNode("Literal", value, node); - } - return node; - }, - - /** - * Method: write - * - * Parameters: - * filter - {<OpenLayers.Filter>} A filter object. - * - * Returns: - * {DOMElement} An ogc:Filter element. - */ - write: function(filter) { - return this.writers.ogc["Filter"].apply(this, [filter]); - }, - - /** - * Property: writers - * As a compliment to the readers property, this structure contains public - * writing functions grouped by namespace alias and named like the - * node names they produce. - */ - writers: { - "ogc": { - "Filter": function(filter) { - var node = this.createElementNSPlus("ogc:Filter"); - this.writeNode(this.getFilterType(filter), filter, node); - return node; - }, - "_featureIds": function(filter) { - var node = this.createDocumentFragment(); - for (var i=0, ii=filter.fids.length; i<ii; ++i) { - this.writeNode("ogc:FeatureId", filter.fids[i], node); - } - return node; - }, - "FeatureId": function(fid) { - return this.createElementNSPlus("ogc:FeatureId", { - attributes: {fid: fid} - }); - }, - "And": function(filter) { - var node = this.createElementNSPlus("ogc:And"); - var childFilter; - for (var i=0, ii=filter.filters.length; i<ii; ++i) { - childFilter = filter.filters[i]; - this.writeNode( - this.getFilterType(childFilter), childFilter, node - ); - } - return node; - }, - "Or": function(filter) { - var node = this.createElementNSPlus("ogc:Or"); - var childFilter; - for (var i=0, ii=filter.filters.length; i<ii; ++i) { - childFilter = filter.filters[i]; - this.writeNode( - this.getFilterType(childFilter), childFilter, node - ); - } - return node; - }, - "Not": function(filter) { - var node = this.createElementNSPlus("ogc:Not"); - var childFilter = filter.filters[0]; - this.writeNode( - this.getFilterType(childFilter), childFilter, node - ); - return node; - }, - "PropertyIsLessThan": function(filter) { - var node = this.createElementNSPlus("ogc:PropertyIsLessThan"); - // no ogc:expression handling for PropertyName for now - this.writeNode("PropertyName", filter, node); - // handle Literals or Functions for now - this.writeOgcExpression(filter.value, node); - return node; - }, - "PropertyIsGreaterThan": function(filter) { - var node = this.createElementNSPlus("ogc:PropertyIsGreaterThan"); - // no ogc:expression handling for PropertyName for now - this.writeNode("PropertyName", filter, node); - // handle Literals or Functions for now - this.writeOgcExpression(filter.value, node); - return node; - }, - "PropertyIsLessThanOrEqualTo": function(filter) { - var node = this.createElementNSPlus("ogc:PropertyIsLessThanOrEqualTo"); - // no ogc:expression handling for PropertyName for now - this.writeNode("PropertyName", filter, node); - // handle Literals or Functions for now - this.writeOgcExpression(filter.value, node); - return node; - }, - "PropertyIsGreaterThanOrEqualTo": function(filter) { - var node = this.createElementNSPlus("ogc:PropertyIsGreaterThanOrEqualTo"); - // no ogc:expression handling for PropertyName for now - this.writeNode("PropertyName", filter, node); - // handle Literals or Functions for now - this.writeOgcExpression(filter.value, node); - return node; - }, - "PropertyIsBetween": function(filter) { - var node = this.createElementNSPlus("ogc:PropertyIsBetween"); - // no ogc:expression handling for PropertyName for now - this.writeNode("PropertyName", filter, node); - this.writeNode("LowerBoundary", filter, node); - this.writeNode("UpperBoundary", filter, node); - return node; - }, - "PropertyName": function(filter) { - // no ogc:expression handling for now - return this.createElementNSPlus("ogc:PropertyName", { - value: filter.property - }); - }, - "Literal": function(value) { - var encode = this.encodeLiteral || - OpenLayers.Format.Filter.v1.prototype.encodeLiteral; - return this.createElementNSPlus("ogc:Literal", { - value: encode(value) - }); - }, - "LowerBoundary": function(filter) { - // handle Literals or Functions for now - var node = this.createElementNSPlus("ogc:LowerBoundary"); - this.writeOgcExpression(filter.lowerBoundary, node); - return node; - }, - "UpperBoundary": function(filter) { - // handle Literals or Functions for now - var node = this.createElementNSPlus("ogc:UpperBoundary"); - this.writeNode("Literal", filter.upperBoundary, node); - return node; - }, - "INTERSECTS": function(filter) { - return this.writeSpatial(filter, "Intersects"); - }, - "WITHIN": function(filter) { - return this.writeSpatial(filter, "Within"); - }, - "CONTAINS": function(filter) { - return this.writeSpatial(filter, "Contains"); - }, - "DWITHIN": function(filter) { - var node = this.writeSpatial(filter, "DWithin"); - this.writeNode("Distance", filter, node); - return node; - }, - "Distance": function(filter) { - return this.createElementNSPlus("ogc:Distance", { - attributes: { - units: filter.distanceUnits - }, - value: filter.distance - }); - }, - "Function": function(filter) { - var node = this.createElementNSPlus("ogc:Function", { - attributes: { - name: filter.name - } - }); - var params = filter.params; - for(var i=0, len=params.length; i<len; i++){ - this.writeOgcExpression(params[i], node); - } - return node; - }, - "PropertyIsNull": function(filter) { - var node = this.createElementNSPlus("ogc:PropertyIsNull"); - this.writeNode("PropertyName", filter, node); - return node; - } - } - }, - - /** - * Method: getFilterType - */ - getFilterType: function(filter) { - var filterType = this.filterMap[filter.type]; - if(!filterType) { - throw "Filter writing not supported for rule type: " + filter.type; - } - return filterType; - }, - - /** - * Property: filterMap - * {Object} Contains a member for each filter type. Values are node names - * for corresponding OGC Filter child elements. - */ - filterMap: { - "&&": "And", - "||": "Or", - "!": "Not", - "==": "PropertyIsEqualTo", - "!=": "PropertyIsNotEqualTo", - "<": "PropertyIsLessThan", - ">": "PropertyIsGreaterThan", - "<=": "PropertyIsLessThanOrEqualTo", - ">=": "PropertyIsGreaterThanOrEqualTo", - "..": "PropertyIsBetween", - "~": "PropertyIsLike", - "NULL": "PropertyIsNull", - "BBOX": "BBOX", - "DWITHIN": "DWITHIN", - "WITHIN": "WITHIN", - "CONTAINS": "CONTAINS", - "INTERSECTS": "INTERSECTS", - "FID": "_featureIds" - }, - - CLASS_NAME: "OpenLayers.Format.Filter.v1" - -}); -/* ====================================================================== - OpenLayers/Format/Filter/v1_0_0.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/GML/v2.js - * @requires OpenLayers/Format/Filter/v1.js - */ - -/** - * Class: OpenLayers.Format.Filter.v1_0_0 - * Write ogc:Filter version 1.0.0. - * - * Inherits from: - * - <OpenLayers.Format.GML.v2> - * - <OpenLayers.Format.Filter.v1> - */ -OpenLayers.Format.Filter.v1_0_0 = OpenLayers.Class( - OpenLayers.Format.GML.v2, OpenLayers.Format.Filter.v1, { - - /** - * Constant: VERSION - * {String} 1.0.0 - */ - VERSION: "1.0.0", - - /** - * Property: schemaLocation - * {String} http://www.opengis.net/ogc/filter/1.0.0/filter.xsd - */ - schemaLocation: "http://www.opengis.net/ogc/filter/1.0.0/filter.xsd", - - /** - * Constructor: OpenLayers.Format.Filter.v1_0_0 - * Instances of this class are not created directly. Use the - * <OpenLayers.Format.Filter> constructor instead. - * - * Parameters: - * options - {Object} An optional object whose properties will be set on - * this instance. - */ - initialize: function(options) { - OpenLayers.Format.GML.v2.prototype.initialize.apply( - this, [options] - ); - }, - - /** - * Property: readers - * Contains public functions, grouped by namespace prefix, that will - * be applied when a namespaced node is found matching the function - * name. The function will be applied in the scope of this parser - * with two arguments: the node being read and a context object passed - * from the parent. - */ - readers: { - "ogc": OpenLayers.Util.applyDefaults({ - "PropertyIsEqualTo": function(node, obj) { - var filter = new OpenLayers.Filter.Comparison({ - type: OpenLayers.Filter.Comparison.EQUAL_TO - }); - this.readChildNodes(node, filter); - obj.filters.push(filter); - }, - "PropertyIsNotEqualTo": function(node, obj) { - var filter = new OpenLayers.Filter.Comparison({ - type: OpenLayers.Filter.Comparison.NOT_EQUAL_TO - }); - this.readChildNodes(node, filter); - obj.filters.push(filter); - }, - "PropertyIsLike": function(node, obj) { - var filter = new OpenLayers.Filter.Comparison({ - type: OpenLayers.Filter.Comparison.LIKE - }); - this.readChildNodes(node, filter); - var wildCard = node.getAttribute("wildCard"); - var singleChar = node.getAttribute("singleChar"); - var esc = node.getAttribute("escape"); - filter.value2regex(wildCard, singleChar, esc); - obj.filters.push(filter); - } - }, OpenLayers.Format.Filter.v1.prototype.readers["ogc"]), - "gml": OpenLayers.Format.GML.v2.prototype.readers["gml"], - "feature": OpenLayers.Format.GML.v2.prototype.readers["feature"] - }, - - /** - * Property: writers - * As a compliment to the readers property, this structure contains public - * writing functions grouped by namespace alias and named like the - * node names they produce. - */ - writers: { - "ogc": OpenLayers.Util.applyDefaults({ - "PropertyIsEqualTo": function(filter) { - var node = this.createElementNSPlus("ogc:PropertyIsEqualTo"); - // no ogc:expression handling for PropertyName for now - this.writeNode("PropertyName", filter, node); - // handle Literals or Functions for now - this.writeOgcExpression(filter.value, node); - return node; - }, - "PropertyIsNotEqualTo": function(filter) { - var node = this.createElementNSPlus("ogc:PropertyIsNotEqualTo"); - // no ogc:expression handling for PropertyName for now - this.writeNode("PropertyName", filter, node); - // handle Literals or Functions for now - this.writeOgcExpression(filter.value, node); - return node; - }, - "PropertyIsLike": function(filter) { - var node = this.createElementNSPlus("ogc:PropertyIsLike", { - attributes: { - wildCard: "*", singleChar: ".", escape: "!" - } - }); - // no ogc:expression handling for now - this.writeNode("PropertyName", filter, node); - // convert regex string to ogc string - this.writeNode("Literal", filter.regex2value(), node); - return node; - }, - "BBOX": function(filter) { - var node = this.createElementNSPlus("ogc:BBOX"); - // PropertyName is mandatory in 1.0.0, but e.g. GeoServer also - // accepts filters without it. When this is used with - // OpenLayers.Protocol.WFS, OpenLayers.Format.WFST will set a - // missing filter.property to the geometryName that is - // configured with the protocol, which defaults to "the_geom". - // So the only way to omit this mandatory property is to not - // set the property on the filter and to set the geometryName - // on the WFS protocol to null. The latter also happens when - // the protocol is configured without a geometryName and a - // featureNS. - filter.property && this.writeNode("PropertyName", filter, node); - var box = this.writeNode("gml:Box", filter.value, node); - if(filter.projection) { - box.setAttribute("srsName", filter.projection); - } - return node; - } - }, OpenLayers.Format.Filter.v1.prototype.writers["ogc"]), - "gml": OpenLayers.Format.GML.v2.prototype.writers["gml"], - "feature": OpenLayers.Format.GML.v2.prototype.writers["feature"] - }, - - /** - * Method: writeSpatial - * - * Read a {<OpenLayers.Filter.Spatial>} filter and converts it into XML. - * - * Parameters: - * filter - {<OpenLayers.Filter.Spatial>} The filter. - * name - {String} Name of the generated XML element. - * - * Returns: - * {DOMElement} The created XML element. - */ - writeSpatial: function(filter, name) { - var node = this.createElementNSPlus("ogc:"+name); - this.writeNode("PropertyName", filter, node); - if(filter.value instanceof OpenLayers.Filter.Function) { - this.writeNode("Function", filter.value, node); - } else { - var child; - if(filter.value instanceof OpenLayers.Geometry) { - child = this.writeNode("feature:_geometry", filter.value).firstChild; - } else { - child = this.writeNode("gml:Box", filter.value); - } - if(filter.projection) { - child.setAttribute("srsName", filter.projection); - } - node.appendChild(child); - } - return node; - }, - - - CLASS_NAME: "OpenLayers.Format.Filter.v1_0_0" - -}); -/* ====================================================================== - OpenLayers/Format/WFST/v1_0_0.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/WFST/v1.js - * @requires OpenLayers/Format/Filter/v1_0_0.js - */ - -/** - * Class: OpenLayers.Format.WFST.v1_0_0 - * A format for creating WFS v1.0.0 transactions. Create a new instance with the - * <OpenLayers.Format.WFST.v1_0_0> constructor. - * - * Inherits from: - * - <OpenLayers.Format.Filter.v1_0_0> - * - <OpenLayers.Format.WFST.v1> - */ -OpenLayers.Format.WFST.v1_0_0 = OpenLayers.Class( - OpenLayers.Format.Filter.v1_0_0, OpenLayers.Format.WFST.v1, { - - /** - * Property: version - * {String} WFS version number. - */ - version: "1.0.0", - - /** - * APIProperty: srsNameInQuery - * {Boolean} If true the reference system is passed in Query requests - * via the "srsName" attribute to the "wfs:Query" element, this - * property defaults to false as it isn't WFS 1.0.0 compliant. - */ - srsNameInQuery: false, - - /** - * Property: schemaLocations - * {Object} Properties are namespace aliases, values are schema locations. - */ - schemaLocations: { - "wfs": "http://schemas.opengis.net/wfs/1.0.0/WFS-transaction.xsd" - }, - - /** - * Constructor: OpenLayers.Format.WFST.v1_0_0 - * A class for parsing and generating WFS v1.0.0 transactions. - * - * Parameters: - * options - {Object} Optional object whose properties will be set on the - * instance. - * - * Valid options properties: - * featureType - {String} Local (without prefix) feature typeName (required). - * featureNS - {String} Feature namespace (optional). - * featurePrefix - {String} Feature namespace alias (optional - only used - * if featureNS is provided). Default is 'feature'. - * geometryName - {String} Name of geometry attribute. Default is 'the_geom'. - */ - initialize: function(options) { - OpenLayers.Format.Filter.v1_0_0.prototype.initialize.apply(this, [options]); - OpenLayers.Format.WFST.v1.prototype.initialize.apply(this, [options]); - }, - - /** - * Method: readNode - * Shorthand for applying one of the named readers given the node - * namespace and local name. Readers take two args (node, obj) and - * generally extend or modify the second. - * - * Parameters: - * node - {DOMElement} The node to be read (required). - * obj - {Object} The object to be modified (optional). - * first - {Boolean} Should be set to true for the first node read. This - * is usually the readNode call in the read method. Without this being - * set, auto-configured properties will stick on subsequent reads. - * - * Returns: - * {Object} The input object, modified (or a new one if none was provided). - */ - readNode: function(node, obj, first) { - // Not the superclass, only the mixin classes inherit from - // Format.GML.v2. We need this because we don't want to get readNode - // from the superclass's superclass, which is OpenLayers.Format.XML. - return OpenLayers.Format.GML.v2.prototype.readNode.apply(this, arguments); - }, - - /** - * Property: readers - * Contains public functions, grouped by namespace prefix, that will - * be applied when a namespaced node is found matching the function - * name. The function will be applied in the scope of this parser - * with two arguments: the node being read and a context object passed - * from the parent. - */ - readers: { - "wfs": OpenLayers.Util.applyDefaults({ - "WFS_TransactionResponse": function(node, obj) { - obj.insertIds = []; - obj.success = false; - this.readChildNodes(node, obj); - }, - "InsertResult": function(node, container) { - var obj = {fids: []}; - this.readChildNodes(node, obj); - container.insertIds = container.insertIds.concat(obj.fids); - }, - "TransactionResult": function(node, obj) { - this.readChildNodes(node, obj); - }, - "Status": function(node, obj) { - this.readChildNodes(node, obj); - }, - "SUCCESS": function(node, obj) { - obj.success = true; - } - }, OpenLayers.Format.WFST.v1.prototype.readers["wfs"]), - "gml": OpenLayers.Format.GML.v2.prototype.readers["gml"], - "feature": OpenLayers.Format.GML.v2.prototype.readers["feature"], - "ogc": OpenLayers.Format.Filter.v1_0_0.prototype.readers["ogc"] - }, - - /** - * Property: writers - * As a compliment to the readers property, this structure contains public - * writing functions grouped by namespace alias and named like the - * node names they produce. - */ - writers: { - "wfs": OpenLayers.Util.applyDefaults({ - "Query": function(options) { - options = OpenLayers.Util.extend({ - featureNS: this.featureNS, - featurePrefix: this.featurePrefix, - featureType: this.featureType, - srsName: this.srsName, - srsNameInQuery: this.srsNameInQuery - }, options); - var prefix = options.featurePrefix; - var node = this.createElementNSPlus("wfs:Query", { - attributes: { - typeName: (prefix ? prefix + ":" : "") + - options.featureType - } - }); - if(options.srsNameInQuery && options.srsName) { - node.setAttribute("srsName", options.srsName); - } - if(options.featureNS) { - node.setAttribute("xmlns:" + prefix, options.featureNS); - } - if(options.propertyNames) { - for(var i=0,len = options.propertyNames.length; i<len; i++) { - this.writeNode( - "ogc:PropertyName", - {property: options.propertyNames[i]}, - node - ); - } - } - if(options.filter) { - this.setFilterProperty(options.filter); - this.writeNode("ogc:Filter", options.filter, node); - } - return node; - } - }, OpenLayers.Format.WFST.v1.prototype.writers["wfs"]), - "gml": OpenLayers.Format.GML.v2.prototype.writers["gml"], - "feature": OpenLayers.Format.GML.v2.prototype.writers["feature"], - "ogc": OpenLayers.Format.Filter.v1_0_0.prototype.writers["ogc"] - }, - - CLASS_NAME: "OpenLayers.Format.WFST.v1_0_0" -}); -/* ====================================================================== - 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/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/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/WFS.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 - */ - -/** - * Class: OpenLayers.Protocol.WFS - * Used to create a versioned WFS protocol. Default version is 1.0.0. - * - * Returns: - * {<OpenLayers.Protocol>} A WFS protocol of the given version. - * - * Example: - * (code) - * var protocol = new OpenLayers.Protocol.WFS({ - * version: "1.1.0", - * url: "http://demo.opengeo.org/geoserver/wfs", - * featureType: "tasmania_roads", - * featureNS: "http://www.openplans.org/topp", - * geometryName: "the_geom" - * }); - * (end) - * - * See the protocols for specific WFS versions for more detail. - */ -OpenLayers.Protocol.WFS = function(options) { - options = OpenLayers.Util.applyDefaults( - options, OpenLayers.Protocol.WFS.DEFAULTS - ); - var cls = OpenLayers.Protocol.WFS["v"+options.version.replace(/\./g, "_")]; - if(!cls) { - throw "Unsupported WFS version: " + options.version; - } - return new cls(options); -}; - -/** - * Function: fromWMSLayer - * Convenience function to create a WFS protocol from a WMS layer. This makes - * the assumption that a WFS requests can be issued at the same URL as - * WMS requests and that a WFS featureType exists with the same name as the - * WMS layer. - * - * This function is designed to auto-configure <url>, <featureType>, - * <featurePrefix> and <srsName> for WFS <version> 1.1.0. Note that - * srsName matching with the WMS layer will not work with WFS 1.0.0. - * - * Parameters: - * layer - {<OpenLayers.Layer.WMS>} WMS layer that has a matching WFS - * FeatureType at the same server url with the same typename. - * options - {Object} Default properties to be set on the protocol. - * - * Returns: - * {<OpenLayers.Protocol.WFS>} - */ -OpenLayers.Protocol.WFS.fromWMSLayer = function(layer, options) { - var typeName, featurePrefix; - var param = layer.params["LAYERS"]; - var parts = (OpenLayers.Util.isArray(param) ? param[0] : param).split(":"); - if(parts.length > 1) { - featurePrefix = parts[0]; - } - typeName = parts.pop(); - var protocolOptions = { - url: layer.url, - featureType: typeName, - featurePrefix: featurePrefix, - srsName: layer.projection && layer.projection.getCode() || - layer.map && layer.map.getProjectionObject().getCode(), - version: "1.1.0" - }; - return new OpenLayers.Protocol.WFS(OpenLayers.Util.applyDefaults( - options, protocolOptions - )); -}; - -/** - * Constant: OpenLayers.Protocol.WFS.DEFAULTS - */ -OpenLayers.Protocol.WFS.DEFAULTS = { - "version": "1.0.0" -}; -/* ====================================================================== - 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/Format/KML.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/Date.js - * @requires OpenLayers/Format/XML.js - * @requires OpenLayers/Feature/Vector.js - * @requires OpenLayers/Geometry/Point.js - * @requires OpenLayers/Geometry/LineString.js - * @requires OpenLayers/Geometry/Polygon.js - * @requires OpenLayers/Geometry/Collection.js - * @requires OpenLayers/Request/XMLHttpRequest.js - * @requires OpenLayers/Projection.js - */ - -/** - * Class: OpenLayers.Format.KML - * Read/Write KML. Create a new instance with the <OpenLayers.Format.KML> - * constructor. - * - * Inherits from: - * - <OpenLayers.Format.XML> - */ -OpenLayers.Format.KML = OpenLayers.Class(OpenLayers.Format.XML, { - - /** - * Property: namespaces - * {Object} Mapping of namespace aliases to namespace URIs. - */ - namespaces: { - kml: "http://www.opengis.net/kml/2.2", - gx: "http://www.google.com/kml/ext/2.2" - }, - - /** - * APIProperty: kmlns - * {String} KML Namespace to use. Defaults to 2.0 namespace. - */ - kmlns: "http://earth.google.com/kml/2.0", - - /** - * APIProperty: placemarksDesc - * {String} Name of the placemarks. Default is "No description available". - */ - placemarksDesc: "No description available", - - /** - * APIProperty: foldersName - * {String} Name of the folders. Default is "OpenLayers export". - * If set to null, no name element will be created. - */ - foldersName: "OpenLayers export", - - /** - * APIProperty: foldersDesc - * {String} Description of the folders. Default is "Exported on [date]." - * If set to null, no description element will be created. - */ - foldersDesc: "Exported on " + new Date(), - - /** - * APIProperty: extractAttributes - * {Boolean} Extract attributes from KML. Default is true. - * Extracting styleUrls requires this to be set to true - * Note that currently only Data and SimpleData - * elements are handled. - */ - extractAttributes: true, - - /** - * APIProperty: kvpAttributes - * {Boolean} Only used if extractAttributes is true. - * If set to true, attributes will be simple - * key-value pairs, compatible with other formats, - * Any displayName elements will be ignored. - * If set to false, attributes will be objects, - * retaining any displayName elements, but not - * compatible with other formats. Any CDATA in - * displayName will be read in as a string value. - * Default is false. - */ - kvpAttributes: false, - - /** - * Property: extractStyles - * {Boolean} Extract styles from KML. Default is false. - * Extracting styleUrls also requires extractAttributes to be - * set to true - */ - extractStyles: false, - - /** - * APIProperty: extractTracks - * {Boolean} Extract gx:Track elements from Placemark elements. Default - * is false. If true, features will be generated for all points in - * all gx:Track elements. Features will have a when (Date) attribute - * based on when elements in the track. If tracks include angle - * elements, features will have heading, tilt, and roll attributes. - * If track point coordinates have three values, features will have - * an altitude attribute with the third coordinate value. - */ - extractTracks: false, - - /** - * APIProperty: trackAttributes - * {Array} If <extractTracks> is true, points within gx:Track elements will - * be parsed as features with when, heading, tilt, and roll attributes. - * Any additional attribute names can be provided in <trackAttributes>. - */ - trackAttributes: null, - - /** - * Property: internalns - * {String} KML Namespace to use -- defaults to the namespace of the - * Placemark node being parsed, but falls back to kmlns. - */ - internalns: null, - - /** - * Property: features - * {Array} Array of features - * - */ - features: null, - - /** - * Property: styles - * {Object} Storage of style objects - * - */ - styles: null, - - /** - * Property: styleBaseUrl - * {String} - */ - styleBaseUrl: "", - - /** - * Property: fetched - * {Object} Storage of KML URLs that have been fetched before - * in order to prevent reloading them. - */ - fetched: null, - - /** - * APIProperty: maxDepth - * {Integer} Maximum depth for recursive loading external KML URLs - * Defaults to 0: do no external fetching - */ - maxDepth: 0, - - /** - * Constructor: OpenLayers.Format.KML - * Create a new parser for KML. - * - * Parameters: - * options - {Object} An optional object whose properties will be set on - * this instance. - */ - initialize: function(options) { - // compile regular expressions once instead of every time they are used - this.regExes = { - trimSpace: (/^\s*|\s*$/g), - removeSpace: (/\s*/g), - splitSpace: (/\s+/), - trimComma: (/\s*,\s*/g), - kmlColor: (/(\w{2})(\w{2})(\w{2})(\w{2})/), - kmlIconPalette: (/root:\/\/icons\/palette-(\d+)(\.\w+)/), - straightBracket: (/\$\[(.*?)\]/g) - }; - // KML coordinates are always in longlat WGS84 - this.externalProjection = new OpenLayers.Projection("EPSG:4326"); - - OpenLayers.Format.XML.prototype.initialize.apply(this, [options]); - }, - - /** - * APIMethod: read - * Read data from a string, and return a list of features. - * - * Parameters: - * data - {String} or {DOMElement} data to read/parse. - * - * Returns: - * {Array(<OpenLayers.Feature.Vector>)} List of features. - */ - read: function(data) { - this.features = []; - this.styles = {}; - this.fetched = {}; - - // Set default options - var options = { - depth: 0, - styleBaseUrl: this.styleBaseUrl - }; - - return this.parseData(data, options); - }, - - /** - * Method: parseData - * Read data from a string, and return a list of features. - * - * Parameters: - * data - {String} or {DOMElement} data to read/parse. - * options - {Object} Hash of options - * - * Returns: - * {Array(<OpenLayers.Feature.Vector>)} List of features. - */ - parseData: function(data, options) { - if(typeof data == "string") { - data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); - } - - // Loop throught the following node types in this order and - // process the nodes found - var types = ["Link", "NetworkLink", "Style", "StyleMap", "Placemark"]; - for(var i=0, len=types.length; i<len; ++i) { - var type = types[i]; - - var nodes = this.getElementsByTagNameNS(data, "*", type); - - // skip to next type if no nodes are found - if(nodes.length == 0) { - continue; - } - - switch (type.toLowerCase()) { - - // Fetch external links - case "link": - case "networklink": - this.parseLinks(nodes, options); - break; - - // parse style information - case "style": - if (this.extractStyles) { - this.parseStyles(nodes, options); - } - break; - case "stylemap": - if (this.extractStyles) { - this.parseStyleMaps(nodes, options); - } - break; - - // parse features - case "placemark": - this.parseFeatures(nodes, options); - break; - } - } - - return this.features; - }, - - /** - * Method: parseLinks - * Finds URLs of linked KML documents and fetches them - * - * Parameters: - * nodes - {Array} of {DOMElement} data to read/parse. - * options - {Object} Hash of options - * - */ - parseLinks: function(nodes, options) { - - // Fetch external links <NetworkLink> and <Link> - // Don't do anything if we have reached our maximum depth for recursion - if (options.depth >= this.maxDepth) { - return false; - } - - // increase depth - var newOptions = OpenLayers.Util.extend({}, options); - newOptions.depth++; - - for(var i=0, len=nodes.length; i<len; i++) { - var href = this.parseProperty(nodes[i], "*", "href"); - if(href && !this.fetched[href]) { - this.fetched[href] = true; // prevent reloading the same urls - var data = this.fetchLink(href); - if (data) { - this.parseData(data, newOptions); - } - } - } - - }, - - /** - * Method: fetchLink - * Fetches a URL and returns the result - * - * Parameters: - * href - {String} url to be fetched - * - */ - fetchLink: function(href) { - var request = OpenLayers.Request.GET({url: href, async: false}); - if (request) { - return request.responseText; - } - }, - - /** - * Method: parseStyles - * Parses <Style> nodes - * - * Parameters: - * nodes - {Array} of {DOMElement} data to read/parse. - * options - {Object} Hash of options - * - */ - parseStyles: function(nodes, options) { - for(var i=0, len=nodes.length; i<len; i++) { - var style = this.parseStyle(nodes[i]); - if(style) { - var styleName = (options.styleBaseUrl || "") + "#" + style.id; - - this.styles[styleName] = style; - } - } - }, - - /** - * Method: parseKmlColor - * Parses a kml color (in 'aabbggrr' format) and returns the corresponding - * color and opacity or null if the color is invalid. - * - * Parameters: - * kmlColor - {String} a kml formated color - * - * Returns: - * {Object} - */ - parseKmlColor: function(kmlColor) { - var color = null; - if (kmlColor) { - var matches = kmlColor.match(this.regExes.kmlColor); - if (matches) { - color = { - color: '#' + matches[4] + matches[3] + matches[2], - opacity: parseInt(matches[1], 16) / 255 - }; - } - } - return color; - }, - - /** - * Method: parseStyle - * Parses the children of a <Style> node and builds the style hash - * accordingly - * - * Parameters: - * node - {DOMElement} <Style> node - * - */ - parseStyle: function(node) { - var style = {}; - - var types = ["LineStyle", "PolyStyle", "IconStyle", "BalloonStyle", - "LabelStyle"]; - var type, styleTypeNode, nodeList, geometry, parser; - for(var i=0, len=types.length; i<len; ++i) { - type = types[i]; - styleTypeNode = this.getElementsByTagNameNS(node, "*", type)[0]; - if(!styleTypeNode) { - continue; - } - - // only deal with first geometry of this type - switch (type.toLowerCase()) { - case "linestyle": - var kmlColor = this.parseProperty(styleTypeNode, "*", "color"); - var color = this.parseKmlColor(kmlColor); - if (color) { - style["strokeColor"] = color.color; - style["strokeOpacity"] = color.opacity; - } - - var width = this.parseProperty(styleTypeNode, "*", "width"); - if (width) { - style["strokeWidth"] = width; - } - break; - - case "polystyle": - var kmlColor = this.parseProperty(styleTypeNode, "*", "color"); - var color = this.parseKmlColor(kmlColor); - if (color) { - style["fillOpacity"] = color.opacity; - style["fillColor"] = color.color; - } - // Check if fill is disabled - var fill = this.parseProperty(styleTypeNode, "*", "fill"); - if (fill == "0") { - style["fillColor"] = "none"; - } - // Check if outline is disabled - var outline = this.parseProperty(styleTypeNode, "*", "outline"); - if (outline == "0") { - style["strokeWidth"] = "0"; - } - - break; - - case "iconstyle": - // set scale - var scale = parseFloat(this.parseProperty(styleTypeNode, - "*", "scale") || 1); - - // set default width and height of icon - var width = 32 * scale; - var height = 32 * scale; - - var iconNode = this.getElementsByTagNameNS(styleTypeNode, - "*", - "Icon")[0]; - if (iconNode) { - var href = this.parseProperty(iconNode, "*", "href"); - if (href) { - - var w = this.parseProperty(iconNode, "*", "w"); - var h = this.parseProperty(iconNode, "*", "h"); - - // Settings for Google specific icons that are 64x64 - // We set the width and height to 64 and halve the - // scale to prevent icons from being too big - var google = "http://maps.google.com/mapfiles/kml"; - if (OpenLayers.String.startsWith( - href, google) && !w && !h) { - w = 64; - h = 64; - scale = scale / 2; - } - - // if only dimension is defined, make sure the - // other one has the same value - w = w || h; - h = h || w; - - if (w) { - width = parseInt(w) * scale; - } - - if (h) { - height = parseInt(h) * scale; - } - - // support for internal icons - // (/root://icons/palette-x.png) - // x and y tell the position on the palette: - // - in pixels - // - starting from the left bottom - // We translate that to a position in the list - // and request the appropriate icon from the - // google maps website - var matches = href.match(this.regExes.kmlIconPalette); - if (matches) { - var palette = matches[1]; - var file_extension = matches[2]; - - var x = this.parseProperty(iconNode, "*", "x"); - var y = this.parseProperty(iconNode, "*", "y"); - - var posX = x ? x/32 : 0; - var posY = y ? (7 - y/32) : 7; - - var pos = posY * 8 + posX; - href = "http://maps.google.com/mapfiles/kml/pal" - + palette + "/icon" + pos + file_extension; - } - - style["graphicOpacity"] = 1; // fully opaque - style["externalGraphic"] = href; - } - - } - - - // hotSpots define the offset for an Icon - var hotSpotNode = this.getElementsByTagNameNS(styleTypeNode, - "*", - "hotSpot")[0]; - if (hotSpotNode) { - var x = parseFloat(hotSpotNode.getAttribute("x")); - var y = parseFloat(hotSpotNode.getAttribute("y")); - - var xUnits = hotSpotNode.getAttribute("xunits"); - if (xUnits == "pixels") { - style["graphicXOffset"] = -x * scale; - } - else if (xUnits == "insetPixels") { - style["graphicXOffset"] = -width + (x * scale); - } - else if (xUnits == "fraction") { - style["graphicXOffset"] = -width * x; - } - - var yUnits = hotSpotNode.getAttribute("yunits"); - if (yUnits == "pixels") { - style["graphicYOffset"] = -height + (y * scale) + 1; - } - else if (yUnits == "insetPixels") { - style["graphicYOffset"] = -(y * scale) + 1; - } - else if (yUnits == "fraction") { - style["graphicYOffset"] = -height * (1 - y) + 1; - } - } - - style["graphicWidth"] = width; - style["graphicHeight"] = height; - break; - - case "balloonstyle": - var balloonStyle = OpenLayers.Util.getXmlNodeValue( - styleTypeNode); - if (balloonStyle) { - style["balloonStyle"] = balloonStyle.replace( - this.regExes.straightBracket, "${$1}"); - } - break; - case "labelstyle": - var kmlColor = this.parseProperty(styleTypeNode, "*", "color"); - var color = this.parseKmlColor(kmlColor); - if (color) { - style["fontColor"] = color.color; - style["fontOpacity"] = color.opacity; - } - break; - - default: - } - } - - // Some polygons have no line color, so we use the fillColor for that - if (!style["strokeColor"] && style["fillColor"]) { - style["strokeColor"] = style["fillColor"]; - } - - var id = node.getAttribute("id"); - if (id && style) { - style.id = id; - } - - return style; - }, - - /** - * Method: parseStyleMaps - * Parses <StyleMap> nodes, but only uses the 'normal' key - * - * Parameters: - * nodes - {Array} of {DOMElement} data to read/parse. - * options - {Object} Hash of options - * - */ - parseStyleMaps: function(nodes, options) { - // Only the default or "normal" part of the StyleMap is processed now - // To do the select or "highlight" bit, we'd need to change lots more - - for(var i=0, len=nodes.length; i<len; i++) { - var node = nodes[i]; - var pairs = this.getElementsByTagNameNS(node, "*", - "Pair"); - - var id = node.getAttribute("id"); - for (var j=0, jlen=pairs.length; j<jlen; j++) { - var pair = pairs[j]; - // Use the shortcut in the SLD format to quickly retrieve the - // value of a node. Maybe it's good to have a method in - // Format.XML to do this - var key = this.parseProperty(pair, "*", "key"); - var styleUrl = this.parseProperty(pair, "*", "styleUrl"); - - if (styleUrl && key == "normal") { - this.styles[(options.styleBaseUrl || "") + "#" + id] = - this.styles[(options.styleBaseUrl || "") + styleUrl]; - } - - // TODO: implement the "select" part - //if (styleUrl && key == "highlight") { - //} - - } - } - - }, - - - /** - * Method: parseFeatures - * Loop through all Placemark nodes and parse them. - * Will create a list of features - * - * Parameters: - * nodes - {Array} of {DOMElement} data to read/parse. - * options - {Object} Hash of options - * - */ - parseFeatures: function(nodes, options) { - var features = []; - for(var i=0, len=nodes.length; i<len; i++) { - var featureNode = nodes[i]; - var feature = this.parseFeature.apply(this,[featureNode]) ; - if(feature) { - - // Create reference to styleUrl - if (this.extractStyles && feature.attributes && - feature.attributes.styleUrl) { - feature.style = this.getStyle(feature.attributes.styleUrl, options); - } - - if (this.extractStyles) { - // Make sure that <Style> nodes within a placemark are - // processed as well - var inlineStyleNode = this.getElementsByTagNameNS(featureNode, - "*", - "Style")[0]; - if (inlineStyleNode) { - var inlineStyle= this.parseStyle(inlineStyleNode); - if (inlineStyle) { - feature.style = OpenLayers.Util.extend( - feature.style, inlineStyle - ); - } - } - } - - // check if gx:Track elements should be parsed - if (this.extractTracks) { - var tracks = this.getElementsByTagNameNS( - featureNode, this.namespaces.gx, "Track" - ); - if (tracks && tracks.length > 0) { - var track = tracks[0]; - var container = { - features: [], - feature: feature - }; - this.readNode(track, container); - if (container.features.length > 0) { - features.push.apply(features, container.features); - } - } - } else { - // add feature to list of features - features.push(feature); - } - } else { - throw "Bad Placemark: " + i; - } - } - - // add new features to existing feature list - this.features = this.features.concat(features); - }, - - /** - * Property: readers - * Contains public functions, grouped by namespace prefix, that will - * be applied when a namespaced node is found matching the function - * name. The function will be applied in the scope of this parser - * with two arguments: the node being read and a context object passed - * from the parent. - */ - readers: { - "kml": { - "when": function(node, container) { - container.whens.push(OpenLayers.Date.parse( - this.getChildValue(node) - )); - }, - "_trackPointAttribute": function(node, container) { - var name = node.nodeName.split(":").pop(); - container.attributes[name].push(this.getChildValue(node)); - } - }, - "gx": { - "Track": function(node, container) { - var obj = { - whens: [], - points: [], - angles: [] - }; - if (this.trackAttributes) { - var name; - obj.attributes = {}; - for (var i=0, ii=this.trackAttributes.length; i<ii; ++i) { - name = this.trackAttributes[i]; - obj.attributes[name] = []; - if (!(name in this.readers.kml)) { - this.readers.kml[name] = this.readers.kml._trackPointAttribute; - } - } - } - this.readChildNodes(node, obj); - if (obj.whens.length !== obj.points.length) { - throw new Error("gx:Track with unequal number of when (" + - obj.whens.length + ") and gx:coord (" + - obj.points.length + ") elements."); - } - var hasAngles = obj.angles.length > 0; - if (hasAngles && obj.whens.length !== obj.angles.length) { - throw new Error("gx:Track with unequal number of when (" + - obj.whens.length + ") and gx:angles (" + - obj.angles.length + ") elements."); - } - var feature, point, angles; - for (var i=0, ii=obj.whens.length; i<ii; ++i) { - feature = container.feature.clone(); - feature.fid = container.feature.fid || container.feature.id; - point = obj.points[i]; - feature.geometry = point; - if ("z" in point) { - feature.attributes.altitude = point.z; - } - if (this.internalProjection && this.externalProjection) { - feature.geometry.transform( - this.externalProjection, this.internalProjection - ); - } - if (this.trackAttributes) { - for (var j=0, jj=this.trackAttributes.length; j<jj; ++j) { - var name = this.trackAttributes[j]; - feature.attributes[name] = obj.attributes[name][i]; - } - } - feature.attributes.when = obj.whens[i]; - feature.attributes.trackId = container.feature.id; - if (hasAngles) { - angles = obj.angles[i]; - feature.attributes.heading = parseFloat(angles[0]); - feature.attributes.tilt = parseFloat(angles[1]); - feature.attributes.roll = parseFloat(angles[2]); - } - container.features.push(feature); - } - }, - "coord": function(node, container) { - var str = this.getChildValue(node); - var coords = str.replace(this.regExes.trimSpace, "").split(/\s+/); - var point = new OpenLayers.Geometry.Point(coords[0], coords[1]); - if (coords.length > 2) { - point.z = parseFloat(coords[2]); - } - container.points.push(point); - }, - "angles": function(node, container) { - var str = this.getChildValue(node); - var parts = str.replace(this.regExes.trimSpace, "").split(/\s+/); - container.angles.push(parts); - } - } - }, - - /** - * Method: parseFeature - * This function is the core of the KML parsing code in OpenLayers. - * It creates the geometries that are then attached to the returned - * feature, and calls parseAttributes() to get attribute data out. - * - * Parameters: - * node - {DOMElement} - * - * Returns: - * {<OpenLayers.Feature.Vector>} A vector feature. - */ - parseFeature: function(node) { - // only accept one geometry per feature - look for highest "order" - var order = ["MultiGeometry", "Polygon", "LineString", "Point"]; - var type, nodeList, geometry, parser; - for(var i=0, len=order.length; i<len; ++i) { - type = order[i]; - this.internalns = node.namespaceURI ? - node.namespaceURI : this.kmlns; - nodeList = this.getElementsByTagNameNS(node, - this.internalns, type); - if(nodeList.length > 0) { - // only deal with first geometry of this type - var parser = this.parseGeometry[type.toLowerCase()]; - if(parser) { - geometry = parser.apply(this, [nodeList[0]]); - if (this.internalProjection && this.externalProjection) { - geometry.transform(this.externalProjection, - this.internalProjection); - } - } else { - throw new TypeError("Unsupported geometry type: " + type); - } - // stop looking for different geometry types - break; - } - } - - // construct feature (optionally with attributes) - var attributes; - if(this.extractAttributes) { - attributes = this.parseAttributes(node); - } - var feature = new OpenLayers.Feature.Vector(geometry, attributes); - - var fid = node.getAttribute("id") || node.getAttribute("name"); - if(fid != null) { - feature.fid = fid; - } - - return feature; - }, - - /** - * Method: getStyle - * Retrieves a style from a style hash using styleUrl as the key - * If the styleUrl doesn't exist yet, we try to fetch it - * Internet - * - * Parameters: - * styleUrl - {String} URL of style - * options - {Object} Hash of options - * - * Returns: - * {Object} - (reference to) Style hash - */ - getStyle: function(styleUrl, options) { - - var styleBaseUrl = OpenLayers.Util.removeTail(styleUrl); - - var newOptions = OpenLayers.Util.extend({}, options); - newOptions.depth++; - newOptions.styleBaseUrl = styleBaseUrl; - - // Fetch remote Style URLs (if not fetched before) - if (!this.styles[styleUrl] - && !OpenLayers.String.startsWith(styleUrl, "#") - && newOptions.depth <= this.maxDepth - && !this.fetched[styleBaseUrl] ) { - - var data = this.fetchLink(styleBaseUrl); - if (data) { - this.parseData(data, newOptions); - } - - } - - // return requested style - var style = OpenLayers.Util.extend({}, this.styles[styleUrl]); - return style; - }, - - /** - * Property: parseGeometry - * Properties of this object are the functions that parse geometries based - * on their type. - */ - parseGeometry: { - - /** - * Method: parseGeometry.point - * Given a KML node representing a point geometry, create an OpenLayers - * point geometry. - * - * Parameters: - * node - {DOMElement} A KML Point node. - * - * Returns: - * {<OpenLayers.Geometry.Point>} A point geometry. - */ - point: function(node) { - var nodeList = this.getElementsByTagNameNS(node, this.internalns, - "coordinates"); - var coords = []; - if(nodeList.length > 0) { - var coordString = nodeList[0].firstChild.nodeValue; - coordString = coordString.replace(this.regExes.removeSpace, ""); - coords = coordString.split(","); - } - - var point = null; - if(coords.length > 1) { - // preserve third dimension - if(coords.length == 2) { - coords[2] = null; - } - point = new OpenLayers.Geometry.Point(coords[0], coords[1], - coords[2]); - } else { - throw "Bad coordinate string: " + coordString; - } - return point; - }, - - /** - * Method: parseGeometry.linestring - * Given a KML node representing a linestring geometry, create an - * OpenLayers linestring geometry. - * - * Parameters: - * node - {DOMElement} A KML LineString node. - * - * Returns: - * {<OpenLayers.Geometry.LineString>} A linestring geometry. - */ - linestring: function(node, ring) { - var nodeList = this.getElementsByTagNameNS(node, this.internalns, - "coordinates"); - var line = null; - if(nodeList.length > 0) { - var coordString = this.getChildValue(nodeList[0]); - - coordString = coordString.replace(this.regExes.trimSpace, - ""); - coordString = coordString.replace(this.regExes.trimComma, - ","); - var pointList = coordString.split(this.regExes.splitSpace); - var numPoints = pointList.length; - var points = new Array(numPoints); - var coords, numCoords; - for(var i=0; i<numPoints; ++i) { - coords = pointList[i].split(","); - numCoords = coords.length; - if(numCoords > 1) { - if(coords.length == 2) { - coords[2] = null; - } - points[i] = new OpenLayers.Geometry.Point(coords[0], - coords[1], - coords[2]); - } else { - throw "Bad LineString point coordinates: " + - pointList[i]; - } - } - if(numPoints) { - if(ring) { - line = new OpenLayers.Geometry.LinearRing(points); - } else { - line = new OpenLayers.Geometry.LineString(points); - } - } else { - throw "Bad LineString coordinates: " + coordString; - } - } - - return line; - }, - - /** - * Method: parseGeometry.polygon - * Given a KML node representing a polygon geometry, create an - * OpenLayers polygon geometry. - * - * Parameters: - * node - {DOMElement} A KML Polygon node. - * - * Returns: - * {<OpenLayers.Geometry.Polygon>} A polygon geometry. - */ - polygon: function(node) { - var nodeList = this.getElementsByTagNameNS(node, this.internalns, - "LinearRing"); - var numRings = nodeList.length; - var components = new Array(numRings); - if(numRings > 0) { - // this assumes exterior ring first, inner rings after - var ring; - for(var i=0, len=nodeList.length; i<len; ++i) { - ring = this.parseGeometry.linestring.apply(this, - [nodeList[i], true]); - if(ring) { - components[i] = ring; - } else { - throw "Bad LinearRing geometry: " + i; - } - } - } - return new OpenLayers.Geometry.Polygon(components); - }, - - /** - * Method: parseGeometry.multigeometry - * Given a KML node representing a multigeometry, create an - * OpenLayers geometry collection. - * - * Parameters: - * node - {DOMElement} A KML MultiGeometry node. - * - * Returns: - * {<OpenLayers.Geometry.Collection>} A geometry collection. - */ - multigeometry: function(node) { - var child, parser; - var parts = []; - var children = node.childNodes; - for(var i=0, len=children.length; i<len; ++i ) { - child = children[i]; - if(child.nodeType == 1) { - var type = (child.prefix) ? - child.nodeName.split(":")[1] : - child.nodeName; - var parser = this.parseGeometry[type.toLowerCase()]; - if(parser) { - parts.push(parser.apply(this, [child])); - } - } - } - return new OpenLayers.Geometry.Collection(parts); - } - - }, - - /** - * Method: parseAttributes - * - * Parameters: - * node - {DOMElement} - * - * Returns: - * {Object} An attributes object. - */ - parseAttributes: function(node) { - var attributes = {}; - - // Extended Data is parsed first. - var edNodes = node.getElementsByTagName("ExtendedData"); - if (edNodes.length) { - attributes = this.parseExtendedData(edNodes[0]); - } - - // assume attribute nodes are type 1 children with a type 3 or 4 child - var child, grandchildren, grandchild; - var children = node.childNodes; - - for(var i=0, len=children.length; i<len; ++i) { - child = children[i]; - if(child.nodeType == 1) { - grandchildren = child.childNodes; - if(grandchildren.length >= 1 && grandchildren.length <= 3) { - var grandchild; - switch (grandchildren.length) { - case 1: - grandchild = grandchildren[0]; - break; - case 2: - var c1 = grandchildren[0]; - var c2 = grandchildren[1]; - grandchild = (c1.nodeType == 3 || c1.nodeType == 4) ? - c1 : c2; - break; - case 3: - default: - grandchild = grandchildren[1]; - break; - } - if(grandchild.nodeType == 3 || grandchild.nodeType == 4) { - var name = (child.prefix) ? - child.nodeName.split(":")[1] : - child.nodeName; - var value = OpenLayers.Util.getXmlNodeValue(grandchild); - if (value) { - value = value.replace(this.regExes.trimSpace, ""); - attributes[name] = value; - } - } - } - } - } - return attributes; - }, - - /** - * Method: parseExtendedData - * Parse ExtendedData from KML. Limited support for schemas/datatypes. - * See http://code.google.com/apis/kml/documentation/kmlreference.html#extendeddata - * for more information on extendeddata. - */ - parseExtendedData: function(node) { - var attributes = {}; - var i, len, data, key; - var dataNodes = node.getElementsByTagName("Data"); - for (i = 0, len = dataNodes.length; i < len; i++) { - data = dataNodes[i]; - key = data.getAttribute("name"); - var ed = {}; - var valueNode = data.getElementsByTagName("value"); - if (valueNode.length) { - ed['value'] = this.getChildValue(valueNode[0]); - } - if (this.kvpAttributes) { - attributes[key] = ed['value']; - } else { - var nameNode = data.getElementsByTagName("displayName"); - if (nameNode.length) { - ed['displayName'] = this.getChildValue(nameNode[0]); - } - attributes[key] = ed; - } - } - var simpleDataNodes = node.getElementsByTagName("SimpleData"); - for (i = 0, len = simpleDataNodes.length; i < len; i++) { - var ed = {}; - data = simpleDataNodes[i]; - key = data.getAttribute("name"); - ed['value'] = this.getChildValue(data); - if (this.kvpAttributes) { - attributes[key] = ed['value']; - } else { - ed['displayName'] = key; - attributes[key] = ed; - } - } - - return attributes; - }, - - /** - * Method: parseProperty - * Convenience method to find a node and return its value - * - * Parameters: - * xmlNode - {<DOMElement>} - * namespace - {String} namespace of the node to find - * tagName - {String} name of the property to parse - * - * Returns: - * {String} The value for the requested property (defaults to null) - */ - parseProperty: function(xmlNode, namespace, tagName) { - var value; - var nodeList = this.getElementsByTagNameNS(xmlNode, namespace, tagName); - try { - value = OpenLayers.Util.getXmlNodeValue(nodeList[0]); - } catch(e) { - value = null; - } - - return value; - }, - - /** - * APIMethod: write - * Accept Feature Collection, and return a string. - * - * Parameters: - * features - {Array(<OpenLayers.Feature.Vector>)} An array of features. - * - * Returns: - * {String} A KML string. - */ - write: function(features) { - if(!(OpenLayers.Util.isArray(features))) { - features = [features]; - } - var kml = this.createElementNS(this.kmlns, "kml"); - var folder = this.createFolderXML(); - for(var i=0, len=features.length; i<len; ++i) { - folder.appendChild(this.createPlacemarkXML(features[i])); - } - kml.appendChild(folder); - return OpenLayers.Format.XML.prototype.write.apply(this, [kml]); - }, - - /** - * Method: createFolderXML - * Creates and returns a KML folder node - * - * Returns: - * {DOMElement} - */ - createFolderXML: function() { - // Folder - var folder = this.createElementNS(this.kmlns, "Folder"); - - // Folder name - if (this.foldersName) { - var folderName = this.createElementNS(this.kmlns, "name"); - var folderNameText = this.createTextNode(this.foldersName); - folderName.appendChild(folderNameText); - folder.appendChild(folderName); - } - - // Folder description - if (this.foldersDesc) { - var folderDesc = this.createElementNS(this.kmlns, "description"); - var folderDescText = this.createTextNode(this.foldersDesc); - folderDesc.appendChild(folderDescText); - folder.appendChild(folderDesc); - } - - return folder; - }, - - /** - * Method: createPlacemarkXML - * Creates and returns a KML placemark node representing the given feature. - * - * Parameters: - * feature - {<OpenLayers.Feature.Vector>} - * - * Returns: - * {DOMElement} - */ - createPlacemarkXML: function(feature) { - // Placemark name - var placemarkName = this.createElementNS(this.kmlns, "name"); - var label = (feature.style && feature.style.label) ? feature.style.label : feature.id; - var name = feature.attributes.name || label; - placemarkName.appendChild(this.createTextNode(name)); - - // Placemark description - var placemarkDesc = this.createElementNS(this.kmlns, "description"); - var desc = feature.attributes.description || this.placemarksDesc; - placemarkDesc.appendChild(this.createTextNode(desc)); - - // Placemark - var placemarkNode = this.createElementNS(this.kmlns, "Placemark"); - if(feature.fid != null) { - placemarkNode.setAttribute("id", feature.fid); - } - placemarkNode.appendChild(placemarkName); - placemarkNode.appendChild(placemarkDesc); - - // Geometry node (Point, LineString, etc. nodes) - var geometryNode = this.buildGeometryNode(feature.geometry); - placemarkNode.appendChild(geometryNode); - - // output attributes as extendedData - if (feature.attributes) { - var edNode = this.buildExtendedData(feature.attributes); - if (edNode) { - placemarkNode.appendChild(edNode); - } - } - - return placemarkNode; - }, - - /** - * Method: buildGeometryNode - * Builds and returns a KML geometry node with the given geometry. - * - * Parameters: - * geometry - {<OpenLayers.Geometry>} - * - * Returns: - * {DOMElement} - */ - buildGeometryNode: function(geometry) { - var className = geometry.CLASS_NAME; - var type = className.substring(className.lastIndexOf(".") + 1); - var builder = this.buildGeometry[type.toLowerCase()]; - var node = null; - if(builder) { - node = builder.apply(this, [geometry]); - } - return node; - }, - - /** - * Property: buildGeometry - * Object containing methods to do the actual geometry node building - * based on geometry type. - */ - buildGeometry: { - // TBD: Anybody care about namespace aliases here (these nodes have - // no prefixes)? - - /** - * Method: buildGeometry.point - * Given an OpenLayers point geometry, create a KML point. - * - * Parameters: - * geometry - {<OpenLayers.Geometry.Point>} A point geometry. - * - * Returns: - * {DOMElement} A KML point node. - */ - point: function(geometry) { - var kml = this.createElementNS(this.kmlns, "Point"); - kml.appendChild(this.buildCoordinatesNode(geometry)); - return kml; - }, - - /** - * Method: buildGeometry.multipoint - * Given an OpenLayers multipoint geometry, create a KML - * GeometryCollection. - * - * Parameters: - * geometry - {<OpenLayers.Geometry.Point>} A multipoint geometry. - * - * Returns: - * {DOMElement} A KML GeometryCollection node. - */ - multipoint: function(geometry) { - return this.buildGeometry.collection.apply(this, [geometry]); - }, - - /** - * Method: buildGeometry.linestring - * Given an OpenLayers linestring geometry, create a KML linestring. - * - * Parameters: - * geometry - {<OpenLayers.Geometry.LineString>} A linestring geometry. - * - * Returns: - * {DOMElement} A KML linestring node. - */ - linestring: function(geometry) { - var kml = this.createElementNS(this.kmlns, "LineString"); - kml.appendChild(this.buildCoordinatesNode(geometry)); - return kml; - }, - - /** - * Method: buildGeometry.multilinestring - * Given an OpenLayers multilinestring geometry, create a KML - * GeometryCollection. - * - * Parameters: - * geometry - {<OpenLayers.Geometry.Point>} A multilinestring geometry. - * - * Returns: - * {DOMElement} A KML GeometryCollection node. - */ - multilinestring: function(geometry) { - return this.buildGeometry.collection.apply(this, [geometry]); - }, - - /** - * Method: buildGeometry.linearring - * Given an OpenLayers linearring geometry, create a KML linearring. - * - * Parameters: - * geometry - {<OpenLayers.Geometry.LinearRing>} A linearring geometry. - * - * Returns: - * {DOMElement} A KML linearring node. - */ - linearring: function(geometry) { - var kml = this.createElementNS(this.kmlns, "LinearRing"); - kml.appendChild(this.buildCoordinatesNode(geometry)); - return kml; - }, - - /** - * Method: buildGeometry.polygon - * Given an OpenLayers polygon geometry, create a KML polygon. - * - * Parameters: - * geometry - {<OpenLayers.Geometry.Polygon>} A polygon geometry. - * - * Returns: - * {DOMElement} A KML polygon node. - */ - polygon: function(geometry) { - var kml = this.createElementNS(this.kmlns, "Polygon"); - var rings = geometry.components; - var ringMember, ringGeom, type; - for(var i=0, len=rings.length; i<len; ++i) { - type = (i==0) ? "outerBoundaryIs" : "innerBoundaryIs"; - ringMember = this.createElementNS(this.kmlns, type); - ringGeom = this.buildGeometry.linearring.apply(this, - [rings[i]]); - ringMember.appendChild(ringGeom); - kml.appendChild(ringMember); - } - return kml; - }, - - /** - * Method: buildGeometry.multipolygon - * Given an OpenLayers multipolygon geometry, create a KML - * GeometryCollection. - * - * Parameters: - * geometry - {<OpenLayers.Geometry.Point>} A multipolygon geometry. - * - * Returns: - * {DOMElement} A KML GeometryCollection node. - */ - multipolygon: function(geometry) { - return this.buildGeometry.collection.apply(this, [geometry]); - }, - - /** - * Method: buildGeometry.collection - * Given an OpenLayers geometry collection, create a KML MultiGeometry. - * - * Parameters: - * geometry - {<OpenLayers.Geometry.Collection>} A geometry collection. - * - * Returns: - * {DOMElement} A KML MultiGeometry node. - */ - collection: function(geometry) { - var kml = this.createElementNS(this.kmlns, "MultiGeometry"); - var child; - for(var i=0, len=geometry.components.length; i<len; ++i) { - child = this.buildGeometryNode.apply(this, - [geometry.components[i]]); - if(child) { - kml.appendChild(child); - } - } - return kml; - } - }, - - /** - * Method: buildCoordinatesNode - * Builds and returns the KML coordinates node with the given geometry - * <coordinates>...</coordinates> - * - * Parameters: - * geometry - {<OpenLayers.Geometry>} - * - * Returns: - * {DOMElement} - */ - buildCoordinatesNode: function(geometry) { - var coordinatesNode = this.createElementNS(this.kmlns, "coordinates"); - - var path; - var points = geometry.components; - if(points) { - // LineString or LinearRing - var point; - var numPoints = points.length; - var parts = new Array(numPoints); - for(var i=0; i<numPoints; ++i) { - point = points[i]; - parts[i] = this.buildCoordinates(point); - } - path = parts.join(" "); - } else { - // Point - path = this.buildCoordinates(geometry); - } - - var txtNode = this.createTextNode(path); - coordinatesNode.appendChild(txtNode); - - return coordinatesNode; - }, - - /** - * Method: buildCoordinates - * - * Parameters: - * point - {<OpenLayers.Geometry.Point>} - * - * Returns - * {String} a coordinate pair - */ - buildCoordinates: function(point) { - if (this.internalProjection && this.externalProjection) { - point = point.clone(); - point.transform(this.internalProjection, - this.externalProjection); - } - return point.x + "," + point.y; - }, - - /** - * Method: buildExtendedData - * - * Parameters: - * attributes - {Object} - * - * Returns - * {DOMElement} A KML ExtendedData node or {null} if no attributes. - */ - buildExtendedData: function(attributes) { - var extendedData = this.createElementNS(this.kmlns, "ExtendedData"); - for (var attributeName in attributes) { - // empty, name, description, styleUrl attributes ignored - if (attributes[attributeName] && attributeName != "name" && attributeName != "description" && attributeName != "styleUrl") { - var data = this.createElementNS(this.kmlns, "Data"); - data.setAttribute("name", attributeName); - var value = this.createElementNS(this.kmlns, "value"); - if (typeof attributes[attributeName] == "object") { - // cater for object attributes with 'value' properties - // other object properties will output an empty node - if (attributes[attributeName].value) { - value.appendChild(this.createTextNode(attributes[attributeName].value)); - } - if (attributes[attributeName].displayName) { - var displayName = this.createElementNS(this.kmlns, "displayName"); - // displayName always written as CDATA - displayName.appendChild(this.getXMLDoc().createCDATASection(attributes[attributeName].displayName)); - data.appendChild(displayName); - } - } else { - value.appendChild(this.createTextNode(attributes[attributeName])); - } - data.appendChild(value); - extendedData.appendChild(data); - } - } - if (this.isSimpleContent(extendedData)) { - return null; - } else { - return extendedData; - } - }, - - CLASS_NAME: "OpenLayers.Format.KML" -}); -/* ====================================================================== - OpenLayers/Protocol/WFS/v1.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/WFS.js - */ - -/** - * Class: OpenLayers.Protocol.WFS.v1 - * Abstract class for for v1.0.0 and v1.1.0 protocol. - * - * Inherits from: - * - <OpenLayers.Protocol> - */ -OpenLayers.Protocol.WFS.v1 = OpenLayers.Class(OpenLayers.Protocol, { - - /** - * Property: version - * {String} WFS version number. - */ - version: null, - - /** - * Property: srsName - * {String} Name of spatial reference system. Default is "EPSG:4326". - */ - srsName: "EPSG:4326", - - /** - * Property: featureType - * {String} Local feature typeName. - */ - featureType: null, - - /** - * Property: featureNS - * {String} Feature namespace. - */ - featureNS: null, - - /** - * Property: geometryName - * {String} Name of the geometry attribute for features. Default is - * "the_geom" for WFS <version> 1.0, and null for higher versions. - */ - geometryName: "the_geom", - - /** - * Property: maxFeatures - * {Integer} Optional maximum number of features to retrieve. - */ - - /** - * Property: schema - * {String} Optional schema location that will be included in the - * schemaLocation attribute value. Note that the feature type schema - * is required for a strict XML validator (on transactions with an - * insert for example), but is *not* required by the WFS specification - * (since the server is supposed to know about feature type schemas). - */ - schema: null, - - /** - * Property: featurePrefix - * {String} Namespace alias for feature type. Default is "feature". - */ - featurePrefix: "feature", - - /** - * Property: formatOptions - * {Object} Optional options for the format. If a format is not provided, - * this property can be used to extend the default format options. - */ - formatOptions: null, - - /** - * Property: readFormat - * {<OpenLayers.Format>} For WFS requests it is possible to get a - * different output format than GML. In that case, we cannot parse - * the response with the default format (WFST) and we need a different - * format for reading. - */ - readFormat: null, - - /** - * Property: readOptions - * {Object} Optional object to pass to format's read. - */ - readOptions: null, - - /** - * Constructor: OpenLayers.Protocol.WFS - * A class for giving layers WFS protocol. - * - * Parameters: - * options - {Object} Optional object whose properties will be set on the - * instance. - * - * Valid options properties: - * url - {String} URL to send requests to (required). - * featureType - {String} Local (without prefix) feature typeName (required). - * featureNS - {String} Feature namespace (required, but can be autodetected - * during the first query if GML is used as readFormat and - * featurePrefix is provided and matches the prefix used by the server - * for this featureType). - * featurePrefix - {String} Feature namespace alias (optional - only used - * for writing if featureNS is provided). Default is 'feature'. - * geometryName - {String} Name of geometry attribute. The default is - * 'the_geom' for WFS <version> 1.0, and null for higher versions. If - * null, it will be set to the name of the first geometry found in the - * first read operation. - * multi - {Boolean} If set to true, geometries will be casted to Multi - * geometries before they are written in a transaction. No casting will - * be done when reading features. - */ - initialize: function(options) { - OpenLayers.Protocol.prototype.initialize.apply(this, [options]); - if(!options.format) { - this.format = OpenLayers.Format.WFST(OpenLayers.Util.extend({ - version: this.version, - featureType: this.featureType, - featureNS: this.featureNS, - featurePrefix: this.featurePrefix, - geometryName: this.geometryName, - srsName: this.srsName, - schema: this.schema - }, this.formatOptions)); - } - if (!options.geometryName && parseFloat(this.format.version) > 1.0) { - this.setGeometryName(null); - } - }, - - /** - * APIMethod: destroy - * Clean up the protocol. - */ - destroy: function() { - if(this.options && !this.options.format) { - this.format.destroy(); - } - this.format = null; - OpenLayers.Protocol.prototype.destroy.apply(this); - }, - - /** - * APIMethod: read - * Construct a request for reading new features. Since WFS splits the - * basic CRUD operations into GetFeature requests (for read) and - * Transactions (for all others), this method does not make use of the - * format's read method (that is only about reading transaction - * responses). - * - * Parameters: - * options - {Object} Options for the read operation, in addition to the - * options set on the instance (options set here will take precedence). - * - * To use a configured protocol to get e.g. a WFS hit count, applications - * could do the following: - * - * (code) - * protocol.read({ - * readOptions: {output: "object"}, - * resultType: "hits", - * maxFeatures: null, - * callback: function(resp) { - * // process resp.numberOfFeatures here - * } - * }); - * (end) - * - * To use a configured protocol to use WFS paging (if supported by the - * server), applications could do the following: - * - * (code) - * protocol.read({ - * startIndex: 0, - * count: 50 - * }); - * (end) - * - * To limit the attributes returned by the GetFeature request, applications - * can use the propertyNames option to specify the properties to include in - * the response: - * - * (code) - * protocol.read({ - * propertyNames: ["DURATION", "INTENSITY"] - * }); - * (end) - */ - read: function(options) { - OpenLayers.Protocol.prototype.read.apply(this, arguments); - options = OpenLayers.Util.extend({}, options); - OpenLayers.Util.applyDefaults(options, this.options || {}); - var response = new OpenLayers.Protocol.Response({requestType: "read"}); - - var data = OpenLayers.Format.XML.prototype.write.apply( - this.format, [this.format.writeNode("wfs:GetFeature", options)] - ); - - response.priv = OpenLayers.Request.POST({ - url: options.url, - callback: this.createCallback(this.handleRead, response, options), - params: options.params, - headers: options.headers, - data: data - }); - - return response; - }, - - /** - * APIMethod: setFeatureType - * Change the feature type on the fly. - * - * Parameters: - * featureType - {String} Local (without prefix) feature typeName. - */ - setFeatureType: function(featureType) { - this.featureType = featureType; - this.format.featureType = featureType; - }, - - /** - * APIMethod: setGeometryName - * Sets the geometryName option after instantiation. - * - * Parameters: - * geometryName - {String} Name of geometry attribute. - */ - setGeometryName: function(geometryName) { - this.geometryName = geometryName; - this.format.geometryName = geometryName; - }, - - /** - * Method: handleRead - * Deal with response from the read request. - * - * Parameters: - * response - {<OpenLayers.Protocol.Response>} The response object to pass - * to the user callback. - * options - {Object} The user options passed to the read call. - */ - handleRead: function(response, options) { - options = OpenLayers.Util.extend({}, options); - OpenLayers.Util.applyDefaults(options, this.options); - - if(options.callback) { - var request = response.priv; - if(request.status >= 200 && request.status < 300) { - // success - var result = this.parseResponse(request, options.readOptions); - if (result && result.success !== false) { - if (options.readOptions && options.readOptions.output == "object") { - OpenLayers.Util.extend(response, result); - } else { - response.features = result; - } - response.code = OpenLayers.Protocol.Response.SUCCESS; - } else { - // failure (service exception) - response.code = OpenLayers.Protocol.Response.FAILURE; - response.error = result; - } - } else { - // failure - response.code = OpenLayers.Protocol.Response.FAILURE; - } - options.callback.call(options.scope, response); - } - }, - - /** - * Method: parseResponse - * Read HTTP response body and return features - * - * Parameters: - * request - {XMLHttpRequest} The request object - * options - {Object} Optional object to pass to format's read - * - * Returns: - * {Object} or {Array({<OpenLayers.Feature.Vector>})} or - * {<OpenLayers.Feature.Vector>} - * An object with a features property, an array of features or a single - * feature. - */ - parseResponse: function(request, options) { - var doc = request.responseXML; - if(!doc || !doc.documentElement) { - doc = request.responseText; - } - if(!doc || doc.length <= 0) { - return null; - } - var result = (this.readFormat !== null) ? this.readFormat.read(doc) : - this.format.read(doc, options); - if (!this.featureNS) { - var format = this.readFormat || this.format; - this.featureNS = format.featureNS; - // no need to auto-configure again on subsequent reads - format.autoConfig = false; - if (!this.geometryName) { - this.setGeometryName(format.geometryName); - } - } - return result; - }, - - /** - * Method: commit - * Given a list of feature, assemble a batch request for update, create, - * and delete transactions. A commit call on the prototype amounts - * to writing a WFS transaction - so the write method on the format - * is used. - * - * Parameters: - * features - {Array(<OpenLayers.Feature.Vector>)} - * options - {Object} - * - * Valid options properties: - * nativeElements - {Array({Object})} Array of objects with information for writing - * out <Native> elements, these objects have vendorId, safeToIgnore and - * value properties. The <Native> element is intended to allow access to - * vendor specific capabilities of any particular web feature server or - * datastore. - * - * Returns: - * {<OpenLayers.Protocol.Response>} A response object with a features - * property containing any insertIds and a priv property referencing - * the XMLHttpRequest object. - */ - commit: function(features, options) { - - options = OpenLayers.Util.extend({}, options); - OpenLayers.Util.applyDefaults(options, this.options); - - var response = new OpenLayers.Protocol.Response({ - requestType: "commit", - reqFeatures: features - }); - response.priv = OpenLayers.Request.POST({ - url: options.url, - headers: options.headers, - data: this.format.write(features, options), - callback: this.createCallback(this.handleCommit, response, options) - }); - - return response; - }, - - /** - * Method: handleCommit - * Called when the commit request returns. - * - * Parameters: - * response - {<OpenLayers.Protocol.Response>} The response object to pass - * to the user callback. - * options - {Object} The user options passed to the commit call. - */ - handleCommit: function(response, options) { - if(options.callback) { - var request = response.priv; - - // ensure that we have an xml doc - var data = request.responseXML; - if(!data || !data.documentElement) { - data = request.responseText; - } - - var obj = this.format.read(data) || {}; - - response.insertIds = obj.insertIds || []; - if (obj.success) { - response.code = OpenLayers.Protocol.Response.SUCCESS; - } else { - response.code = OpenLayers.Protocol.Response.FAILURE; - response.error = obj; - } - options.callback.call(options.scope, response); - } - }, - - /** - * Method: filterDelete - * Send a request that deletes all features by their filter. - * - * Parameters: - * filter - {<OpenLayers.Filter>} filter - */ - filterDelete: function(filter, options) { - options = OpenLayers.Util.extend({}, options); - OpenLayers.Util.applyDefaults(options, this.options); - - var response = new OpenLayers.Protocol.Response({ - requestType: "commit" - }); - - var root = this.format.createElementNSPlus("wfs:Transaction", { - attributes: { - service: "WFS", - version: this.version - } - }); - - var deleteNode = this.format.createElementNSPlus("wfs:Delete", { - attributes: { - typeName: (options.featureNS ? this.featurePrefix + ":" : "") + - options.featureType - } - }); - - if(options.featureNS) { - deleteNode.setAttribute("xmlns:" + this.featurePrefix, options.featureNS); - } - var filterNode = this.format.writeNode("ogc:Filter", filter); - - deleteNode.appendChild(filterNode); - - root.appendChild(deleteNode); - - var data = OpenLayers.Format.XML.prototype.write.apply( - this.format, [root] - ); - - return OpenLayers.Request.POST({ - url: this.url, - callback : options.callback || function(){}, - data: data - }); - - }, - - /** - * Method: abort - * Abort an ongoing request, the response object passed to - * this method must come from this protocol (as a result - * of a read, or commit operation). - * - * Parameters: - * response - {<OpenLayers.Protocol.Response>} - */ - abort: function(response) { - if (response) { - response.priv.abort(); - } - }, - - CLASS_NAME: "OpenLayers.Protocol.WFS.v1" -}); -/* ====================================================================== - 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/Handler/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/Handler.js - * @requires OpenLayers/Geometry/Point.js - */ - -/** - * Class: OpenLayers.Handler.Point - * Handler to draw a point on the map. Point is displayed on activation, - * moves on mouse move, and is finished on mouse up. The handler triggers - * callbacks for 'done', 'cancel', and 'modify'. The modify callback is - * called with each change in the sketch and will receive the latest point - * drawn. Create a new instance with the <OpenLayers.Handler.Point> - * constructor. - * - * Inherits from: - * - <OpenLayers.Handler> - */ -OpenLayers.Handler.Point = OpenLayers.Class(OpenLayers.Handler, { - - /** - * Property: point - * {<OpenLayers.Feature.Vector>} The currently drawn point - */ - point: null, - - /** - * Property: layer - * {<OpenLayers.Layer.Vector>} The temporary drawing layer - */ - layer: null, - - /** - * APIProperty: multi - * {Boolean} Cast features to multi-part geometries before passing to the - * layer. Default is false. - */ - multi: false, - - /** - * APIProperty: citeCompliant - * {Boolean} If set to true, coordinates of features drawn in a map extent - * crossing the date line won't exceed the world bounds. Default is false. - */ - citeCompliant: false, - - /** - * Property: mouseDown - * {Boolean} The mouse is down - */ - mouseDown: false, - - /** - * Property: stoppedDown - * {Boolean} Indicate whether the last mousedown stopped the event - * propagation. - */ - stoppedDown: null, - - /** - * Property: lastDown - * {<OpenLayers.Pixel>} Location of the last mouse down - */ - lastDown: null, - - /** - * Property: lastUp - * {<OpenLayers.Pixel>} - */ - lastUp: null, - - /** - * APIProperty: persist - * {Boolean} Leave the feature rendered until destroyFeature is called. - * Default is false. If set to true, the feature remains rendered until - * destroyFeature is called, typically by deactivating the handler or - * starting another drawing. - */ - persist: false, - - /** - * APIProperty: stopDown - * {Boolean} Stop event propagation on mousedown. Must be false to - * allow "pan while drawing". Defaults to false. - */ - stopDown: false, - - /** - * APIPropery: stopUp - * {Boolean} Stop event propagation on mouse. Must be false to - * allow "pan while dragging". Defaults to fase. - */ - stopUp: false, - - /** - * Property: layerOptions - * {Object} Any optional properties to be set on the sketch layer. - */ - layerOptions: null, - - /** - * APIProperty: pixelTolerance - * {Number} Maximum number of pixels between down and up (mousedown - * and mouseup, or touchstart and touchend) for the handler to - * add a new point. If set to an integer value, if the - * displacement between down and up is great to this value - * no point will be added. Default value is 5. - */ - pixelTolerance: 5, - - /** - * Property: lastTouchPx - * {<OpenLayers.Pixel>} The last pixel used to know the distance between - * two touches (for double touch). - */ - lastTouchPx: null, - - /** - * Constructor: OpenLayers.Handler.Point - * Create a new point handler. - * - * Parameters: - * control - {<OpenLayers.Control>} The control that owns this handler - * callbacks - {Object} An object with a properties whose values are - * functions. Various callbacks described below. - * options - {Object} An optional object with properties to be set on the - * handler - * - * Named callbacks: - * create - Called when a sketch is first created. Callback called with - * the creation point geometry and sketch feature. - * modify - Called with each move of a vertex with the vertex (point) - * geometry and the sketch feature. - * done - Called when the point drawing is finished. The callback will - * recieve a single argument, the point geometry. - * cancel - Called when the handler is deactivated while drawing. The - * cancel callback will receive a geometry. - */ - initialize: function(control, callbacks, options) { - if(!(options && options.layerOptions && options.layerOptions.styleMap)) { - this.style = OpenLayers.Util.extend(OpenLayers.Feature.Vector.style['default'], {}); - } - - OpenLayers.Handler.prototype.initialize.apply(this, arguments); - }, - - /** - * APIMethod: activate - * turn on the handler - */ - activate: function() { - if(!OpenLayers.Handler.prototype.activate.apply(this, arguments)) { - return false; - } - // create temporary vector layer for rendering geometry sketch - // TBD: this could be moved to initialize/destroy - setting visibility here - var options = OpenLayers.Util.extend({ - displayInLayerSwitcher: false, - // indicate that the temp vector layer will never be out of range - // without this, resolution properties must be specified at the - // map-level for this temporary layer to init its resolutions - // correctly - calculateInRange: OpenLayers.Function.True, - wrapDateLine: this.citeCompliant - }, this.layerOptions); - this.layer = new OpenLayers.Layer.Vector(this.CLASS_NAME, options); - this.map.addLayer(this.layer); - return true; - }, - - /** - * Method: createFeature - * Add temporary features - * - * Parameters: - * pixel - {<OpenLayers.Pixel>} A pixel location on the map. - */ - createFeature: function(pixel) { - var lonlat = this.layer.getLonLatFromViewPortPx(pixel); - var geometry = new OpenLayers.Geometry.Point( - lonlat.lon, lonlat.lat - ); - this.point = new OpenLayers.Feature.Vector(geometry); - this.callback("create", [this.point.geometry, this.point]); - this.point.geometry.clearBounds(); - this.layer.addFeatures([this.point], {silent: true}); - }, - - /** - * APIMethod: deactivate - * turn off the handler - */ - deactivate: function() { - if(!OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) { - return false; - } - this.cancel(); - // If a layer's map property is set to null, it means that that layer - // isn't added to the map. Since we ourself added the layer to the map - // in activate(), we can assume that if this.layer.map is null it means - // that the layer has been destroyed (as a result of map.destroy() for - // example. - if (this.layer.map != null) { - this.destroyFeature(true); - this.layer.destroy(false); - } - this.layer = null; - return true; - }, - - /** - * Method: destroyFeature - * Destroy the temporary geometries - * - * Parameters: - * force - {Boolean} Destroy even if persist is true. - */ - destroyFeature: function(force) { - if(this.layer && (force || !this.persist)) { - this.layer.destroyFeatures(); - } - this.point = null; - }, - - /** - * Method: destroyPersistedFeature - * Destroy the persisted feature. - */ - destroyPersistedFeature: function() { - var layer = this.layer; - if(layer && layer.features.length > 1) { - this.layer.features[0].destroy(); - } - }, - - /** - * Method: finalize - * Finish the geometry and call the "done" callback. - * - * Parameters: - * cancel - {Boolean} Call cancel instead of done callback. Default - * is false. - */ - finalize: function(cancel) { - var key = cancel ? "cancel" : "done"; - this.mouseDown = false; - this.lastDown = null; - this.lastUp = null; - this.lastTouchPx = null; - this.callback(key, [this.geometryClone()]); - this.destroyFeature(cancel); - }, - - /** - * APIMethod: cancel - * Finish the geometry and call the "cancel" callback. - */ - cancel: function() { - this.finalize(true); - }, - - /** - * Method: click - * Handle clicks. Clicks are stopped from propagating to other listeners - * on map.events or other dom elements. - * - * Parameters: - * evt - {Event} The browser event - * - * Returns: - * {Boolean} Allow event propagation - */ - click: function(evt) { - OpenLayers.Event.stop(evt); - return false; - }, - - /** - * Method: dblclick - * Handle double-clicks. Double-clicks are stopped from propagating to other - * listeners on map.events or other dom elements. - * - * Parameters: - * evt - {Event} The browser event - * - * Returns: - * {Boolean} Allow event propagation - */ - dblclick: function(evt) { - OpenLayers.Event.stop(evt); - return false; - }, - - /** - * Method: modifyFeature - * Modify the existing geometry given a pixel location. - * - * Parameters: - * pixel - {<OpenLayers.Pixel>} A pixel location on the map. - */ - modifyFeature: function(pixel) { - if(!this.point) { - this.createFeature(pixel); - } - var lonlat = this.layer.getLonLatFromViewPortPx(pixel); - this.point.geometry.x = lonlat.lon; - this.point.geometry.y = lonlat.lat; - this.callback("modify", [this.point.geometry, this.point, false]); - this.point.geometry.clearBounds(); - this.drawFeature(); - }, - - /** - * Method: drawFeature - * Render features on the temporary layer. - */ - drawFeature: function() { - this.layer.drawFeature(this.point, this.style); - }, - - /** - * Method: getGeometry - * Return the sketch geometry. If <multi> is true, this will return - * a multi-part geometry. - * - * Returns: - * {<OpenLayers.Geometry.Point>} - */ - getGeometry: function() { - var geometry = this.point && this.point.geometry; - if(geometry && this.multi) { - geometry = new OpenLayers.Geometry.MultiPoint([geometry]); - } - return geometry; - }, - - /** - * Method: geometryClone - * Return a clone of the relevant geometry. - * - * Returns: - * {<OpenLayers.Geometry>} - */ - geometryClone: function() { - var geom = this.getGeometry(); - return geom && geom.clone(); - }, - - /** - * Method: mousedown - * Handle mousedown. - * - * Parameters: - * evt - {Event} The browser event - * - * Returns: - * {Boolean} Allow event propagation - */ - mousedown: function(evt) { - return this.down(evt); - }, - - /** - * Method: touchstart - * Handle touchstart. - * - * Parameters: - * evt - {Event} The browser event - * - * Returns: - * {Boolean} Allow event propagation - */ - touchstart: function(evt) { - this.startTouch(); - this.lastTouchPx = evt.xy; - return this.down(evt); - }, - - /** - * Method: mousemove - * Handle mousemove. - * - * Parameters: - * evt - {Event} The browser event - * - * Returns: - * {Boolean} Allow event propagation - */ - mousemove: function(evt) { - return this.move(evt); - }, - - /** - * Method: touchmove - * Handle touchmove. - * - * Parameters: - * evt - {Event} The browser event - * - * Returns: - * {Boolean} Allow event propagation - */ - touchmove: function(evt) { - this.lastTouchPx = evt.xy; - return this.move(evt); - }, - - /** - * Method: mouseup - * Handle mouseup. - * - * Parameters: - * evt - {Event} The browser event - * - * Returns: - * {Boolean} Allow event propagation - */ - mouseup: function(evt) { - return this.up(evt); - }, - - /** - * Method: touchend - * Handle touchend. - * - * Parameters: - * evt - {Event} The browser event - * - * Returns: - * {Boolean} Allow event propagation - */ - touchend: function(evt) { - evt.xy = this.lastTouchPx; - return this.up(evt); - }, - - /** - * Method: down - * Handle mousedown and touchstart. Adjust the geometry and redraw. - * Return determines whether to propagate the event on the map. - * - * Parameters: - * evt - {Event} The browser event - * - * Returns: - * {Boolean} Allow event propagation - */ - down: function(evt) { - this.mouseDown = true; - this.lastDown = evt.xy; - if(!this.touch) { // no point displayed until up on touch devices - this.modifyFeature(evt.xy); - } - this.stoppedDown = this.stopDown; - return !this.stopDown; - }, - - /** - * Method: move - * Handle mousemove and touchmove. Adjust the geometry and redraw. - * Return determines whether to propagate the event on the map. - * - * Parameters: - * evt - {Event} The browser event - * - * Returns: - * {Boolean} Allow event propagation - */ - move: function (evt) { - if(!this.touch // no point displayed until up on touch devices - && (!this.mouseDown || this.stoppedDown)) { - this.modifyFeature(evt.xy); - } - return true; - }, - - /** - * Method: up - * Handle mouseup and touchend. Send the latest point in the geometry to the control. - * Return determines whether to propagate the event on the map. - * - * Parameters: - * evt - {Event} The browser event - * - * Returns: - * {Boolean} Allow event propagation - */ - up: function (evt) { - this.mouseDown = false; - this.stoppedDown = this.stopDown; - - // check keyboard modifiers - if(!this.checkModifiers(evt)) { - return true; - } - // ignore double-clicks - if (this.lastUp && this.lastUp.equals(evt.xy)) { - return true; - } - if (this.lastDown && this.passesTolerance(this.lastDown, evt.xy, - this.pixelTolerance)) { - if (this.touch) { - this.modifyFeature(evt.xy); - } - if(this.persist) { - this.destroyPersistedFeature(); - } - this.lastUp = evt.xy; - this.finalize(); - return !this.stopUp; - } else { - return true; - } - }, - - /** - * Method: mouseout - * Handle mouse out. For better user experience reset mouseDown - * and stoppedDown when the mouse leaves the map viewport. - * - * Parameters: - * evt - {Event} The browser event - */ - mouseout: function(evt) { - if(OpenLayers.Util.mouseLeft(evt, this.map.viewPortDiv)) { - this.stoppedDown = this.stopDown; - this.mouseDown = false; - } - }, - - /** - * Method: passesTolerance - * Determine whether the event is within the optional pixel tolerance. - * - * Returns: - * {Boolean} The event is within the pixel tolerance (if specified). - */ - passesTolerance: function(pixel1, pixel2, tolerance) { - var passes = true; - - if (tolerance != null && pixel1 && pixel2) { - var dist = pixel1.distanceTo(pixel2); - if (dist > tolerance) { - passes = false; - } - } - return passes; - }, - - CLASS_NAME: "OpenLayers.Handler.Point" -}); -/* ====================================================================== - OpenLayers/Handler/Path.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/Point.js - * @requires OpenLayers/Geometry/Point.js - * @requires OpenLayers/Geometry/LineString.js - */ - -/** - * Class: OpenLayers.Handler.Path - * Handler to draw a path on the map. Path is displayed on mouse down, - * moves on mouse move, and is finished on mouse up. - * - * Inherits from: - * - <OpenLayers.Handler.Point> - */ -OpenLayers.Handler.Path = OpenLayers.Class(OpenLayers.Handler.Point, { - - /** - * Property: line - * {<OpenLayers.Feature.Vector>} - */ - line: null, - - /** - * APIProperty: maxVertices - * {Number} The maximum number of vertices which can be drawn by this - * handler. When the number of vertices reaches maxVertices, the - * geometry is automatically finalized. Default is null. - */ - maxVertices: null, - - /** - * Property: doubleTouchTolerance - * {Number} Maximum number of pixels between two touches for - * the gesture to be considered a "finalize feature" action. - * Default is 20. - */ - doubleTouchTolerance: 20, - - /** - * Property: freehand - * {Boolean} In freehand mode, the handler starts the path on mouse down, - * adds a point for every mouse move, and finishes the path on mouse up. - * Outside of freehand mode, a point is added to the path on every mouse - * click and double-click finishes the path. - */ - freehand: false, - - /** - * Property: freehandToggle - * {String} If set, freehandToggle is checked on mouse events and will set - * the freehand mode to the opposite of this.freehand. To disallow - * toggling between freehand and non-freehand mode, set freehandToggle to - * null. Acceptable toggle values are 'shiftKey', 'ctrlKey', and 'altKey'. - */ - freehandToggle: 'shiftKey', - - /** - * Property: timerId - * {Integer} The timer used to test the double touch. - */ - timerId: null, - - /** - * Property: redoStack - * {Array} Stack containing points removed with <undo>. - */ - redoStack: null, - - /** - * Constructor: OpenLayers.Handler.Path - * Create a new path hander - * - * Parameters: - * control - {<OpenLayers.Control>} The control that owns this handler - * callbacks - {Object} An object with a properties whose values are - * functions. Various callbacks described below. - * options - {Object} An optional object with properties to be set on the - * handler - * - * Named callbacks: - * create - Called when a sketch is first created. Callback called with - * the creation point geometry and sketch feature. - * modify - Called with each move of a vertex with the vertex (point) - * geometry and the sketch feature. - * point - Called as each point is added. Receives the new point geometry. - * done - Called when the point drawing is finished. The callback will - * recieve a single argument, the linestring geometry. - * cancel - Called when the handler is deactivated while drawing. The - * cancel callback will receive a geometry. - */ - - /** - * Method: createFeature - * Add temporary geometries - * - * Parameters: - * pixel - {<OpenLayers.Pixel>} The initial pixel location for the new - * feature. - */ - createFeature: function(pixel) { - var lonlat = this.layer.getLonLatFromViewPortPx(pixel); - var geometry = new OpenLayers.Geometry.Point( - lonlat.lon, lonlat.lat - ); - this.point = new OpenLayers.Feature.Vector(geometry); - this.line = new OpenLayers.Feature.Vector( - new OpenLayers.Geometry.LineString([this.point.geometry]) - ); - this.callback("create", [this.point.geometry, this.getSketch()]); - this.point.geometry.clearBounds(); - this.layer.addFeatures([this.line, this.point], {silent: true}); - }, - - /** - * Method: destroyFeature - * Destroy temporary geometries - * - * Parameters: - * force - {Boolean} Destroy even if persist is true. - */ - destroyFeature: function(force) { - OpenLayers.Handler.Point.prototype.destroyFeature.call( - this, force); - this.line = null; - }, - - /** - * Method: destroyPersistedFeature - * Destroy the persisted feature. - */ - destroyPersistedFeature: function() { - var layer = this.layer; - if(layer && layer.features.length > 2) { - this.layer.features[0].destroy(); - } - }, - - /** - * Method: removePoint - * Destroy the temporary point. - */ - removePoint: function() { - if(this.point) { - this.layer.removeFeatures([this.point]); - } - }, - - /** - * Method: addPoint - * Add point to geometry. Send the point index to override - * the behavior of LinearRing that disregards adding duplicate points. - * - * Parameters: - * pixel - {<OpenLayers.Pixel>} The pixel location for the new point. - */ - addPoint: function(pixel) { - this.layer.removeFeatures([this.point]); - var lonlat = this.layer.getLonLatFromViewPortPx(pixel); - this.point = new OpenLayers.Feature.Vector( - new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat) - ); - this.line.geometry.addComponent( - this.point.geometry, this.line.geometry.components.length - ); - this.layer.addFeatures([this.point]); - this.callback("point", [this.point.geometry, this.getGeometry()]); - this.callback("modify", [this.point.geometry, this.getSketch()]); - this.drawFeature(); - delete this.redoStack; - }, - - /** - * Method: insertXY - * Insert a point in the current sketch given x & y coordinates. The new - * point is inserted immediately before the most recently drawn point. - * - * Parameters: - * x - {Number} The x-coordinate of the point. - * y - {Number} The y-coordinate of the point. - */ - insertXY: function(x, y) { - this.line.geometry.addComponent( - new OpenLayers.Geometry.Point(x, y), - this.getCurrentPointIndex() - ); - this.drawFeature(); - delete this.redoStack; - }, - - /** - * Method: insertDeltaXY - * Insert a point given offsets from the previously inserted point. - * - * Parameters: - * dx - {Number} The x-coordinate offset of the point. - * dy - {Number} The y-coordinate offset of the point. - */ - insertDeltaXY: function(dx, dy) { - var previousIndex = this.getCurrentPointIndex() - 1; - var p0 = this.line.geometry.components[previousIndex]; - if (p0 && !isNaN(p0.x) && !isNaN(p0.y)) { - this.insertXY(p0.x + dx, p0.y + dy); - } - }, - - /** - * Method: insertDirectionLength - * Insert a point in the current sketch given a direction and a length. - * - * Parameters: - * direction - {Number} Degrees clockwise from the positive x-axis. - * length - {Number} Distance from the previously drawn point. - */ - insertDirectionLength: function(direction, length) { - direction *= Math.PI / 180; - var dx = length * Math.cos(direction); - var dy = length * Math.sin(direction); - this.insertDeltaXY(dx, dy); - }, - - /** - * Method: insertDeflectionLength - * Insert a point in the current sketch given a deflection and a length. - * The deflection should be degrees clockwise from the previously - * digitized segment. - * - * Parameters: - * deflection - {Number} Degrees clockwise from the previous segment. - * length - {Number} Distance from the previously drawn point. - */ - insertDeflectionLength: function(deflection, length) { - var previousIndex = this.getCurrentPointIndex() - 1; - if (previousIndex > 0) { - var p1 = this.line.geometry.components[previousIndex]; - var p0 = this.line.geometry.components[previousIndex-1]; - var theta = Math.atan2(p1.y - p0.y, p1.x - p0.x); - this.insertDirectionLength( - (theta * 180 / Math.PI) + deflection, length - ); - } - }, - - /** - * Method: getCurrentPointIndex - * - * Returns: - * {Number} The index of the most recently drawn point. - */ - getCurrentPointIndex: function() { - return this.line.geometry.components.length - 1; - }, - - - /** - * Method: undo - * Remove the most recently added point in the sketch geometry. - * - * Returns: - * {Boolean} A point was removed. - */ - undo: function() { - var geometry = this.line.geometry; - var components = geometry.components; - var index = this.getCurrentPointIndex() - 1; - var target = components[index]; - var undone = geometry.removeComponent(target); - if (undone) { - // On touch devices, set the current ("mouse location") point to - // match the last digitized point. - if (this.touch && index > 0) { - components = geometry.components; // safety - var lastpt = components[index - 1]; - var curptidx = this.getCurrentPointIndex(); - var curpt = components[curptidx]; - curpt.x = lastpt.x; - curpt.y = lastpt.y; - } - if (!this.redoStack) { - this.redoStack = []; - } - this.redoStack.push(target); - this.drawFeature(); - } - return undone; - }, - - /** - * Method: redo - * Reinsert the most recently removed point resulting from an <undo> call. - * The undo stack is deleted whenever a point is added by other means. - * - * Returns: - * {Boolean} A point was added. - */ - redo: function() { - var target = this.redoStack && this.redoStack.pop(); - if (target) { - this.line.geometry.addComponent(target, this.getCurrentPointIndex()); - this.drawFeature(); - } - return !!target; - }, - - /** - * Method: freehandMode - * Determine whether to behave in freehand mode or not. - * - * Returns: - * {Boolean} - */ - freehandMode: function(evt) { - return (this.freehandToggle && evt[this.freehandToggle]) ? - !this.freehand : this.freehand; - }, - - /** - * Method: modifyFeature - * Modify the existing geometry given the new point - * - * Parameters: - * pixel - {<OpenLayers.Pixel>} The updated pixel location for the latest - * point. - * drawing - {Boolean} Indicate if we're currently drawing. - */ - modifyFeature: function(pixel, drawing) { - if(!this.line) { - this.createFeature(pixel); - } - var lonlat = this.layer.getLonLatFromViewPortPx(pixel); - this.point.geometry.x = lonlat.lon; - this.point.geometry.y = lonlat.lat; - this.callback("modify", [this.point.geometry, this.getSketch(), drawing]); - this.point.geometry.clearBounds(); - this.drawFeature(); - }, - - /** - * Method: drawFeature - * Render geometries on the temporary layer. - */ - drawFeature: function() { - this.layer.drawFeature(this.line, this.style); - this.layer.drawFeature(this.point, this.style); - }, - - /** - * Method: getSketch - * Return the sketch feature. - * - * Returns: - * {<OpenLayers.Feature.Vector>} - */ - getSketch: function() { - return this.line; - }, - - /** - * Method: getGeometry - * Return the sketch geometry. If <multi> is true, this will return - * a multi-part geometry. - * - * Returns: - * {<OpenLayers.Geometry.LineString>} - */ - getGeometry: function() { - var geometry = this.line && this.line.geometry; - if(geometry && this.multi) { - geometry = new OpenLayers.Geometry.MultiLineString([geometry]); - } - return geometry; - }, - - /** - * method: touchstart - * handle touchstart. - * - * parameters: - * evt - {event} the browser event - * - * returns: - * {boolean} allow event propagation - */ - touchstart: function(evt) { - if (this.timerId && - this.passesTolerance(this.lastTouchPx, evt.xy, - this.doubleTouchTolerance)) { - // double-tap, finalize the geometry - this.finishGeometry(); - window.clearTimeout(this.timerId); - this.timerId = null; - return false; - } else { - if (this.timerId) { - window.clearTimeout(this.timerId); - this.timerId = null; - } - this.timerId = window.setTimeout( - OpenLayers.Function.bind(function() { - this.timerId = null; - }, this), 300); - return OpenLayers.Handler.Point.prototype.touchstart.call(this, evt); - } - }, - - /** - * Method: down - * Handle mousedown and touchstart. Add a new point to the geometry and - * render it. Return determines whether to propagate the event on the map. - * - * Parameters: - * evt - {Event} The browser event - * - * Returns: - * {Boolean} Allow event propagation - */ - down: function(evt) { - var stopDown = this.stopDown; - if(this.freehandMode(evt)) { - stopDown = true; - if (this.touch) { - this.modifyFeature(evt.xy, !!this.lastUp); - OpenLayers.Event.stop(evt); - } - } - if (!this.touch && (!this.lastDown || - !this.passesTolerance(this.lastDown, evt.xy, - this.pixelTolerance))) { - this.modifyFeature(evt.xy, !!this.lastUp); - } - this.mouseDown = true; - this.lastDown = evt.xy; - this.stoppedDown = stopDown; - return !stopDown; - }, - - /** - * Method: move - * Handle mousemove and touchmove. Adjust the geometry and redraw. - * Return determines whether to propagate the event on the map. - * - * Parameters: - * evt - {Event} The browser event - * - * Returns: - * {Boolean} Allow event propagation - */ - move: function (evt) { - if(this.stoppedDown && this.freehandMode(evt)) { - if(this.persist) { - this.destroyPersistedFeature(); - } - if(this.maxVertices && this.line && - this.line.geometry.components.length === this.maxVertices) { - this.removePoint(); - this.finalize(); - } else { - this.addPoint(evt.xy); - } - return false; - } - if (!this.touch && (!this.mouseDown || this.stoppedDown)) { - this.modifyFeature(evt.xy, !!this.lastUp); - } - return true; - }, - - /** - * Method: up - * Handle mouseup and touchend. Send the latest point in the geometry to - * the control. Return determines whether to propagate the event on the map. - * - * Parameters: - * evt - {Event} The browser event - * - * Returns: - * {Boolean} Allow event propagation - */ - up: function (evt) { - if (this.mouseDown && (!this.lastUp || !this.lastUp.equals(evt.xy))) { - if(this.stoppedDown && this.freehandMode(evt)) { - if (this.persist) { - this.destroyPersistedFeature(); - } - this.removePoint(); - this.finalize(); - } else { - if (this.passesTolerance(this.lastDown, evt.xy, - this.pixelTolerance)) { - if (this.touch) { - this.modifyFeature(evt.xy); - } - if(this.lastUp == null && this.persist) { - this.destroyPersistedFeature(); - } - this.addPoint(evt.xy); - this.lastUp = evt.xy; - if(this.line.geometry.components.length === this.maxVertices + 1) { - this.finishGeometry(); - } - } - } - } - this.stoppedDown = this.stopDown; - this.mouseDown = false; - return !this.stopUp; - }, - - /** - * APIMethod: finishGeometry - * Finish the geometry and send it back to the control. - */ - finishGeometry: function() { - var index = this.line.geometry.components.length - 1; - this.line.geometry.removeComponent(this.line.geometry.components[index]); - this.removePoint(); - this.finalize(); - }, - - /** - * Method: dblclick - * Handle double-clicks. - * - * Parameters: - * evt - {Event} The browser event - * - * Returns: - * {Boolean} Allow event propagation - */ - dblclick: function(evt) { - if(!this.freehandMode(evt)) { - this.finishGeometry(); - } - return false; - }, - - CLASS_NAME: "OpenLayers.Handler.Path" -}); -/* ====================================================================== - 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/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/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/Control/DrawFeature.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 - */ - -/** - * Class: OpenLayers.Control.DrawFeature - * The DrawFeature control draws point, line or polygon features on a vector - * layer when active. - * - * Inherits from: - * - <OpenLayers.Control> - */ -OpenLayers.Control.DrawFeature = OpenLayers.Class(OpenLayers.Control, { - - /** - * Property: layer - * {<OpenLayers.Layer.Vector>} - */ - layer: null, - - /** - * Property: callbacks - * {Object} The functions that are sent to the handler for callback - */ - callbacks: null, - - /** - * 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>): - * featureadded - Triggered when a feature is added - */ - - /** - * APIProperty: multi - * {Boolean} Cast features to multi-part geometries before passing to the - * layer. Default is false. - */ - multi: false, - - /** - * APIProperty: featureAdded - * {Function} Called after each feature is added - */ - featureAdded: function() {}, - - /** - * APIProperty: handlerOptions - * {Object} Used to set non-default properties on the control's handler - */ - - /** - * Constructor: OpenLayers.Control.DrawFeature - * - * Parameters: - * layer - {<OpenLayers.Layer.Vector>} - * handler - {<OpenLayers.Handler>} - * options - {Object} - */ - initialize: function(layer, handler, options) { - OpenLayers.Control.prototype.initialize.apply(this, [options]); - this.callbacks = OpenLayers.Util.extend( - { - done: this.drawFeature, - modify: function(vertex, feature) { - this.layer.events.triggerEvent( - "sketchmodified", {vertex: vertex, feature: feature} - ); - }, - create: function(vertex, feature) { - this.layer.events.triggerEvent( - "sketchstarted", {vertex: vertex, feature: feature} - ); - } - }, - this.callbacks - ); - this.layer = layer; - this.handlerOptions = this.handlerOptions || {}; - this.handlerOptions.layerOptions = OpenLayers.Util.applyDefaults( - this.handlerOptions.layerOptions, { - renderers: layer.renderers, rendererOptions: layer.rendererOptions - } - ); - if (!("multi" in this.handlerOptions)) { - this.handlerOptions.multi = this.multi; - } - var sketchStyle = this.layer.styleMap && this.layer.styleMap.styles.temporary; - if(sketchStyle) { - this.handlerOptions.layerOptions = OpenLayers.Util.applyDefaults( - this.handlerOptions.layerOptions, - {styleMap: new OpenLayers.StyleMap({"default": sketchStyle})} - ); - } - this.handler = new handler(this, this.callbacks, this.handlerOptions); - }, - - /** - * Method: drawFeature - */ - drawFeature: function(geometry) { - var feature = new OpenLayers.Feature.Vector(geometry); - var proceed = this.layer.events.triggerEvent( - "sketchcomplete", {feature: feature} - ); - if(proceed !== false) { - feature.state = OpenLayers.State.INSERT; - this.layer.addFeatures([feature]); - this.featureAdded(feature); - this.events.triggerEvent("featureadded",{feature : feature}); - } - }, - - /** - * APIMethod: insertXY - * Insert a point in the current sketch given x & y coordinates. - * - * Parameters: - * x - {Number} The x-coordinate of the point. - * y - {Number} The y-coordinate of the point. - */ - insertXY: function(x, y) { - if (this.handler && this.handler.line) { - this.handler.insertXY(x, y); - } - }, - - /** - * APIMethod: insertDeltaXY - * Insert a point given offsets from the previously inserted point. - * - * Parameters: - * dx - {Number} The x-coordinate offset of the point. - * dy - {Number} The y-coordinate offset of the point. - */ - insertDeltaXY: function(dx, dy) { - if (this.handler && this.handler.line) { - this.handler.insertDeltaXY(dx, dy); - } - }, - - /** - * APIMethod: insertDirectionLength - * Insert a point in the current sketch given a direction and a length. - * - * Parameters: - * direction - {Number} Degrees clockwise from the positive x-axis. - * length - {Number} Distance from the previously drawn point. - */ - insertDirectionLength: function(direction, length) { - if (this.handler && this.handler.line) { - this.handler.insertDirectionLength(direction, length); - } - }, - - /** - * APIMethod: insertDeflectionLength - * Insert a point in the current sketch given a deflection and a length. - * The deflection should be degrees clockwise from the previously - * digitized segment. - * - * Parameters: - * deflection - {Number} Degrees clockwise from the previous segment. - * length - {Number} Distance from the previously drawn point. - */ - insertDeflectionLength: function(deflection, length) { - if (this.handler && this.handler.line) { - this.handler.insertDeflectionLength(deflection, length); - } - }, - - /** - * APIMethod: undo - * Remove the most recently added point in the current sketch geometry. - * - * Returns: - * {Boolean} An edit was undone. - */ - undo: function() { - return this.handler.undo && this.handler.undo(); - }, - - /** - * APIMethod: redo - * Reinsert the most recently removed point resulting from an <undo> call. - * The undo stack is deleted whenever a point is added by other means. - * - * Returns: - * {Boolean} An edit was redone. - */ - redo: function() { - return this.handler.redo && this.handler.redo(); - }, - - /** - * APIMethod: finishSketch - * Finishes the sketch without including the currently drawn point. - * This method can be called to terminate drawing programmatically - * instead of waiting for the user to end the sketch. - */ - finishSketch: function() { - this.handler.finishGeometry(); - }, - - /** - * APIMethod: cancel - * Cancel the current sketch. This removes the current sketch and keeps - * the drawing control active. - */ - cancel: function() { - this.handler.cancel(); - }, - - CLASS_NAME: "OpenLayers.Control.DrawFeature" -}); -/* ====================================================================== - OpenLayers/Handler/Pinch.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.Pinch - * The pinch handler is used to deal with sequences of browser events related - * to pinch gestures. The handler is used by controls that want to know - * when a pinch sequence begins, when a pinch is happening, and when it has - * finished. - * - * Controls that use the pinch handler typically construct it with callbacks - * for 'start', 'move', and 'done'. Callbacks for these keys are - * called when the pinch begins, with each change, and when the pinch is - * done. - * - * Create a new pinch handler with the <OpenLayers.Handler.Pinch> constructor. - * - * Inherits from: - * - <OpenLayers.Handler> - */ -OpenLayers.Handler.Pinch = OpenLayers.Class(OpenLayers.Handler, { - - /** - * Property: started - * {Boolean} When a touchstart event is received, we want to record it, - * but not set 'pinching' until the touchmove get started after - * starting. - */ - started: false, - - /** - * Property: stopDown - * {Boolean} Stop propagation of touchstart events from getting to - * listeners on the same element. Default is false. - */ - stopDown: false, - - /** - * Property: pinching - * {Boolean} - */ - pinching: false, - - /** - * Property: last - * {Object} Object that store informations related to pinch last touch. - */ - last: null, - - /** - * Property: start - * {Object} Object that store informations related to pinch touchstart. - */ - start: null, - - /** - * Constructor: OpenLayers.Handler.Pinch - * Returns OpenLayers.Handler.Pinch - * - * 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 functions to be called when - * the pinch operation start, change, or is finished. The callbacks - * should expect to receive an object argument, which contains - * information about scale, distance, and position of touch points. - * options - {Object} - */ - - /** - * Method: touchstart - * Handle touchstart events - * - * Parameters: - * evt - {Event} - * - * Returns: - * {Boolean} Let the event propagate. - */ - touchstart: function(evt) { - var propagate = true; - this.pinching = false; - if (OpenLayers.Event.isMultiTouch(evt)) { - this.started = true; - this.last = this.start = { - distance: this.getDistance(evt.touches), - delta: 0, - scale: 1 - }; - this.callback("start", [evt, this.start]); - propagate = !this.stopDown; - } else if (this.started) { - // Some webkit versions send fake single-touch events during - // multitouch, which cause the drag handler to trigger - return false; - } else { - this.started = false; - this.start = null; - this.last = null; - } - // prevent document dragging - OpenLayers.Event.preventDefault(evt); - return propagate; - }, - - /** - * Method: touchmove - * Handle touchmove events - * - * Parameters: - * evt - {Event} - * - * Returns: - * {Boolean} Let the event propagate. - */ - touchmove: function(evt) { - if (this.started && OpenLayers.Event.isMultiTouch(evt)) { - this.pinching = true; - var current = this.getPinchData(evt); - this.callback("move", [evt, current]); - this.last = current; - // prevent document dragging - OpenLayers.Event.stop(evt); - } else if (this.started) { - // Some webkit versions send fake single-touch events during - // multitouch, which cause the drag handler to trigger - return false; - } - return true; - }, - - /** - * Method: touchend - * Handle touchend events - * - * Parameters: - * evt - {Event} - * - * Returns: - * {Boolean} Let the event propagate. - */ - touchend: function(evt) { - if (this.started && !OpenLayers.Event.isMultiTouch(evt)) { - this.started = false; - this.pinching = false; - this.callback("done", [evt, this.start, this.last]); - this.start = null; - this.last = null; - return false; - } - return true; - }, - - /** - * 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.pinching = 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.pinching = false; - this.start = null; - this.last = null; - deactivated = true; - } - return deactivated; - }, - - /** - * Method: getDistance - * Get the distance in pixels between two touches. - * - * Parameters: - * touches - {Array(Object)} - * - * Returns: - * {Number} The distance in pixels. - */ - getDistance: function(touches) { - var t0 = touches[0]; - var t1 = touches[1]; - return Math.sqrt( - Math.pow(t0.olClientX - t1.olClientX, 2) + - Math.pow(t0.olClientY - t1.olClientY, 2) - ); - }, - - - /** - * Method: getPinchData - * Get informations about the pinch event. - * - * Parameters: - * evt - {Event} - * - * Returns: - * {Object} Object that contains data about the current pinch. - */ - getPinchData: function(evt) { - var distance = this.getDistance(evt.touches); - var scale = distance / this.start.distance; - return { - distance: distance, - delta: this.last.distance - distance, - scale: scale - }; - }, - - CLASS_NAME: "OpenLayers.Handler.Pinch" -}); - -/* ====================================================================== - OpenLayers/Handler/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/Handler/Path.js - * @requires OpenLayers/Geometry/Polygon.js - */ - -/** - * Class: OpenLayers.Handler.Polygon - * Handler to draw a polygon on the map. Polygon is displayed on mouse down, - * moves on mouse move, and is finished on mouse up. - * - * Inherits from: - * - <OpenLayers.Handler.Path> - * - <OpenLayers.Handler> - */ -OpenLayers.Handler.Polygon = OpenLayers.Class(OpenLayers.Handler.Path, { - - /** - * APIProperty: holeModifier - * {String} Key modifier to trigger hole digitizing. Acceptable values are - * "altKey", "shiftKey", or "ctrlKey". If not set, no hole digitizing - * will take place. Default is null. - */ - holeModifier: null, - - /** - * Property: drawingHole - * {Boolean} Currently drawing an interior ring. - */ - drawingHole: false, - - /** - * Property: polygon - * {<OpenLayers.Feature.Vector>} - */ - polygon: null, - - /** - * Constructor: OpenLayers.Handler.Polygon - * Create a Polygon Handler. - * - * Parameters: - * control - {<OpenLayers.Control>} The control that owns this handler - * callbacks - {Object} An object with a properties whose values are - * functions. Various callbacks described below. - * options - {Object} An optional object with properties to be set on the - * handler - * - * Named callbacks: - * create - Called when a sketch is first created. Callback called with - * the creation point geometry and sketch feature. - * modify - Called with each move of a vertex with the vertex (point) - * geometry and the sketch feature. - * point - Called as each point is added. Receives the new point geometry. - * done - Called when the point drawing is finished. The callback will - * recieve a single argument, the polygon geometry. - * cancel - Called when the handler is deactivated while drawing. The - * cancel callback will receive a geometry. - */ - - /** - * Method: createFeature - * Add temporary geometries - * - * Parameters: - * pixel - {<OpenLayers.Pixel>} The initial pixel location for the new - * feature. - */ - createFeature: function(pixel) { - var lonlat = this.layer.getLonLatFromViewPortPx(pixel); - var geometry = new OpenLayers.Geometry.Point( - lonlat.lon, lonlat.lat - ); - this.point = new OpenLayers.Feature.Vector(geometry); - this.line = new OpenLayers.Feature.Vector( - new OpenLayers.Geometry.LinearRing([this.point.geometry]) - ); - this.polygon = new OpenLayers.Feature.Vector( - new OpenLayers.Geometry.Polygon([this.line.geometry]) - ); - this.callback("create", [this.point.geometry, this.getSketch()]); - this.point.geometry.clearBounds(); - this.layer.addFeatures([this.polygon, this.point], {silent: true}); - }, - - /** - * Method: addPoint - * Add point to geometry. - * - * Parameters: - * pixel - {<OpenLayers.Pixel>} The pixel location for the new point. - */ - addPoint: function(pixel) { - if(!this.drawingHole && this.holeModifier && - this.evt && this.evt[this.holeModifier]) { - var geometry = this.point.geometry; - var features = this.control.layer.features; - var candidate, polygon; - // look for intersections, last drawn gets priority - for (var i=features.length-1; i>=0; --i) { - candidate = features[i].geometry; - if ((candidate instanceof OpenLayers.Geometry.Polygon || - candidate instanceof OpenLayers.Geometry.MultiPolygon) && - candidate.intersects(geometry)) { - polygon = features[i]; - this.control.layer.removeFeatures([polygon], {silent: true}); - this.control.layer.events.registerPriority( - "sketchcomplete", this, this.finalizeInteriorRing - ); - this.control.layer.events.registerPriority( - "sketchmodified", this, this.enforceTopology - ); - polygon.geometry.addComponent(this.line.geometry); - this.polygon = polygon; - this.drawingHole = true; - break; - } - } - } - OpenLayers.Handler.Path.prototype.addPoint.apply(this, arguments); - }, - - /** - * Method: getCurrentPointIndex - * - * Returns: - * {Number} The index of the most recently drawn point. - */ - getCurrentPointIndex: function() { - return this.line.geometry.components.length - 2; - }, - - /** - * Method: enforceTopology - * Simple topology enforcement for drawing interior rings. Ensures vertices - * of interior rings are contained by exterior ring. Other topology - * rules are enforced in <finalizeInteriorRing> to allow drawing of - * rings that intersect only during the sketch (e.g. a "C" shaped ring - * that nearly encloses another ring). - */ - enforceTopology: function(event) { - var point = event.vertex; - var components = this.line.geometry.components; - // ensure that vertices of interior ring are contained by exterior ring - if (!this.polygon.geometry.intersects(point)) { - var last = components[components.length-3]; - point.x = last.x; - point.y = last.y; - } - }, - - /** - * Method: finishGeometry - * Finish the geometry and send it back to the control. - */ - finishGeometry: function() { - var index = this.line.geometry.components.length - 2; - this.line.geometry.removeComponent(this.line.geometry.components[index]); - this.removePoint(); - this.finalize(); - }, - - /** - * Method: finalizeInteriorRing - * Enforces that new ring has some area and doesn't contain vertices of any - * other rings. - */ - finalizeInteriorRing: function() { - var ring = this.line.geometry; - // ensure that ring has some area - var modified = (ring.getArea() !== 0); - if (modified) { - // ensure that new ring doesn't intersect any other rings - var rings = this.polygon.geometry.components; - for (var i=rings.length-2; i>=0; --i) { - if (ring.intersects(rings[i])) { - modified = false; - break; - } - } - if (modified) { - // ensure that new ring doesn't contain any other rings - var target; - outer: for (var i=rings.length-2; i>0; --i) { - var points = rings[i].components; - for (var j=0, jj=points.length; j<jj; ++j) { - if (ring.containsPoint(points[j])) { - modified = false; - break outer; - } - } - } - } - } - if (modified) { - if (this.polygon.state !== OpenLayers.State.INSERT) { - this.polygon.state = OpenLayers.State.UPDATE; - } - } else { - this.polygon.geometry.removeComponent(ring); - } - this.restoreFeature(); - return false; - }, - - /** - * APIMethod: cancel - * Finish the geometry and call the "cancel" callback. - */ - cancel: function() { - if (this.drawingHole) { - this.polygon.geometry.removeComponent(this.line.geometry); - this.restoreFeature(true); - } - return OpenLayers.Handler.Path.prototype.cancel.apply(this, arguments); - }, - - /** - * Method: restoreFeature - * Move the feature from the sketch layer to the target layer. - * - * Properties: - * cancel - {Boolean} Cancel drawing. If falsey, the "sketchcomplete" event - * will be fired. - */ - restoreFeature: function(cancel) { - this.control.layer.events.unregister( - "sketchcomplete", this, this.finalizeInteriorRing - ); - this.control.layer.events.unregister( - "sketchmodified", this, this.enforceTopology - ); - this.layer.removeFeatures([this.polygon], {silent: true}); - this.control.layer.addFeatures([this.polygon], {silent: true}); - this.drawingHole = false; - if (!cancel) { - // Re-trigger "sketchcomplete" so other listeners can do their - // business. While this is somewhat sloppy (if a listener is - // registered with registerPriority - not common - between the start - // and end of a single ring drawing - very uncommon - it will be - // called twice). - // TODO: In 3.0, collapse sketch handlers into geometry specific - // drawing controls. - this.control.layer.events.triggerEvent( - "sketchcomplete", {feature : this.polygon} - ); - } - }, - - /** - * Method: destroyFeature - * Destroy temporary geometries - * - * Parameters: - * force - {Boolean} Destroy even if persist is true. - */ - destroyFeature: function(force) { - OpenLayers.Handler.Path.prototype.destroyFeature.call( - this, force); - this.polygon = null; - }, - - /** - * Method: drawFeature - * Render geometries on the temporary layer. - */ - drawFeature: function() { - this.layer.drawFeature(this.polygon, this.style); - this.layer.drawFeature(this.point, this.style); - }, - - /** - * Method: getSketch - * Return the sketch feature. - * - * Returns: - * {<OpenLayers.Feature.Vector>} - */ - getSketch: function() { - return this.polygon; - }, - - /** - * Method: getGeometry - * Return the sketch geometry. If <multi> is true, this will return - * a multi-part geometry. - * - * Returns: - * {<OpenLayers.Geometry.Polygon>} - */ - getGeometry: function() { - var geometry = this.polygon && this.polygon.geometry; - if(geometry && this.multi) { - geometry = new OpenLayers.Geometry.MultiPolygon([geometry]); - } - return geometry; - }, - - CLASS_NAME: "OpenLayers.Handler.Polygon" -}); -/* ====================================================================== - OpenLayers/Control/Geolocate.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/Geometry/Point.js - * @requires OpenLayers/Projection.js - */ - -/** - * Class: OpenLayers.Control.Geolocate - * The Geolocate control wraps w3c geolocation API into control that can be - * bound to a map, and generate events on location update - * - * To use this control requires to load the proj4js library if the projection - * of the map is not EPSG:4326 or EPSG:900913. - * - * Inherits from: - * - <OpenLayers.Control> - */ -OpenLayers.Control.Geolocate = 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>): - * locationupdated - Triggered when browser return a new position. Listeners will - * receive an object with a 'position' property which is the browser.geolocation.position - * native object, as well as a 'point' property which is the location transformed in the - * current map projection. - * locationfailed - Triggered when geolocation has failed - * locationuncapable - Triggered when control is activated on a browser - * which doesn't support geolocation - */ - - /** - * Property: geolocation - * {Object} The geolocation engine, as a property to be possibly mocked. - * This is set lazily to avoid a memory leak in IE9. - */ - geolocation: null, - - /** - * Property: available - * {Boolean} The navigator.geolocation object is available. - */ - available: ('geolocation' in navigator), - - /** - * APIProperty: bind - * {Boolean} If true, map center will be set on location update. - */ - bind: true, - - /** - * APIProperty: watch - * {Boolean} If true, position will be update regularly. - */ - watch: false, - - /** - * APIProperty: geolocationOptions - * {Object} Options to pass to the navigator's geolocation API. See - * <http://dev.w3.org/geo/api/spec-source.html>. No specific - * option is passed to the geolocation API by default. - */ - geolocationOptions: null, - - /** - * Constructor: OpenLayers.Control.Geolocate - * Create a new control to deal with browser geolocation API - * - */ - - /** - * Method: destroy - */ - destroy: function() { - this.deactivate(); - OpenLayers.Control.prototype.destroy.apply(this, arguments); - }, - - /** - * Method: activate - * Activates the control. - * - * Returns: - * {Boolean} The control was effectively activated. - */ - activate: function () { - if (this.available && !this.geolocation) { - // set lazily to avoid IE9 memory leak - this.geolocation = navigator.geolocation; - } - if (!this.geolocation) { - this.events.triggerEvent("locationuncapable"); - return false; - } - if (OpenLayers.Control.prototype.activate.apply(this, arguments)) { - if (this.watch) { - this.watchId = this.geolocation.watchPosition( - OpenLayers.Function.bind(this.geolocate, this), - OpenLayers.Function.bind(this.failure, this), - this.geolocationOptions - ); - } else { - this.getCurrentLocation(); - } - return true; - } - return false; - }, - - /** - * Method: deactivate - * Deactivates the control. - * - * Returns: - * {Boolean} The control was effectively deactivated. - */ - deactivate: function () { - if (this.active && this.watchId !== null) { - this.geolocation.clearWatch(this.watchId); - } - return OpenLayers.Control.prototype.deactivate.apply( - this, arguments - ); - }, - - /** - * Method: geolocate - * Activates the control. - * - */ - geolocate: function (position) { - var center = new OpenLayers.LonLat( - position.coords.longitude, - position.coords.latitude - ).transform( - new OpenLayers.Projection("EPSG:4326"), - this.map.getProjectionObject() - ); - if (this.bind) { - this.map.setCenter(center); - } - this.events.triggerEvent("locationupdated", { - position: position, - point: new OpenLayers.Geometry.Point( - center.lon, center.lat - ) - }); - }, - - /** - * APIMethod: getCurrentLocation - * - * Returns: - * {Boolean} Returns true if a event will be fired (successfull - * registration) - */ - getCurrentLocation: function() { - if (!this.active || this.watch) { - return false; - } - this.geolocation.getCurrentPosition( - OpenLayers.Function.bind(this.geolocate, this), - OpenLayers.Function.bind(this.failure, this), - this.geolocationOptions - ); - return true; - }, - - /** - * Method: failure - * method called on browser's geolocation failure - * - */ - failure: function (error) { - this.events.triggerEvent("locationfailed", {error: error}); - }, - - CLASS_NAME: "OpenLayers.Control.Geolocate" -}); -/* ====================================================================== - 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/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/Control/PinchZoom.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/Pinch.js - */ - -/** - * Class: OpenLayers.Control.PinchZoom - * - * Inherits: - * - <OpenLayers.Control> - */ -OpenLayers.Control.PinchZoom = OpenLayers.Class(OpenLayers.Control, { - - /** - * Property: type - * {OpenLayers.Control.TYPES} - */ - type: OpenLayers.Control.TYPE_TOOL, - - /** - * Property: pinchOrigin - * {Object} Cached object representing the pinch start (in pixels). - */ - pinchOrigin: null, - - /** - * Property: currentCenter - * {Object} Cached object representing the latest pinch center (in pixels). - */ - currentCenter: null, - - /** - * APIProperty: autoActivate - * {Boolean} Activate the control when it is added to a map. Default is - * true. - */ - autoActivate: true, - - /** - * APIProperty: preserveCenter - * {Boolean} Set this to true if you don't want the map center to change - * while pinching. For example you may want to set preserveCenter to - * true when the user location is being watched and you want to preserve - * the user location at the center of the map even if he zooms in or - * out using pinch. This property's value can be changed any time on an - * existing instance. Default is false. - */ - preserveCenter: false, - - /** - * APIProperty: handlerOptions - * {Object} Used to set non-default properties on the pinch handler - */ - - /** - * Constructor: OpenLayers.Control.PinchZoom - * Create a control for zooming with pinch gestures. This works on devices - * with multi-touch support. - * - * Parameters: - * options - {Object} An optional object whose properties will be set on - * the control - */ - initialize: function(options) { - OpenLayers.Control.prototype.initialize.apply(this, arguments); - this.handler = new OpenLayers.Handler.Pinch(this, { - start: this.pinchStart, - move: this.pinchMove, - done: this.pinchDone - }, this.handlerOptions); - }, - - /** - * Method: pinchStart - * - * Parameters: - * evt - {Event} - * pinchData - {Object} pinch data object related to the current touchmove - * of the pinch gesture. This give us the current scale of the pinch. - */ - pinchStart: function(evt, pinchData) { - var xy = (this.preserveCenter) ? - this.map.getPixelFromLonLat(this.map.getCenter()) : evt.xy; - this.pinchOrigin = xy; - this.currentCenter = xy; - }, - - /** - * Method: pinchMove - * - * Parameters: - * evt - {Event} - * pinchData - {Object} pinch data object related to the current touchmove - * of the pinch gesture. This give us the current scale of the pinch. - */ - pinchMove: function(evt, pinchData) { - var scale = pinchData.scale; - var containerOrigin = this.map.layerContainerOriginPx; - var pinchOrigin = this.pinchOrigin; - var current = (this.preserveCenter) ? - this.map.getPixelFromLonLat(this.map.getCenter()) : evt.xy; - - var dx = Math.round((containerOrigin.x + current.x - pinchOrigin.x) + (scale - 1) * (containerOrigin.x - pinchOrigin.x)); - var dy = Math.round((containerOrigin.y + current.y - pinchOrigin.y) + (scale - 1) * (containerOrigin.y - pinchOrigin.y)); - - this.map.applyTransform(dx, dy, scale); - this.currentCenter = current; - }, - - /** - * Method: pinchDone - * - * Parameters: - * evt - {Event} - * start - {Object} pinch data object related to the touchstart event that - * started the pinch gesture. - * last - {Object} pinch data object related to the last touchmove event - * of the pinch gesture. This give us the final scale of the pinch. - */ - pinchDone: function(evt, start, last) { - this.map.applyTransform(); - var zoom = this.map.getZoomForResolution(this.map.getResolution() / last.scale, true); - if (zoom !== this.map.getZoom() || !this.currentCenter.equals(this.pinchOrigin)) { - var resolution = this.map.getResolutionForZoom(zoom); - - var location = this.map.getLonLatFromPixel(this.pinchOrigin); - var zoomPixel = this.currentCenter; - var size = this.map.getSize(); - - location.lon += resolution * ((size.w / 2) - zoomPixel.x); - location.lat -= resolution * ((size.h / 2) - zoomPixel.y); - - // Force a reflow before calling setCenter. This is to work - // around an issue occuring in iOS. - // - // See https://github.com/openlayers/openlayers/pull/351. - // - // Without a reflow setting the layer container div's top left - // style properties to "0px" - as done in Map.moveTo when zoom - // is changed - won't actually correctly reposition the layer - // container div. - // - // Also, we need to use a statement that the Google Closure - // compiler won't optimize away. - this.map.div.clientWidth = this.map.div.clientWidth; - - this.map.setCenter(location, zoom); - } - }, - - CLASS_NAME: "OpenLayers.Control.PinchZoom" - -}); -/* ====================================================================== - 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/TouchNavigation.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/DragPan.js - * @requires OpenLayers/Control/PinchZoom.js - * @requires OpenLayers/Handler/Click.js - */ - -/** - * Class: OpenLayers.Control.TouchNavigation - * The navigation control handles map browsing with touch events (dragging, - * double-tapping, tap with two fingers, and pinch zoom). Create a new - * control with the <OpenLayers.Control.TouchNavigation> constructor. - * - * If you’re only targeting touch enabled devices with your mapping application, - * you can create a map with only a TouchNavigation control. The - * <OpenLayers.Control.Navigation> control is mobile ready by default, but - * you can generate a smaller build of the library by only including this - * touch navigation control if you aren't concerned about mouse interaction. - * - * Inherits: - * - <OpenLayers.Control> - */ -OpenLayers.Control.TouchNavigation = 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: clickHandlerOptions - * {Object} Options passed to the Click handler. - */ - clickHandlerOptions: null, - - /** - * APIProperty: documentDrag - * {Boolean} Allow panning of the map by dragging outside map viewport. - * Default is false. - */ - documentDrag: false, - - /** - * APIProperty: autoActivate - * {Boolean} Activate the control when it is added to a map. Default is - * true. - */ - autoActivate: true, - - /** - * Constructor: OpenLayers.Control.TouchNavigation - * 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.pinchZoom) { - this.pinchZoom.destroy(); - delete this.pinchZoom; - } - OpenLayers.Control.prototype.destroy.apply(this,arguments); - }, - - /** - * Method: activate - */ - activate: function() { - if(OpenLayers.Control.prototype.activate.apply(this,arguments)) { - this.dragPan.activate(); - this.handlers.click.activate(); - this.pinchZoom.activate(); - return true; - } - return false; - }, - - /** - * Method: deactivate - */ - deactivate: function() { - if(OpenLayers.Control.prototype.deactivate.apply(this,arguments)) { - this.dragPan.deactivate(); - this.handlers.click.deactivate(); - this.pinchZoom.deactivate(); - return true; - } - return false; - }, - - /** - * Method: draw - */ - draw: function() { - var clickCallbacks = { - click: this.defaultClick, - dblclick: this.defaultDblClick - }; - var clickOptions = OpenLayers.Util.extend({ - "double": true, - stopDouble: true, - pixelTolerance: 2 - }, this.clickHandlerOptions); - 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.dragPan.draw(); - 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); - }, - - CLASS_NAME: "OpenLayers.Control.TouchNavigation" -}); -/* ====================================================================== - OpenLayers/Protocol/WFS/v1_0_0.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/WFS/v1.js - * @requires OpenLayers/Format/WFST/v1_0_0.js - */ - -/** - * Class: OpenLayers.Protocol.WFS.v1_0_0 - * A WFS v1.0.0 protocol for vector layers. Create a new instance with the - * <OpenLayers.Protocol.WFS.v1_0_0> constructor. - * - * Inherits from: - * - <OpenLayers.Protocol.WFS.v1> - */ -OpenLayers.Protocol.WFS.v1_0_0 = OpenLayers.Class(OpenLayers.Protocol.WFS.v1, { - - /** - * Property: version - * {String} WFS version number. - */ - version: "1.0.0", - - /** - * Constructor: OpenLayers.Protocol.WFS.v1_0_0 - * A class for giving layers WFS v1.0.0 protocol. - * - * Parameters: - * options - {Object} Optional object whose properties will be set on the - * instance. - * - * Valid options properties: - * featureType - {String} Local (without prefix) feature typeName (required). - * featureNS - {String} Feature namespace (optional). - * featurePrefix - {String} Feature namespace alias (optional - only used - * if featureNS is provided). Default is 'feature'. - * geometryName - {String} Name of geometry attribute. Default is 'the_geom'. - */ - - CLASS_NAME: "OpenLayers.Protocol.WFS.v1_0_0" -}); -/* ====================================================================== - OpenLayers/TileManager.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 - * @requires OpenLayers/BaseTypes/Element.js - * @requires OpenLayers/Layer/Grid.js - * @requires OpenLayers/Tile/Image.js - */ - -/** - * Class: OpenLayers.TileManager - * Provides queueing of image requests and caching of image elements. - * - * Queueing avoids unnecessary image requests while changing zoom levels - * quickly, and helps improve dragging performance on mobile devices that show - * a lag in dragging when loading of new images starts. <zoomDelay> and - * <moveDelay> are the configuration options to control this behavior. - * - * Caching avoids setting the src on image elements for images that have already - * been used. Several maps can share a TileManager instance, in which case each - * map gets its own tile queue, but all maps share the same tile cache. - */ -OpenLayers.TileManager = OpenLayers.Class({ - - /** - * APIProperty: cacheSize - * {Number} Number of image elements to keep referenced in this instance's - * cache for fast reuse. Default is 256. - */ - cacheSize: 256, - - /** - * APIProperty: tilesPerFrame - * {Number} Number of queued tiles to load per frame (see <frameDelay>). - * Default is 2. - */ - tilesPerFrame: 2, - - /** - * APIProperty: frameDelay - * {Number} Delay between tile loading frames (see <tilesPerFrame>) in - * milliseconds. Default is 16. - */ - frameDelay: 16, - - /** - * APIProperty: moveDelay - * {Number} Delay in milliseconds after a map's move event before loading - * tiles. Default is 100. - */ - moveDelay: 100, - - /** - * APIProperty: zoomDelay - * {Number} Delay in milliseconds after a map's zoomend event before loading - * tiles. Default is 200. - */ - zoomDelay: 200, - - /** - * Property: maps - * {Array(<OpenLayers.Map>)} The maps to manage tiles on. - */ - maps: null, - - /** - * Property: tileQueueId - * {Object} The ids of the <drawTilesFromQueue> loop, keyed by map id. - */ - tileQueueId: null, - - /** - * Property: tileQueue - * {Object(Array(<OpenLayers.Tile>))} Tiles queued for drawing, keyed by - * map id. - */ - tileQueue: null, - - /** - * Property: tileCache - * {Object} Cached image elements, keyed by URL. - */ - tileCache: null, - - /** - * Property: tileCacheIndex - * {Array(String)} URLs of cached tiles. First entry is the least recently - * used. - */ - tileCacheIndex: null, - - /** - * Constructor: OpenLayers.TileManager - * Constructor for a new <OpenLayers.TileManager> instance. - * - * Parameters: - * options - {Object} Configuration for this instance. - */ - initialize: function(options) { - OpenLayers.Util.extend(this, options); - this.maps = []; - this.tileQueueId = {}; - this.tileQueue = {}; - this.tileCache = {}; - this.tileCacheIndex = []; - }, - - /** - * Method: addMap - * Binds this instance to a map - * - * Parameters: - * map - {<OpenLayers.Map>} - */ - addMap: function(map) { - if (this._destroyed || !OpenLayers.Layer.Grid) { - return; - } - this.maps.push(map); - this.tileQueue[map.id] = []; - for (var i=0, ii=map.layers.length; i<ii; ++i) { - this.addLayer({layer: map.layers[i]}); - } - map.events.on({ - move: this.move, - zoomend: this.zoomEnd, - changelayer: this.changeLayer, - addlayer: this.addLayer, - preremovelayer: this.removeLayer, - scope: this - }); - }, - - /** - * Method: removeMap - * Unbinds this instance from a map - * - * Parameters: - * map - {<OpenLayers.Map>} - */ - removeMap: function(map) { - if (this._destroyed || !OpenLayers.Layer.Grid) { - return; - } - window.clearTimeout(this.tileQueueId[map.id]); - if (map.layers) { - for (var i=0, ii=map.layers.length; i<ii; ++i) { - this.removeLayer({layer: map.layers[i]}); - } - } - if (map.events) { - map.events.un({ - move: this.move, - zoomend: this.zoomEnd, - changelayer: this.changeLayer, - addlayer: this.addLayer, - preremovelayer: this.removeLayer, - scope: this - }); - } - delete this.tileQueue[map.id]; - delete this.tileQueueId[map.id]; - OpenLayers.Util.removeItem(this.maps, map); - }, - - /** - * Method: move - * Handles the map's move event - * - * Parameters: - * evt - {Object} Listener argument - */ - move: function(evt) { - this.updateTimeout(evt.object, this.moveDelay, true); - }, - - /** - * Method: zoomEnd - * Handles the map's zoomEnd event - * - * Parameters: - * evt - {Object} Listener argument - */ - zoomEnd: function(evt) { - this.updateTimeout(evt.object, this.zoomDelay); - }, - - /** - * Method: changeLayer - * Handles the map's changeLayer event - * - * Parameters: - * evt - {Object} Listener argument - */ - changeLayer: function(evt) { - if (evt.property === 'visibility' || evt.property === 'params') { - this.updateTimeout(evt.object, 0); - } - }, - - /** - * Method: addLayer - * Handles the map's addlayer event - * - * Parameters: - * evt - {Object} The listener argument - */ - addLayer: function(evt) { - var layer = evt.layer; - if (layer instanceof OpenLayers.Layer.Grid) { - layer.events.on({ - addtile: this.addTile, - retile: this.clearTileQueue, - scope: this - }); - var i, j, tile; - for (i=layer.grid.length-1; i>=0; --i) { - for (j=layer.grid[i].length-1; j>=0; --j) { - tile = layer.grid[i][j]; - this.addTile({tile: tile}); - if (tile.url && !tile.imgDiv) { - this.manageTileCache({object: tile}); - } - } - } - } - }, - - /** - * Method: removeLayer - * Handles the map's preremovelayer event - * - * Parameters: - * evt - {Object} The listener argument - */ - removeLayer: function(evt) { - var layer = evt.layer; - if (layer instanceof OpenLayers.Layer.Grid) { - this.clearTileQueue({object: layer}); - if (layer.events) { - layer.events.un({ - addtile: this.addTile, - retile: this.clearTileQueue, - scope: this - }); - } - if (layer.grid) { - var i, j, tile; - for (i=layer.grid.length-1; i>=0; --i) { - for (j=layer.grid[i].length-1; j>=0; --j) { - tile = layer.grid[i][j]; - this.unloadTile({object: tile}); - } - } - } - } - }, - - /** - * Method: updateTimeout - * Applies the <moveDelay> or <zoomDelay> to the <drawTilesFromQueue> loop, - * and schedules more queue processing after <frameDelay> if there are still - * tiles in the queue. - * - * Parameters: - * map - {<OpenLayers.Map>} The map to update the timeout for - * delay - {Number} The delay to apply - * nice - {Boolean} If true, the timeout function will only be created if - * the tilequeue is not empty. This is used by the move handler to - * avoid impacts on dragging performance. For other events, the tile - * queue may not be populated yet, so we need to set the timer - * regardless of the queue size. - */ - updateTimeout: function(map, delay, nice) { - window.clearTimeout(this.tileQueueId[map.id]); - var tileQueue = this.tileQueue[map.id]; - if (!nice || tileQueue.length) { - this.tileQueueId[map.id] = window.setTimeout( - OpenLayers.Function.bind(function() { - this.drawTilesFromQueue(map); - if (tileQueue.length) { - this.updateTimeout(map, this.frameDelay); - } - }, this), delay - ); - } - }, - - /** - * Method: addTile - * Listener for the layer's addtile event - * - * Parameters: - * evt - {Object} The listener argument - */ - addTile: function(evt) { - if (evt.tile instanceof OpenLayers.Tile.Image) { - evt.tile.events.on({ - beforedraw: this.queueTileDraw, - beforeload: this.manageTileCache, - loadend: this.addToCache, - unload: this.unloadTile, - scope: this - }); - } else { - // Layer has the wrong tile type, so don't handle it any longer - this.removeLayer({layer: evt.tile.layer}); - } - }, - - /** - * Method: unloadTile - * Listener for the tile's unload event - * - * Parameters: - * evt - {Object} The listener argument - */ - unloadTile: function(evt) { - var tile = evt.object; - tile.events.un({ - beforedraw: this.queueTileDraw, - beforeload: this.manageTileCache, - loadend: this.addToCache, - unload: this.unloadTile, - scope: this - }); - OpenLayers.Util.removeItem(this.tileQueue[tile.layer.map.id], tile); - }, - - /** - * Method: queueTileDraw - * Adds a tile to the queue that will draw it. - * - * Parameters: - * evt - {Object} Listener argument of the tile's beforedraw event - */ - queueTileDraw: function(evt) { - var tile = evt.object; - var queued = false; - var layer = tile.layer; - var url = layer.getURL(tile.bounds); - var img = this.tileCache[url]; - if (img && img.className !== 'olTileImage') { - // cached image no longer valid, e.g. because we're olTileReplacing - delete this.tileCache[url]; - OpenLayers.Util.removeItem(this.tileCacheIndex, url); - img = null; - } - // queue only if image with same url not cached already - if (layer.url && (layer.async || !img)) { - // add to queue only if not in queue already - var tileQueue = this.tileQueue[layer.map.id]; - if (!~OpenLayers.Util.indexOf(tileQueue, tile)) { - tileQueue.push(tile); - } - queued = true; - } - return !queued; - }, - - /** - * Method: drawTilesFromQueue - * Draws tiles from the tileQueue, and unqueues the tiles - */ - drawTilesFromQueue: function(map) { - var tileQueue = this.tileQueue[map.id]; - var limit = this.tilesPerFrame; - var animating = map.zoomTween && map.zoomTween.playing; - while (!animating && tileQueue.length && limit) { - tileQueue.shift().draw(true); - --limit; - } - }, - - /** - * Method: manageTileCache - * Adds, updates, removes and fetches cache entries. - * - * Parameters: - * evt - {Object} Listener argument of the tile's beforeload event - */ - manageTileCache: function(evt) { - var tile = evt.object; - var img = this.tileCache[tile.url]; - if (img) { - // if image is on its layer's backbuffer, remove it from backbuffer - if (img.parentNode && - OpenLayers.Element.hasClass(img.parentNode, 'olBackBuffer')) { - img.parentNode.removeChild(img); - img.id = null; - } - // only use image from cache if it is not on a layer already - if (!img.parentNode) { - img.style.visibility = 'hidden'; - img.style.opacity = 0; - tile.setImage(img); - // LRU - move tile to the end of the array to mark it as the most - // recently used - OpenLayers.Util.removeItem(this.tileCacheIndex, tile.url); - this.tileCacheIndex.push(tile.url); - } - } - }, - - /** - * Method: addToCache - * - * Parameters: - * evt - {Object} Listener argument for the tile's loadend event - */ - addToCache: function(evt) { - var tile = evt.object; - if (!this.tileCache[tile.url]) { - if (!OpenLayers.Element.hasClass(tile.imgDiv, 'olImageLoadError')) { - if (this.tileCacheIndex.length >= this.cacheSize) { - delete this.tileCache[this.tileCacheIndex[0]]; - this.tileCacheIndex.shift(); - } - this.tileCache[tile.url] = tile.imgDiv; - this.tileCacheIndex.push(tile.url); - } - } - }, - - /** - * Method: clearTileQueue - * Clears the tile queue from tiles of a specific layer - * - * Parameters: - * evt - {Object} Listener argument of the layer's retile event - */ - clearTileQueue: function(evt) { - var layer = evt.object; - var tileQueue = this.tileQueue[layer.map.id]; - for (var i=tileQueue.length-1; i>=0; --i) { - if (tileQueue[i].layer === layer) { - tileQueue.splice(i, 1); - } - } - }, - - /** - * Method: destroy - */ - destroy: function() { - for (var i=this.maps.length-1; i>=0; --i) { - this.removeMap(this.maps[i]); - } - this.maps = null; - this.tileQueue = null; - this.tileQueueId = null; - this.tileCache = null; - this.tileCacheIndex = null; - this._destroyed = true; - } - -}); |