UnknownSec Bypass
403
:
/
usr
/
share
/
javascript
/
openlayers
/ [
drwxr-xr-x
]
Menu
Upload
Mass depes
Mass delete
Terminal
Info server
About
name :
OpenLayers.js
/* 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/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/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/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/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/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/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/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/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/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/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/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/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/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/WKT.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 * @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.WKT * Class for reading and writing Well-Known Text. Create a new instance * with the <OpenLayers.Format.WKT> constructor. * * Inherits from: * - <OpenLayers.Format> */ OpenLayers.Format.WKT = OpenLayers.Class(OpenLayers.Format, { /** * Constructor: OpenLayers.Format.WKT * Create a new parser for WKT * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance * * Returns: * {<OpenLayers.Format.WKT>} A new WKT parser. */ initialize: function(options) { this.regExes = { 'typeStr': /^\s*(\w+)\s*\(\s*(.*)\s*\)\s*$/, 'spaces': /\s+/, 'parenComma': /\)\s*,\s*\(/, 'doubleParenComma': /\)\s*\)\s*,\s*\(\s*\(/, // can't use {2} here 'trimParens': /^\s*\(?(.*?)\)?\s*$/ }; OpenLayers.Format.prototype.initialize.apply(this, [options]); }, /** * APIMethod: read * Deserialize a WKT string and return a vector feature or an * array of vector features. Supports WKT for POINT, MULTIPOINT, * LINESTRING, MULTILINESTRING, POLYGON, MULTIPOLYGON, and * GEOMETRYCOLLECTION. * * Parameters: * wkt - {String} A WKT string * * Returns: * {<OpenLayers.Feature.Vector>|Array} A feature or array of features for * GEOMETRYCOLLECTION WKT. */ read: function(wkt) { var features, type, str; wkt = wkt.replace(/[\n\r]/g, " "); var matches = this.regExes.typeStr.exec(wkt); if(matches) { type = matches[1].toLowerCase(); str = matches[2]; if(this.parse[type]) { features = this.parse[type].apply(this, [str]); } if (this.internalProjection && this.externalProjection) { if (features && features.CLASS_NAME == "OpenLayers.Feature.Vector") { features.geometry.transform(this.externalProjection, this.internalProjection); } else if (features && type != "geometrycollection" && typeof features == "object") { for (var i=0, len=features.length; i<len; i++) { var component = features[i]; component.geometry.transform(this.externalProjection, this.internalProjection); } } } } return features; }, /** * APIMethod: write * Serialize a feature or array of features into a WKT string. * * Parameters: * features - {<OpenLayers.Feature.Vector>|Array} A feature or array of * features * * Returns: * {String} The WKT string representation of the input geometries */ write: function(features) { var collection, geometry, isCollection; if (features.constructor == Array) { collection = features; isCollection = true; } else { collection = [features]; isCollection = false; } var pieces = []; if (isCollection) { pieces.push('GEOMETRYCOLLECTION('); } for (var i=0, len=collection.length; i<len; ++i) { if (isCollection && i>0) { pieces.push(','); } geometry = collection[i].geometry; pieces.push(this.extractGeometry(geometry)); } if (isCollection) { pieces.push(')'); } return pieces.join(''); }, /** * Method: extractGeometry * Entry point to construct the WKT for a single Geometry object. * * Parameters: * geometry - {<OpenLayers.Geometry.Geometry>} * * Returns: * {String} A WKT string of representing the geometry */ extractGeometry: function(geometry) { var type = geometry.CLASS_NAME.split('.')[2].toLowerCase(); if (!this.extract[type]) { return null; } if (this.internalProjection && this.externalProjection) { geometry = geometry.clone(); geometry.transform(this.internalProjection, this.externalProjection); } var wktType = type == 'collection' ? 'GEOMETRYCOLLECTION' : type.toUpperCase(); var data = wktType + '(' + this.extract[type].apply(this, [geometry]) + ')'; return data; }, /** * Object with properties corresponding to the geometry types. * Property values are functions that do the actual data extraction. */ extract: { /** * Return a space delimited string of point coordinates. * @param {OpenLayers.Geometry.Point} point * @returns {String} A string of coordinates representing the point */ 'point': function(point) { return point.x + ' ' + point.y; }, /** * Return a comma delimited string of point coordinates from a multipoint. * @param {OpenLayers.Geometry.MultiPoint} multipoint * @returns {String} A string of point coordinate strings representing * the multipoint */ 'multipoint': function(multipoint) { var array = []; for(var i=0, len=multipoint.components.length; i<len; ++i) { array.push('(' + this.extract.point.apply(this, [multipoint.components[i]]) + ')'); } return array.join(','); }, /** * Return a comma delimited string of point coordinates from a line. * @param {OpenLayers.Geometry.LineString} linestring * @returns {String} A string of point coordinate strings representing * the linestring */ 'linestring': function(linestring) { var array = []; for(var i=0, len=linestring.components.length; i<len; ++i) { array.push(this.extract.point.apply(this, [linestring.components[i]])); } return array.join(','); }, /** * Return a comma delimited string of linestring strings from a multilinestring. * @param {OpenLayers.Geometry.MultiLineString} multilinestring * @returns {String} A string of of linestring strings representing * the multilinestring */ 'multilinestring': function(multilinestring) { var array = []; for(var i=0, len=multilinestring.components.length; i<len; ++i) { array.push('(' + this.extract.linestring.apply(this, [multilinestring.components[i]]) + ')'); } return array.join(','); }, /** * Return a comma delimited string of linear ring arrays from a polygon. * @param {OpenLayers.Geometry.Polygon} polygon * @returns {String} An array of linear ring arrays representing the polygon */ 'polygon': function(polygon) { var array = []; for(var i=0, len=polygon.components.length; i<len; ++i) { array.push('(' + this.extract.linestring.apply(this, [polygon.components[i]]) + ')'); } return array.join(','); }, /** * Return an array of polygon arrays from a multipolygon. * @param {OpenLayers.Geometry.MultiPolygon} multipolygon * @returns {String} An array of polygon arrays representing * the multipolygon */ 'multipolygon': function(multipolygon) { var array = []; for(var i=0, len=multipolygon.components.length; i<len; ++i) { array.push('(' + this.extract.polygon.apply(this, [multipolygon.components[i]]) + ')'); } return array.join(','); }, /** * Return the WKT portion between 'GEOMETRYCOLLECTION(' and ')' for an <OpenLayers.Geometry.Collection> * @param {OpenLayers.Geometry.Collection} collection * @returns {String} internal WKT representation of the collection */ 'collection': function(collection) { var array = []; for(var i=0, len=collection.components.length; i<len; ++i) { array.push(this.extractGeometry.apply(this, [collection.components[i]])); } return array.join(','); } }, /** * Object with properties corresponding to the geometry types. * Property values are functions that do the actual parsing. */ parse: { /** * Return point feature given a point WKT fragment. * @param {String} str A WKT fragment representing the point * @returns {OpenLayers.Feature.Vector} A point feature * @private */ 'point': function(str) { var coords = OpenLayers.String.trim(str).split(this.regExes.spaces); return new OpenLayers.Feature.Vector( new OpenLayers.Geometry.Point(coords[0], coords[1]) ); }, /** * Return a multipoint feature given a multipoint WKT fragment. * @param {String} str A WKT fragment representing the multipoint * @returns {OpenLayers.Feature.Vector} A multipoint feature * @private */ 'multipoint': function(str) { var point; var points = OpenLayers.String.trim(str).split(','); var components = []; for(var i=0, len=points.length; i<len; ++i) { point = points[i].replace(this.regExes.trimParens, '$1'); components.push(this.parse.point.apply(this, [point]).geometry); } return new OpenLayers.Feature.Vector( new OpenLayers.Geometry.MultiPoint(components) ); }, /** * Return a linestring feature given a linestring WKT fragment. * @param {String} str A WKT fragment representing the linestring * @returns {OpenLayers.Feature.Vector} A linestring feature * @private */ 'linestring': function(str) { var points = OpenLayers.String.trim(str).split(','); var components = []; for(var i=0, len=points.length; i<len; ++i) { components.push(this.parse.point.apply(this, [points[i]]).geometry); } return new OpenLayers.Feature.Vector( new OpenLayers.Geometry.LineString(components) ); }, /** * Return a multilinestring feature given a multilinestring WKT fragment. * @param {String} str A WKT fragment representing the multilinestring * @returns {OpenLayers.Feature.Vector} A multilinestring feature * @private */ 'multilinestring': function(str) { var line; var lines = OpenLayers.String.trim(str).split(this.regExes.parenComma); var components = []; for(var i=0, len=lines.length; i<len; ++i) { line = lines[i].replace(this.regExes.trimParens, '$1'); components.push(this.parse.linestring.apply(this, [line]).geometry); } return new OpenLayers.Feature.Vector( new OpenLayers.Geometry.MultiLineString(components) ); }, /** * Return a polygon feature given a polygon WKT fragment. * @param {String} str A WKT fragment representing the polygon * @returns {OpenLayers.Feature.Vector} A polygon feature * @private */ 'polygon': function(str) { var ring, linestring, linearring; var rings = OpenLayers.String.trim(str).split(this.regExes.parenComma); var components = []; for(var i=0, len=rings.length; i<len; ++i) { ring = rings[i].replace(this.regExes.trimParens, '$1'); linestring = this.parse.linestring.apply(this, [ring]).geometry; linearring = new OpenLayers.Geometry.LinearRing(linestring.components); components.push(linearring); } return new OpenLayers.Feature.Vector( new OpenLayers.Geometry.Polygon(components) ); }, /** * Return a multipolygon feature given a multipolygon WKT fragment. * @param {String} str A WKT fragment representing the multipolygon * @returns {OpenLayers.Feature.Vector} A multipolygon feature * @private */ 'multipolygon': function(str) { var polygon; var polygons = OpenLayers.String.trim(str).split(this.regExes.doubleParenComma); var components = []; for(var i=0, len=polygons.length; i<len; ++i) { polygon = polygons[i].replace(this.regExes.trimParens, '$1'); components.push(this.parse.polygon.apply(this, [polygon]).geometry); } return new OpenLayers.Feature.Vector( new OpenLayers.Geometry.MultiPolygon(components) ); }, /** * Return an array of features given a geometrycollection WKT fragment. * @param {String} str A WKT fragment representing the geometrycollection * @returns {Array} An array of OpenLayers.Feature.Vector * @private */ 'geometrycollection': function(str) { // separate components of the collection with | str = str.replace(/,\s*([A-Za-z])/g, '|$1'); var wktArray = OpenLayers.String.trim(str).split('|'); var components = []; for(var i=0, len=wktArray.length; i<len; ++i) { components.push(OpenLayers.Format.WKT.prototype.read.apply(this,[wktArray[i]])); } return components; } }, CLASS_NAME: "OpenLayers.Format.WKT" }); /* ====================================================================== OpenLayers/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/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/Format/OWSCommon.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 */ /** * Class: OpenLayers.Format.OWSCommon * Read OWSCommon. Create a new instance with the <OpenLayers.Format.OWSCommon> * constructor. * * Inherits from: * - <OpenLayers.Format.XML.VersionedOGC> */ OpenLayers.Format.OWSCommon = 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", /** * Constructor: OpenLayers.Format.OWSCommon * Create a new parser for OWSCommon. * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ /** * 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 = this.version; if(!version) { // remember version does not correspond to the OWS version // it corresponds to the WMS/WFS/WCS etc. request version var uri = root.getAttribute("xmlns:ows"); // the above will fail if the namespace prefix is different than // ows and if the namespace is declared on a different element if (uri && uri.substring(uri.lastIndexOf("/")+1) === "1.1") { version ="1.1.0"; } if(!version) { version = this.defaultVersion; } } return version; }, /** * APIMethod: read * Read an OWSCommon document and return an object. * * Parameters: * data - {String | DOMElement} Data to read. * options - {Object} Options for the reader. * * Returns: * {Object} An object representing the structure of the document. */ CLASS_NAME: "OpenLayers.Format.OWSCommon" }); /* ====================================================================== OpenLayers/Format/OWSCommon/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/OWSCommon.js */ /** * Class: OpenLayers.Format.OWSCommon.v1 * Common readers and writers for OWSCommon v1.X formats * * Inherits from: * - <OpenLayers.Format.XML> */ OpenLayers.Format.OWSCommon.v1 = OpenLayers.Class(OpenLayers.Format.XML, { /** * Property: regExes * Compiled regular expressions for manipulating strings. */ regExes: { trimSpace: (/^\s*|\s*$/g), removeSpace: (/\s*/g), splitSpace: (/\s+/), trimComma: (/\s*,\s*/g) }, /** * Method: read * * Parameters: * data - {DOMElement} An OWSCommon document element. * options - {Object} Options for the reader. * * Returns: * {Object} An object representing the OWSCommon document. */ read: function(data, options) { options = OpenLayers.Util.applyDefaults(options, this.options); var ows = {}; this.readChildNodes(data, ows); return ows; }, /** * 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: { "ows": { "Exception": function(node, exceptionReport) { var exception = { code: node.getAttribute('exceptionCode'), locator: node.getAttribute('locator'), texts: [] }; exceptionReport.exceptions.push(exception); this.readChildNodes(node, exception); }, "ExceptionText": function(node, exception) { var text = this.getChildValue(node); exception.texts.push(text); }, "ServiceIdentification": function(node, obj) { obj.serviceIdentification = {}; this.readChildNodes(node, obj.serviceIdentification); }, "Title": function(node, obj) { obj.title = this.getChildValue(node); }, "Abstract": function(node, serviceIdentification) { serviceIdentification["abstract"] = this.getChildValue(node); }, "Keywords": function(node, serviceIdentification) { serviceIdentification.keywords = {}; this.readChildNodes(node, serviceIdentification.keywords); }, "Keyword": function(node, keywords) { keywords[this.getChildValue(node)] = true; }, "ServiceType": function(node, serviceIdentification) { serviceIdentification.serviceType = { codeSpace: node.getAttribute('codeSpace'), value: this.getChildValue(node)}; }, "ServiceTypeVersion": function(node, serviceIdentification) { serviceIdentification.serviceTypeVersion = this.getChildValue(node); }, "Fees": function(node, serviceIdentification) { serviceIdentification.fees = this.getChildValue(node); }, "AccessConstraints": function(node, serviceIdentification) { serviceIdentification.accessConstraints = this.getChildValue(node); }, "ServiceProvider": function(node, obj) { obj.serviceProvider = {}; this.readChildNodes(node, obj.serviceProvider); }, "ProviderName": function(node, serviceProvider) { serviceProvider.providerName = this.getChildValue(node); }, "ProviderSite": function(node, serviceProvider) { serviceProvider.providerSite = this.getAttributeNS(node, this.namespaces.xlink, "href"); }, "ServiceContact": function(node, serviceProvider) { serviceProvider.serviceContact = {}; this.readChildNodes(node, serviceProvider.serviceContact); }, "IndividualName": function(node, serviceContact) { serviceContact.individualName = this.getChildValue(node); }, "PositionName": function(node, serviceContact) { serviceContact.positionName = this.getChildValue(node); }, "ContactInfo": function(node, serviceContact) { serviceContact.contactInfo = {}; this.readChildNodes(node, serviceContact.contactInfo); }, "Phone": function(node, contactInfo) { contactInfo.phone = {}; this.readChildNodes(node, contactInfo.phone); }, "Voice": function(node, phone) { phone.voice = this.getChildValue(node); }, "Address": function(node, contactInfo) { contactInfo.address = {}; this.readChildNodes(node, contactInfo.address); }, "DeliveryPoint": function(node, address) { address.deliveryPoint = this.getChildValue(node); }, "City": function(node, address) { address.city = this.getChildValue(node); }, "AdministrativeArea": function(node, address) { address.administrativeArea = this.getChildValue(node); }, "PostalCode": function(node, address) { address.postalCode = this.getChildValue(node); }, "Country": function(node, address) { address.country = this.getChildValue(node); }, "ElectronicMailAddress": function(node, address) { address.electronicMailAddress = this.getChildValue(node); }, "Role": function(node, serviceContact) { serviceContact.role = this.getChildValue(node); }, "OperationsMetadata": function(node, obj) { obj.operationsMetadata = {}; this.readChildNodes(node, obj.operationsMetadata); }, "Operation": function(node, operationsMetadata) { var name = node.getAttribute("name"); operationsMetadata[name] = {}; this.readChildNodes(node, operationsMetadata[name]); }, "DCP": function(node, operation) { operation.dcp = {}; this.readChildNodes(node, operation.dcp); }, "HTTP": function(node, dcp) { dcp.http = {}; this.readChildNodes(node, dcp.http); }, "Get": function(node, http) { if (!http.get) { http.get = []; } var obj = { url: this.getAttributeNS(node, this.namespaces.xlink, "href") }; this.readChildNodes(node, obj); http.get.push(obj); }, "Post": function(node, http) { if (!http.post) { http.post = []; } var obj = { url: this.getAttributeNS(node, this.namespaces.xlink, "href") }; this.readChildNodes(node, obj); http.post.push(obj); }, "Parameter": function(node, operation) { if (!operation.parameters) { operation.parameters = {}; } var name = node.getAttribute("name"); operation.parameters[name] = {}; this.readChildNodes(node, operation.parameters[name]); }, "Constraint": function(node, obj) { if (!obj.constraints) { obj.constraints = {}; } var name = node.getAttribute("name"); obj.constraints[name] = {}; this.readChildNodes(node, obj.constraints[name]); }, "Value": function(node, allowedValues) { allowedValues[this.getChildValue(node)] = true; }, "OutputFormat": function(node, obj) { obj.formats.push({value: this.getChildValue(node)}); this.readChildNodes(node, obj); }, "WGS84BoundingBox": function(node, obj) { var boundingBox = {}; boundingBox.crs = node.getAttribute("crs"); if (obj.BoundingBox) { obj.BoundingBox.push(boundingBox); } else { obj.projection = boundingBox.crs; boundingBox = obj; } this.readChildNodes(node, boundingBox); }, "BoundingBox": function(node, obj) { // FIXME: We consider that BoundingBox is the same as WGS84BoundingBox // LowerCorner = "min_x min_y" // UpperCorner = "max_x max_y" // It should normally depend on the projection this.readers['ows']['WGS84BoundingBox'].apply(this, [node, obj]); }, "LowerCorner": 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); obj.left = pointList[0]; obj.bottom = pointList[1]; }, "UpperCorner": 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); obj.right = pointList[0]; obj.top = pointList[1]; obj.bounds = new OpenLayers.Bounds(obj.left, obj.bottom, obj.right, obj.top); delete obj.left; delete obj.bottom; delete obj.right; delete obj.top; }, "Language": function(node, obj) { obj.language = this.getChildValue(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: { "ows": { "BoundingBox": function(options, nodeName) { var node = this.createElementNSPlus(nodeName || "ows:BoundingBox", { attributes: { crs: options.projection } }); this.writeNode("ows:LowerCorner", options, node); this.writeNode("ows:UpperCorner", options, node); return node; }, "LowerCorner": function(options) { var node = this.createElementNSPlus("ows:LowerCorner", { value: options.bounds.left + " " + options.bounds.bottom }); return node; }, "UpperCorner": function(options) { var node = this.createElementNSPlus("ows:UpperCorner", { value: options.bounds.right + " " + options.bounds.top }); return node; }, "Identifier": function(identifier) { var node = this.createElementNSPlus("ows:Identifier", { value: identifier }); return node; }, "Title": function(title) { var node = this.createElementNSPlus("ows:Title", { value: title }); return node; }, "Abstract": function(abstractValue) { var node = this.createElementNSPlus("ows:Abstract", { value: abstractValue }); return node; }, "OutputFormat": function(format) { var node = this.createElementNSPlus("ows:OutputFormat", { value: format }); return node; } } }, CLASS_NAME: "OpenLayers.Format.OWSCommon.v1" }); /* ====================================================================== OpenLayers/Format/OWSCommon/v1_1_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/OWSCommon/v1.js */ /** * Class: OpenLayers.Format.OWSCommon.v1_1_0 * Parser for OWS Common version 1.1.0. * * Inherits from: * - <OpenLayers.Format.OWSCommon.v1> */ OpenLayers.Format.OWSCommon.v1_1_0 = OpenLayers.Class(OpenLayers.Format.OWSCommon.v1, { /** * Property: namespaces * {Object} Mapping of namespace aliases to namespace URIs. */ namespaces: { ows: "http://www.opengis.net/ows/1.1", xlink: "http://www.w3.org/1999/xlink" }, /** * 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: { "ows": OpenLayers.Util.applyDefaults({ "ExceptionReport": function(node, obj) { obj.exceptionReport = { version: node.getAttribute('version'), language: node.getAttribute('xml:lang'), exceptions: [] }; this.readChildNodes(node, obj.exceptionReport); }, "AllowedValues": function(node, parameter) { parameter.allowedValues = {}; this.readChildNodes(node, parameter.allowedValues); }, "AnyValue": function(node, parameter) { parameter.anyValue = true; }, "DataType": function(node, parameter) { parameter.dataType = this.getChildValue(node); }, "Range": function(node, allowedValues) { allowedValues.range = {}; this.readChildNodes(node, allowedValues.range); }, "MinimumValue": function(node, range) { range.minValue = this.getChildValue(node); }, "MaximumValue": function(node, range) { range.maxValue = this.getChildValue(node); }, "Identifier": function(node, obj) { obj.identifier = this.getChildValue(node); }, "SupportedCRS": function(node, obj) { obj.supportedCRS = this.getChildValue(node); } }, OpenLayers.Format.OWSCommon.v1.prototype.readers["ows"]) }, /** * 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: { "ows": OpenLayers.Util.applyDefaults({ "Range": function(range) { var node = this.createElementNSPlus("ows:Range", { attributes: { 'ows:rangeClosure': range.closure } }); this.writeNode("ows:MinimumValue", range.minValue, node); this.writeNode("ows:MaximumValue", range.maxValue, node); return node; }, "MinimumValue": function(minValue) { var node = this.createElementNSPlus("ows:MinimumValue", { value: minValue }); return node; }, "MaximumValue": function(maxValue) { var node = this.createElementNSPlus("ows:MaximumValue", { value: maxValue }); return node; }, "Value": function(value) { var node = this.createElementNSPlus("ows:Value", { value: value }); return node; } }, OpenLayers.Format.OWSCommon.v1.prototype.writers["ows"]) }, CLASS_NAME: "OpenLayers.Format.OWSCommon.v1_1_0" }); /* ====================================================================== OpenLayers/Format/WCSGetCoverage.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/OWSCommon/v1_1_0.js */ /** * Class: OpenLayers.Format.WCSGetCoverage version 1.1.0 * * Inherits from: * - <OpenLayers.Format.XML> */ OpenLayers.Format.WCSGetCoverage = OpenLayers.Class(OpenLayers.Format.XML, { /** * Property: namespaces * {Object} Mapping of namespace aliases to namespace URIs. */ namespaces: { ows: "http://www.opengis.net/ows/1.1", wcs: "http://www.opengis.net/wcs/1.1", xlink: "http://www.w3.org/1999/xlink", xsi: "http://www.w3.org/2001/XMLSchema-instance" }, /** * Property: regExes * Compiled regular expressions for manipulating strings. */ regExes: { trimSpace: (/^\s*|\s*$/g), removeSpace: (/\s*/g), splitSpace: (/\s+/), trimComma: (/\s*,\s*/g) }, /** * Constant: VERSION * {String} 1.1.2 */ VERSION: "1.1.2", /** * Property: schemaLocation * {String} Schema location */ schemaLocation: "http://www.opengis.net/wcs/1.1 http://schemas.opengis.net/wcs/1.1/wcsGetCoverage.xsd", /** * Constructor: OpenLayers.Format.WCSGetCoverage * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ /** * Method: write * * Parameters: * options - {Object} Optional object. * * Returns: * {String} A WCS GetCoverage request XML string. */ write: function(options) { var node = this.writeNode("wcs:GetCoverage", options); this.setAttributeNS( node, this.namespaces.xsi, "xsi:schemaLocation", this.schemaLocation ); 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: { "wcs": { "GetCoverage": function(options) { var node = this.createElementNSPlus("wcs:GetCoverage", { attributes: { version: options.version || this.VERSION, service: 'WCS' } }); this.writeNode("ows:Identifier", options.identifier, node); this.writeNode("wcs:DomainSubset", options.domainSubset, node); this.writeNode("wcs:Output", options.output, node); return node; }, "DomainSubset": function(domainSubset) { var node = this.createElementNSPlus("wcs:DomainSubset", {}); this.writeNode("ows:BoundingBox", domainSubset.boundingBox, node); if (domainSubset.temporalSubset) { this.writeNode("wcs:TemporalSubset", domainSubset.temporalSubset, node); } return node; }, "TemporalSubset": function(temporalSubset) { var node = this.createElementNSPlus("wcs:TemporalSubset", {}); for (var i=0, len=temporalSubset.timePeriods.length; i<len; ++i) { this.writeNode("wcs:TimePeriod", temporalSubset.timePeriods[i], node); } return node; }, "TimePeriod": function(timePeriod) { var node = this.createElementNSPlus("wcs:TimePeriod", {}); this.writeNode("wcs:BeginPosition", timePeriod.begin, node); this.writeNode("wcs:EndPosition", timePeriod.end, node); if (timePeriod.resolution) { this.writeNode("wcs:TimeResolution", timePeriod.resolution, node); } return node; }, "BeginPosition": function(begin) { var node = this.createElementNSPlus("wcs:BeginPosition", { value: begin }); return node; }, "EndPosition": function(end) { var node = this.createElementNSPlus("wcs:EndPosition", { value: end }); return node; }, "TimeResolution": function(resolution) { var node = this.createElementNSPlus("wcs:TimeResolution", { value: resolution }); return node; }, "Output": function(output) { var node = this.createElementNSPlus("wcs:Output", { attributes: { format: output.format, store: output.store } }); if (output.gridCRS) { this.writeNode("wcs:GridCRS", output.gridCRS, node); } return node; }, "GridCRS": function(gridCRS) { var node = this.createElementNSPlus("wcs:GridCRS", {}); this.writeNode("wcs:GridBaseCRS", gridCRS.baseCRS, node); if (gridCRS.type) { this.writeNode("wcs:GridType", gridCRS.type, node); } if (gridCRS.origin) { this.writeNode("wcs:GridOrigin", gridCRS.origin, node); } this.writeNode("wcs:GridOffsets", gridCRS.offsets, node); if (gridCRS.CS) { this.writeNode("wcs:GridCS", gridCRS.CS, node); } return node; }, "GridBaseCRS": function(baseCRS) { return this.createElementNSPlus("wcs:GridBaseCRS", { value: baseCRS }); }, "GridOrigin": function(origin) { return this.createElementNSPlus("wcs:GridOrigin", { value: origin }); }, "GridType": function(type) { return this.createElementNSPlus("wcs:GridType", { value: type }); }, "GridOffsets": function(offsets) { return this.createElementNSPlus("wcs:GridOffsets", { value: offsets }); }, "GridCS": function(CS) { return this.createElementNSPlus("wcs:GridCS", { value: CS }); } }, "ows": OpenLayers.Format.OWSCommon.v1_1_0.prototype.writers.ows }, CLASS_NAME: "OpenLayers.Format.WCSGetCoverage" }); /* ====================================================================== 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/Style.js ====================================================================== */ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for * full list of contributors). Published under the 2-clause BSD license. * See license.txt in the OpenLayers distribution or repository for the * full text of the license. */ /** * @requires OpenLayers/BaseTypes/Class.js * @requires OpenLayers/Util.js * @requires OpenLayers/Feature/Vector.js */ /** * Class: OpenLayers.Style * This class represents a UserStyle obtained * from a SLD, containing styling rules. */ OpenLayers.Style = OpenLayers.Class({ /** * Property: id * {String} A unique id for this session. */ id: null, /** * APIProperty: name * {String} */ name: null, /** * Property: title * {String} Title of this style (set if included in SLD) */ title: null, /** * Property: description * {String} Description of this style (set if abstract is included in SLD) */ description: null, /** * APIProperty: layerName * {<String>} name of the layer that this style belongs to, usually * according to the NamedLayer attribute of an SLD document. */ layerName: null, /** * APIProperty: isDefault * {Boolean} */ isDefault: false, /** * Property: rules * {Array(<OpenLayers.Rule>)} */ rules: null, /** * APIProperty: context * {Object} An optional object with properties that symbolizers' property * values should be evaluated against. If no context is specified, * feature.attributes will be used */ context: null, /** * Property: defaultStyle * {Object} hash of style properties to use as default for merging * rule-based style symbolizers onto. If no rules are defined, * createSymbolizer will return this style. If <defaultsPerSymbolizer> is set to * true, the defaultStyle will only be taken into account if there are * rules defined. */ defaultStyle: null, /** * Property: defaultsPerSymbolizer * {Boolean} If set to true, the <defaultStyle> will extend the symbolizer * of every rule. Properties of the <defaultStyle> will also be used to set * missing symbolizer properties if the symbolizer has stroke, fill or * graphic set to true. Default is false. */ defaultsPerSymbolizer: false, /** * Property: propertyStyles * {Hash of Boolean} cache of style properties that need to be parsed for * propertyNames. Property names are keys, values won't be used. */ propertyStyles: null, /** * Constructor: OpenLayers.Style * Creates a UserStyle. * * Parameters: * style - {Object} Optional hash of style properties that will be * used as default style for this style object. This style * applies if no rules are specified. Symbolizers defined in * rules will extend this default style. * options - {Object} An optional object with properties to set on the * style. * * Valid options: * rules - {Array(<OpenLayers.Rule>)} List of rules to be added to the * style. * * Returns: * {<OpenLayers.Style>} */ initialize: function(style, options) { OpenLayers.Util.extend(this, options); this.rules = []; if(options && options.rules) { this.addRules(options.rules); } // use the default style from OpenLayers.Feature.Vector if no style // was given in the constructor this.setDefaultStyle(style || OpenLayers.Feature.Vector.style["default"]); this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_"); }, /** * APIMethod: destroy * nullify references to prevent circular references and memory leaks */ destroy: function() { for (var i=0, len=this.rules.length; i<len; i++) { this.rules[i].destroy(); this.rules[i] = null; } this.rules = null; this.defaultStyle = null; }, /** * Method: createSymbolizer * creates a style by applying all feature-dependent rules to the base * style. * * Parameters: * feature - {<OpenLayers.Feature>} feature to evaluate rules for * * Returns: * {Object} symbolizer hash */ createSymbolizer: function(feature) { var style = this.defaultsPerSymbolizer ? {} : this.createLiterals( OpenLayers.Util.extend({}, this.defaultStyle), feature); var rules = this.rules; var rule, context; var elseRules = []; var appliedRules = false; for(var i=0, len=rules.length; i<len; i++) { rule = rules[i]; // does the rule apply? var applies = rule.evaluate(feature); if(applies) { if(rule instanceof OpenLayers.Rule && rule.elseFilter) { elseRules.push(rule); } else { appliedRules = true; this.applySymbolizer(rule, style, feature); } } } // if no other rules apply, apply the rules with else filters if(appliedRules == false && elseRules.length > 0) { appliedRules = true; for(var i=0, len=elseRules.length; i<len; i++) { this.applySymbolizer(elseRules[i], style, feature); } } // don't display if there were rules but none applied if(rules.length > 0 && appliedRules == false) { style.display = "none"; } if (style.label != null && typeof style.label !== "string") { style.label = String(style.label); } return style; }, /** * Method: applySymbolizer * * Parameters: * rule - {<OpenLayers.Rule>} * style - {Object} * feature - {<OpenLayer.Feature.Vector>} * * Returns: * {Object} A style with new symbolizer applied. */ applySymbolizer: function(rule, style, feature) { var symbolizerPrefix = feature.geometry ? this.getSymbolizerPrefix(feature.geometry) : OpenLayers.Style.SYMBOLIZER_PREFIXES[0]; var symbolizer = rule.symbolizer[symbolizerPrefix] || rule.symbolizer; if(this.defaultsPerSymbolizer === true) { var defaults = this.defaultStyle; OpenLayers.Util.applyDefaults(symbolizer, { pointRadius: defaults.pointRadius }); if(symbolizer.stroke === true || symbolizer.graphic === true) { OpenLayers.Util.applyDefaults(symbolizer, { strokeWidth: defaults.strokeWidth, strokeColor: defaults.strokeColor, strokeOpacity: defaults.strokeOpacity, strokeDashstyle: defaults.strokeDashstyle, strokeLinecap: defaults.strokeLinecap }); } if(symbolizer.fill === true || symbolizer.graphic === true) { OpenLayers.Util.applyDefaults(symbolizer, { fillColor: defaults.fillColor, fillOpacity: defaults.fillOpacity }); } if(symbolizer.graphic === true) { OpenLayers.Util.applyDefaults(symbolizer, { pointRadius: this.defaultStyle.pointRadius, externalGraphic: this.defaultStyle.externalGraphic, graphicName: this.defaultStyle.graphicName, graphicOpacity: this.defaultStyle.graphicOpacity, graphicWidth: this.defaultStyle.graphicWidth, graphicHeight: this.defaultStyle.graphicHeight, graphicXOffset: this.defaultStyle.graphicXOffset, graphicYOffset: this.defaultStyle.graphicYOffset }); } } // merge the style with the current style return this.createLiterals( OpenLayers.Util.extend(style, symbolizer), feature); }, /** * Method: createLiterals * creates literals for all style properties that have an entry in * <this.propertyStyles>. * * Parameters: * style - {Object} style to create literals for. Will be modified * inline. * feature - {Object} * * Returns: * {Object} the modified style */ createLiterals: function(style, feature) { var context = OpenLayers.Util.extend({}, feature.attributes || feature.data); OpenLayers.Util.extend(context, this.context); for (var i in this.propertyStyles) { style[i] = OpenLayers.Style.createLiteral(style[i], context, feature, i); } return style; }, /** * Method: findPropertyStyles * Looks into all rules for this style and the defaultStyle to collect * all the style hash property names containing ${...} strings that have * to be replaced using the createLiteral method before returning them. * * Returns: * {Object} hash of property names that need createLiteral parsing. The * name of the property is the key, and the value is true; */ findPropertyStyles: function() { var propertyStyles = {}; // check the default style var style = this.defaultStyle; this.addPropertyStyles(propertyStyles, style); // walk through all rules to check for properties in their symbolizer var rules = this.rules; var symbolizer, value; for (var i=0, len=rules.length; i<len; i++) { symbolizer = rules[i].symbolizer; for (var key in symbolizer) { value = symbolizer[key]; if (typeof value == "object") { // symbolizer key is "Point", "Line" or "Polygon" this.addPropertyStyles(propertyStyles, value); } else { // symbolizer is a hash of style properties this.addPropertyStyles(propertyStyles, symbolizer); break; } } } return propertyStyles; }, /** * Method: addPropertyStyles * * Parameters: * propertyStyles - {Object} hash to add new property styles to. Will be * modified inline * symbolizer - {Object} search this symbolizer for property styles * * Returns: * {Object} propertyStyles hash */ addPropertyStyles: function(propertyStyles, symbolizer) { var property; for (var key in symbolizer) { property = symbolizer[key]; if (typeof property == "string" && property.match(/\$\{\w+\}/)) { propertyStyles[key] = true; } } return propertyStyles; }, /** * APIMethod: addRules * Adds rules to this style. * * Parameters: * rules - {Array(<OpenLayers.Rule>)} */ addRules: function(rules) { Array.prototype.push.apply(this.rules, rules); this.propertyStyles = this.findPropertyStyles(); }, /** * APIMethod: setDefaultStyle * Sets the default style for this style object. * * Parameters: * style - {Object} Hash of style properties */ setDefaultStyle: function(style) { this.defaultStyle = style; this.propertyStyles = this.findPropertyStyles(); }, /** * Method: getSymbolizerPrefix * Returns the correct symbolizer prefix according to the * geometry type of the passed geometry * * Parameters: * geometry - {<OpenLayers.Geometry>} * * Returns: * {String} key of the according symbolizer */ getSymbolizerPrefix: function(geometry) { var prefixes = OpenLayers.Style.SYMBOLIZER_PREFIXES; for (var i=0, len=prefixes.length; i<len; i++) { if (geometry.CLASS_NAME.indexOf(prefixes[i]) != -1) { return prefixes[i]; } } }, /** * APIMethod: clone * Clones this style. * * Returns: * {<OpenLayers.Style>} Clone of this style. */ clone: function() { var options = OpenLayers.Util.extend({}, this); // clone rules if(this.rules) { options.rules = []; for(var i=0, len=this.rules.length; i<len; ++i) { options.rules.push(this.rules[i].clone()); } } // clone context options.context = this.context && OpenLayers.Util.extend({}, this.context); //clone default style var defaultStyle = OpenLayers.Util.extend({}, this.defaultStyle); return new OpenLayers.Style(defaultStyle, options); }, CLASS_NAME: "OpenLayers.Style" }); /** * Function: createLiteral * converts a style value holding a combination of PropertyName and Literal * into a Literal, taking the property values from the passed features. * * Parameters: * value - {String} value to parse. If this string contains a construct like * "foo ${bar}", then "foo " will be taken as literal, and "${bar}" * will be replaced by the value of the "bar" attribute of the passed * feature. * context - {Object} context to take attribute values from * feature - {<OpenLayers.Feature.Vector>} optional feature to pass to * <OpenLayers.String.format> for evaluating functions in the * context. * property - {String} optional, name of the property for which the literal is * being created for evaluating functions in the context. * * Returns: * {String} the parsed value. In the example of the value parameter above, the * result would be "foo valueOfBar", assuming that the passed feature has an * attribute named "bar" with the value "valueOfBar". */ OpenLayers.Style.createLiteral = function(value, context, feature, property) { if (typeof value == "string" && value.indexOf("${") != -1) { value = OpenLayers.String.format(value, context, [feature, property]); value = (isNaN(value) || !value) ? value : parseFloat(value); } return value; }; /** * Constant: OpenLayers.Style.SYMBOLIZER_PREFIXES * {Array} prefixes of the sld symbolizers. These are the * same as the main geometry types */ OpenLayers.Style.SYMBOLIZER_PREFIXES = ['Point', 'Line', 'Polygon', 'Text', 'Raster']; /* ====================================================================== OpenLayers/Filter.js ====================================================================== */ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for * full list of contributors). Published under the 2-clause BSD license. * See license.txt in the OpenLayers distribution or repository for the * full text of the license. */ /** * @requires OpenLayers/BaseTypes/Class.js * @requires OpenLayers/Util.js * @requires OpenLayers/Style.js */ /** * Class: OpenLayers.Filter * This class represents an OGC Filter. */ OpenLayers.Filter = OpenLayers.Class({ /** * Constructor: OpenLayers.Filter * This class represents a generic filter. * * Parameters: * options - {Object} Optional object whose properties will be set on the * instance. * * Returns: * {<OpenLayers.Filter>} */ initialize: function(options) { OpenLayers.Util.extend(this, options); }, /** * APIMethod: destroy * Remove reference to anything added. */ destroy: function() { }, /** * APIMethod: evaluate * Evaluates this filter in a specific context. Instances or subclasses * are supposed to override this method. * * Parameters: * context - {Object} Context to use in evaluating the filter. If a vector * feature is provided, the feature.attributes will be used as context. * * Returns: * {Boolean} The filter applies. */ evaluate: function(context) { return true; }, /** * APIMethod: clone * Clones this filter. Should be implemented by subclasses. * * Returns: * {<OpenLayers.Filter>} Clone of this filter. */ clone: function() { return null; }, /** * APIMethod: toString * * Returns: * {String} Include <OpenLayers.Format.CQL> in your build to get a CQL * representation of the filter returned. Otherwise "[Object object]" * will be returned. */ toString: function() { var string; if (OpenLayers.Format && OpenLayers.Format.CQL) { string = OpenLayers.Format.CQL.prototype.write(this); } else { string = Object.prototype.toString.call(this); } return string; }, CLASS_NAME: "OpenLayers.Filter" }); /* ====================================================================== OpenLayers/Filter/Spatial.js ====================================================================== */ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for * full list of contributors). Published under the 2-clause BSD license. * See license.txt in the OpenLayers distribution or repository for the * full text of the license. */ /** * @requires OpenLayers/Filter.js */ /** * Class: OpenLayers.Filter.Spatial * This class represents a spatial filter. * Currently implemented: BBOX, DWithin and Intersects * * Inherits from: * - <OpenLayers.Filter> */ OpenLayers.Filter.Spatial = OpenLayers.Class(OpenLayers.Filter, { /** * APIProperty: type * {String} Type of spatial filter. * * The type should be one of: * - OpenLayers.Filter.Spatial.BBOX * - OpenLayers.Filter.Spatial.INTERSECTS * - OpenLayers.Filter.Spatial.DWITHIN * - OpenLayers.Filter.Spatial.WITHIN * - OpenLayers.Filter.Spatial.CONTAINS */ type: null, /** * APIProperty: property * {String} Name of the context property to compare. */ property: null, /** * APIProperty: value * {<OpenLayers.Bounds> || <OpenLayers.Geometry>} The bounds or geometry * to be used by the filter. Use bounds for BBOX filters and geometry * for INTERSECTS or DWITHIN filters. */ value: null, /** * APIProperty: distance * {Number} The distance to use in a DWithin spatial filter. */ distance: null, /** * APIProperty: distanceUnits * {String} The units to use for the distance, e.g. 'm'. */ distanceUnits: null, /** * Constructor: OpenLayers.Filter.Spatial * Creates a spatial filter. * * Parameters: * options - {Object} An optional object with properties to set on the * filter. * * Returns: * {<OpenLayers.Filter.Spatial>} */ /** * Method: evaluate * Evaluates this filter for a specific feature. * * Parameters: * feature - {<OpenLayers.Feature.Vector>} feature to apply the filter to. * * Returns: * {Boolean} The feature meets filter criteria. */ evaluate: function(feature) { var intersect = false; switch(this.type) { case OpenLayers.Filter.Spatial.BBOX: case OpenLayers.Filter.Spatial.INTERSECTS: if(feature.geometry) { var geom = this.value; if(this.value.CLASS_NAME == "OpenLayers.Bounds") { geom = this.value.toGeometry(); } if(feature.geometry.intersects(geom)) { intersect = true; } } break; default: throw new Error('evaluate is not implemented for this filter type.'); } return intersect; }, /** * APIMethod: clone * Clones this filter. * * Returns: * {<OpenLayers.Filter.Spatial>} Clone of this filter. */ clone: function() { var options = OpenLayers.Util.applyDefaults({ value: this.value && this.value.clone && this.value.clone() }, this); return new OpenLayers.Filter.Spatial(options); }, CLASS_NAME: "OpenLayers.Filter.Spatial" }); OpenLayers.Filter.Spatial.BBOX = "BBOX"; OpenLayers.Filter.Spatial.INTERSECTS = "INTERSECTS"; OpenLayers.Filter.Spatial.DWITHIN = "DWITHIN"; OpenLayers.Filter.Spatial.WITHIN = "WITHIN"; OpenLayers.Filter.Spatial.CONTAINS = "CONTAINS"; /* ====================================================================== OpenLayers/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/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/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/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/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/v3.js ====================================================================== */ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for * full list of contributors). Published under the 2-clause BSD license. * See license.txt in the OpenLayers distribution or repository for the * full text of the license. */ /** * @requires OpenLayers/Format/GML/Base.js */ /** * Class: OpenLayers.Format.GML.v3 * Parses GML version 3. * * Inherits from: * - <OpenLayers.Format.GML.Base> */ OpenLayers.Format.GML.v3 = OpenLayers.Class(OpenLayers.Format.GML.Base, { /** * Property: schemaLocation * {String} Schema location for a particular minor version. The writers * conform with the Simple Features Profile for GML. */ schemaLocation: "http://www.opengis.net/gml http://schemas.opengis.net/gml/3.1.1/profiles/gmlsfProfile/1.0.0/gmlsf.xsd", /** * Property: curve * {Boolean} Write gml:Curve instead of gml:LineString elements. This also * affects the elements in multi-part geometries. Default is false. * To write gml:Curve elements instead of gml:LineString, set curve * to true in the options to the contstructor (cannot be changed after * instantiation). */ curve: false, /** * Property: multiCurve * {Boolean} Write gml:MultiCurve instead of gml:MultiLineString. Since * the latter is deprecated in GML 3, the default is true. To write * gml:MultiLineString instead of gml:MultiCurve, set multiCurve to * false in the options to the constructor (cannot be changed after * instantiation). */ multiCurve: true, /** * Property: surface * {Boolean} Write gml:Surface instead of gml:Polygon elements. This also * affects the elements in multi-part geometries. Default is false. * To write gml:Surface elements instead of gml:Polygon, set surface * to true in the options to the contstructor (cannot be changed after * instantiation). */ surface: false, /** * Property: multiSurface * {Boolean} Write gml:multiSurface instead of gml:MultiPolygon. Since * the latter is deprecated in GML 3, the default is true. To write * gml:MultiPolygon instead of gml:multiSurface, set multiSurface to * false in the options to the constructor (cannot be changed after * instantiation). */ multiSurface: true, /** * Constructor: OpenLayers.Format.GML.v3 * Create a parser for GML v3. * * 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({ "_inherit": function(node, obj, container) { // SRSReferenceGroup attributes var dim = parseInt(node.getAttribute("srsDimension"), 10) || (container && container.srsDimension); if (dim) { obj.srsDimension = dim; } }, "featureMembers": function(node, obj) { this.readChildNodes(node, obj); }, "Curve": function(node, container) { var obj = {points: []}; 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) ); }, "segments": function(node, obj) { this.readChildNodes(node, obj); }, "LineStringSegment": function(node, container) { var obj = {}; this.readChildNodes(node, obj); if(obj.points) { Array.prototype.push.apply(container.points, obj.points); } }, "pos": function(node, obj) { var str = this.getChildValue(node).replace( this.regExes.trimSpace, "" ); var coords = str.split(this.regExes.splitSpace); var point; if(this.xy) { point = new OpenLayers.Geometry.Point( coords[0], coords[1], coords[2] ); } else { point = new OpenLayers.Geometry.Point( coords[1], coords[0], coords[2] ); } obj.points = [point]; }, "posList": function(node, obj) { var str = this.getChildValue(node).replace( this.regExes.trimSpace, "" ); var coords = str.split(this.regExes.splitSpace); // The "dimension" attribute is from the GML 3.0.1 spec. var dim = obj.srsDimension || parseInt(node.getAttribute("srsDimension") || node.getAttribute("dimension"), 10) || 2; var j, x, y, z; var numPoints = coords.length / dim; var points = new Array(numPoints); for(var i=0, len=coords.length; i<len; i += dim) { x = coords[i]; y = coords[i+1]; z = (dim == 2) ? undefined : coords[i+2]; if (this.xy) { points[i/dim] = new OpenLayers.Geometry.Point(x, y, z); } else { points[i/dim] = new OpenLayers.Geometry.Point(y, x, z); } } obj.points = points; }, "Surface": function(node, obj) { this.readChildNodes(node, obj); }, "patches": function(node, obj) { this.readChildNodes(node, obj); }, "PolygonPatch": function(node, obj) { this.readers.gml.Polygon.apply(this, [node, obj]); }, "exterior": function(node, container) { var obj = {}; this.readChildNodes(node, obj); container.outer = obj.components[0]; }, "interior": function(node, container) { var obj = {}; this.readChildNodes(node, obj); container.inner.push(obj.components[0]); }, "MultiCurve": function(node, container) { var obj = {components: []}; this.readers.gml._inherit.apply(this, [node, obj, container]); this.readChildNodes(node, obj); if(obj.components.length > 0) { container.components = [ new OpenLayers.Geometry.MultiLineString(obj.components) ]; } }, "curveMember": function(node, obj) { this.readChildNodes(node, obj); }, "MultiSurface": function(node, container) { var obj = {components: []}; this.readers.gml._inherit.apply(this, [node, obj, container]); this.readChildNodes(node, obj); if(obj.components.length > 0) { container.components = [ new OpenLayers.Geometry.MultiPolygon(obj.components) ]; } }, "surfaceMember": function(node, obj) { this.readChildNodes(node, obj); }, "surfaceMembers": function(node, obj) { this.readChildNodes(node, obj); }, "pointMembers": function(node, obj) { this.readChildNodes(node, obj); }, "lineStringMembers": function(node, obj) { this.readChildNodes(node, obj); }, "polygonMembers": function(node, obj) { this.readChildNodes(node, obj); }, "geometryMembers": function(node, obj) { this.readChildNodes(node, obj); }, "Envelope": function(node, container) { var obj = {points: new Array(2)}; 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) ); }, "lowerCorner": function(node, container) { var obj = {}; this.readers.gml.pos.apply(this, [node, obj]); container.points[0] = obj.points[0]; }, "upperCorner": function(node, container) { var obj = {}; this.readers.gml.pos.apply(this, [node, obj]); container.points[1] = obj.points[0]; } }, 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)) { 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": OpenLayers.Util.applyDefaults({ "featureMembers": function(features) { var node = this.createElementNSPlus("gml:featureMembers"); for(var i=0, len=features.length; i<len; ++i) { this.writeNode("feature:_typeName", features[i], node); } return node; }, "Point": function(geometry) { var node = this.createElementNSPlus("gml:Point"); this.writeNode("pos", geometry, node); return node; }, "pos": function(point) { // only 2d for simple features profile var pos = (this.xy) ? (point.x + " " + point.y) : (point.y + " " + point.x); return this.createElementNSPlus("gml:pos", { value: pos }); }, "LineString": function(geometry) { var node = this.createElementNSPlus("gml:LineString"); this.writeNode("posList", geometry.components, node); return node; }, "Curve": function(geometry) { var node = this.createElementNSPlus("gml:Curve"); this.writeNode("segments", geometry, node); return node; }, "segments": function(geometry) { var node = this.createElementNSPlus("gml:segments"); this.writeNode("LineStringSegment", geometry, node); return node; }, "LineStringSegment": function(geometry) { var node = this.createElementNSPlus("gml:LineStringSegment"); this.writeNode("posList", geometry.components, node); return node; }, "posList": function(points) { // only 2d for simple features profile var len = points.length; var parts = new Array(len); var point; for(var i=0; i<len; ++i) { point = points[i]; if(this.xy) { parts[i] = point.x + " " + point.y; } else { parts[i] = point.y + " " + point.x; } } return this.createElementNSPlus("gml:posList", { value: parts.join(" ") }); }, "Surface": function(geometry) { var node = this.createElementNSPlus("gml:Surface"); this.writeNode("patches", geometry, node); return node; }, "patches": function(geometry) { var node = this.createElementNSPlus("gml:patches"); this.writeNode("PolygonPatch", geometry, node); return node; }, "PolygonPatch": function(geometry) { var node = this.createElementNSPlus("gml:PolygonPatch", { attributes: {interpolation: "planar"} }); this.writeNode("exterior", geometry.components[0], node); for(var i=1, len=geometry.components.length; i<len; ++i) { this.writeNode( "interior", geometry.components[i], node ); } return node; }, "Polygon": function(geometry) { var node = this.createElementNSPlus("gml:Polygon"); this.writeNode("exterior", geometry.components[0], node); for(var i=1, len=geometry.components.length; i<len; ++i) { this.writeNode( "interior", geometry.components[i], node ); } return node; }, "exterior": function(ring) { var node = this.createElementNSPlus("gml:exterior"); this.writeNode("LinearRing", ring, node); return node; }, "interior": function(ring) { var node = this.createElementNSPlus("gml:interior"); this.writeNode("LinearRing", ring, node); return node; }, "LinearRing": function(ring) { var node = this.createElementNSPlus("gml:LinearRing"); this.writeNode("posList", ring.components, node); return node; }, "MultiCurve": function(geometry) { var node = this.createElementNSPlus("gml:MultiCurve"); var components = geometry.components || [geometry]; for(var i=0, len=components.length; i<len; ++i) { this.writeNode("curveMember", components[i], node); } return node; }, "curveMember": function(geometry) { var node = this.createElementNSPlus("gml:curveMember"); if(this.curve) { this.writeNode("Curve", geometry, node); } else { this.writeNode("LineString", geometry, node); } return node; }, "MultiSurface": function(geometry) { var node = this.createElementNSPlus("gml:MultiSurface"); var components = geometry.components || [geometry]; for(var i=0, len=components.length; i<len; ++i) { this.writeNode("surfaceMember", components[i], node); } return node; }, "surfaceMember": function(polygon) { var node = this.createElementNSPlus("gml:surfaceMember"); if(this.surface) { this.writeNode("Surface", polygon, node); } else { this.writeNode("Polygon", polygon, node); } return node; }, "Envelope": function(bounds) { var node = this.createElementNSPlus("gml:Envelope"); this.writeNode("lowerCorner", bounds, node); this.writeNode("upperCorner", bounds, node); // srsName attribute is required for gml:Envelope if(this.srsName) { node.setAttribute("srsName", this.srsName); } return node; }, "lowerCorner": function(bounds) { // only 2d for simple features profile var pos = (this.xy) ? (bounds.left + " " + bounds.bottom) : (bounds.bottom + " " + bounds.left); return this.createElementNSPlus("gml:lowerCorner", { value: pos }); }, "upperCorner": function(bounds) { // only 2d for simple features profile var pos = (this.xy) ? (bounds.right + " " + bounds.top) : (bounds.top + " " + bounds.right); return this.createElementNSPlus("gml:upperCorner", { value: pos }); } }, OpenLayers.Format.GML.Base.prototype.writers["gml"]), "feature": OpenLayers.Format.GML.Base.prototype.writers["feature"], "wfs": OpenLayers.Format.GML.Base.prototype.writers["wfs"] }, /** * Method: setGeometryTypes * Sets the <geometryTypes> mapping. */ setGeometryTypes: function() { this.geometryTypes = { "OpenLayers.Geometry.Point": "Point", "OpenLayers.Geometry.MultiPoint": "MultiPoint", "OpenLayers.Geometry.LineString": (this.curve === true) ? "Curve": "LineString", "OpenLayers.Geometry.MultiLineString": (this.multiCurve === false) ? "MultiLineString" : "MultiCurve", "OpenLayers.Geometry.Polygon": (this.surface === true) ? "Surface" : "Polygon", "OpenLayers.Geometry.MultiPolygon": (this.multiSurface === false) ? "MultiPolygon" : "MultiSurface", "OpenLayers.Geometry.Collection": "GeometryCollection" }; }, CLASS_NAME: "OpenLayers.Format.GML.v3" }); /* ====================================================================== OpenLayers/Format/Filter/v1_1_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/Filter/v1.js * @requires OpenLayers/Format/GML/v3.js */ /** * Class: OpenLayers.Format.Filter.v1_1_0 * Write ogc:Filter version 1.1.0. * * Differences from the v1.0.0 parser: * - uses GML v3 instead of GML v2 * - reads matchCase attribute on ogc:PropertyIsEqual and * ogc:PropertyIsNotEqual elements. * - writes matchCase attribute from comparison filters of type EQUAL_TO, * NOT_EQUAL_TO and LIKE. * * Inherits from: * - <OpenLayers.Format.GML.v3> * - <OpenLayers.Format.Filter.v1> */ OpenLayers.Format.Filter.v1_1_0 = OpenLayers.Class( OpenLayers.Format.GML.v3, OpenLayers.Format.Filter.v1, { /** * Constant: VERSION * {String} 1.1.0 */ VERSION: "1.1.0", /** * Property: schemaLocation * {String} http://www.opengis.net/ogc/filter/1.1.0/filter.xsd */ schemaLocation: "http://www.opengis.net/ogc/filter/1.1.0/filter.xsd", /** * Constructor: OpenLayers.Format.Filter.v1_1_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.v3.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 matchCase = node.getAttribute("matchCase"); var filter = new OpenLayers.Filter.Comparison({ type: OpenLayers.Filter.Comparison.EQUAL_TO, matchCase: !(matchCase === "false" || matchCase === "0") }); this.readChildNodes(node, filter); obj.filters.push(filter); }, "PropertyIsNotEqualTo": function(node, obj) { var matchCase = node.getAttribute("matchCase"); var filter = new OpenLayers.Filter.Comparison({ type: OpenLayers.Filter.Comparison.NOT_EQUAL_TO, matchCase: !(matchCase === "false" || matchCase === "0") }); 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("escapeChar"); filter.value2regex(wildCard, singleChar, esc); obj.filters.push(filter); } }, OpenLayers.Format.Filter.v1.prototype.readers["ogc"]), "gml": OpenLayers.Format.GML.v3.prototype.readers["gml"], "feature": OpenLayers.Format.GML.v3.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", { attributes: {matchCase: filter.matchCase} }); // 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", { attributes: {matchCase: filter.matchCase} }); // 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: { matchCase: filter.matchCase, wildCard: "*", singleChar: ".", escapeChar: "!" } }); // 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 optional in 1.1.0 filter.property && this.writeNode("PropertyName", filter, node); var box = this.writeNode("gml:Envelope", filter.value); if(filter.projection) { box.setAttribute("srsName", filter.projection); } node.appendChild(box); return node; }, "SortBy": function(sortProperties) { var node = this.createElementNSPlus("ogc:SortBy"); for (var i=0,l=sortProperties.length;i<l;i++) { this.writeNode( "ogc:SortProperty", sortProperties[i], node ); } return node; }, "SortProperty": function(sortProperty) { var node = this.createElementNSPlus("ogc:SortProperty"); this.writeNode( "ogc:PropertyName", sortProperty, node ); this.writeNode( "ogc:SortOrder", (sortProperty.order == 'DESC') ? 'DESC' : 'ASC', node ); return node; }, "SortOrder": function(value) { var node = this.createElementNSPlus("ogc:SortOrder", { value: value }); return node; } }, OpenLayers.Format.Filter.v1.prototype.writers["ogc"]), "gml": OpenLayers.Format.GML.v3.prototype.writers["gml"], "feature": OpenLayers.Format.GML.v3.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:Envelope", filter.value); } if(filter.projection) { child.setAttribute("srsName", filter.projection); } node.appendChild(child); } return node; }, CLASS_NAME: "OpenLayers.Format.Filter.v1_1_0" }); /* ====================================================================== OpenLayers/Format/OWSCommon/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/OWSCommon/v1.js */ /** * Class: OpenLayers.Format.OWSCommon.v1_0_0 * Parser for OWS Common version 1.0.0. * * Inherits from: * - <OpenLayers.Format.OWSCommon.v1> */ OpenLayers.Format.OWSCommon.v1_0_0 = OpenLayers.Class(OpenLayers.Format.OWSCommon.v1, { /** * Property: namespaces * {Object} Mapping of namespace aliases to namespace URIs. */ namespaces: { ows: "http://www.opengis.net/ows", xlink: "http://www.w3.org/1999/xlink" }, /** * 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: { "ows": OpenLayers.Util.applyDefaults({ "ExceptionReport": function(node, obj) { obj.success = false; obj.exceptionReport = { version: node.getAttribute('version'), language: node.getAttribute('language'), exceptions: [] }; this.readChildNodes(node, obj.exceptionReport); } }, OpenLayers.Format.OWSCommon.v1.prototype.readers.ows) }, /** * 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: { "ows": OpenLayers.Format.OWSCommon.v1.prototype.writers.ows }, CLASS_NAME: "OpenLayers.Format.OWSCommon.v1_0_0" }); /* ====================================================================== OpenLayers/Format/WFST/v1_1_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_1_0.js * @requires OpenLayers/Format/OWSCommon/v1_0_0.js */ /** * Class: OpenLayers.Format.WFST.v1_1_0 * A format for creating WFS v1.1.0 transactions. Create a new instance with the * <OpenLayers.Format.WFST.v1_1_0> constructor. * * Inherits from: * - <OpenLayers.Format.Filter.v1_1_0> * - <OpenLayers.Format.WFST.v1> */ OpenLayers.Format.WFST.v1_1_0 = OpenLayers.Class( OpenLayers.Format.Filter.v1_1_0, OpenLayers.Format.WFST.v1, { /** * Property: version * {String} WFS version number. */ version: "1.1.0", /** * Property: schemaLocations * {Object} Properties are namespace aliases, values are schema locations. */ schemaLocations: { "wfs": "http://schemas.opengis.net/wfs/1.1.0/wfs.xsd" }, /** * Constructor: OpenLayers.Format.WFST.v1_1_0 * A class for parsing and generating WFS v1.1.0 transactions. * * To read additional information like hit count (numberOfFeatures) from * the FeatureCollection, call the <OpenLayers.Format.WFST.v1.read> method * with {output: "object"} as 2nd argument. Note that it is possible to * just request the hit count from a WFS 1.1.0 server with the * resultType="hits" request parameter. * * 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_1_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.v3. 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.v3.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({ "FeatureCollection": function(node, obj) { obj.numberOfFeatures = parseInt(node.getAttribute( "numberOfFeatures")); OpenLayers.Format.WFST.v1.prototype.readers["wfs"]["FeatureCollection"].apply( this, arguments); }, "TransactionResponse": function(node, obj) { obj.insertIds = []; obj.success = false; this.readChildNodes(node, obj); }, "TransactionSummary": function(node, obj) { // this is a limited test of success obj.success = true; }, "InsertResults": function(node, obj) { this.readChildNodes(node, obj); }, "Feature": function(node, container) { var obj = {fids: []}; this.readChildNodes(node, obj); container.insertIds.push(obj.fids[0]); } }, OpenLayers.Format.WFST.v1.prototype.readers["wfs"]), "gml": OpenLayers.Format.GML.v3.prototype.readers["gml"], "feature": OpenLayers.Format.GML.v3.prototype.readers["feature"], "ogc": OpenLayers.Format.Filter.v1_1_0.prototype.readers["ogc"], "ows": OpenLayers.Format.OWSCommon.v1_0_0.prototype.readers["ows"] }, /** * 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({ "GetFeature": function(options) { var node = OpenLayers.Format.WFST.v1.prototype.writers["wfs"]["GetFeature"].apply(this, arguments); options && this.setAttributes(node, { resultType: options.resultType, startIndex: options.startIndex, count: options.count }); return node; }, "Query": function(options) { options = OpenLayers.Util.extend({ featureNS: this.featureNS, featurePrefix: this.featurePrefix, featureType: this.featureType, srsName: this.srsName }, options); var prefix = options.featurePrefix; var node = this.createElementNSPlus("wfs:Query", { attributes: { typeName: (prefix ? prefix + ":" : "") + options.featureType, 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( "wfs:PropertyName", {property: options.propertyNames[i]}, node ); } } if(options.filter) { OpenLayers.Format.WFST.v1_1_0.prototype.setFilterProperty.call(this, options.filter); this.writeNode("ogc:Filter", options.filter, node); } return node; }, "PropertyName": function(obj) { return this.createElementNSPlus("wfs:PropertyName", { value: obj.property }); } }, OpenLayers.Format.WFST.v1.prototype.writers["wfs"]), "gml": OpenLayers.Format.GML.v3.prototype.writers["gml"], "feature": OpenLayers.Format.GML.v3.prototype.writers["feature"], "ogc": OpenLayers.Format.Filter.v1_1_0.prototype.writers["ogc"] }, CLASS_NAME: "OpenLayers.Format.WFST.v1_1_0" }); /* ====================================================================== OpenLayers/Format/WPSExecute.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/OWSCommon/v1_1_0.js * @requires OpenLayers/Format/WCSGetCoverage.js * @requires OpenLayers/Format/WFST/v1_1_0.js */ /** * Class: OpenLayers.Format.WPSExecute version 1.0.0 * * Inherits from: * - <OpenLayers.Format.XML> */ OpenLayers.Format.WPSExecute = OpenLayers.Class(OpenLayers.Format.XML, OpenLayers.Format.Filter.v1_1_0, { /** * Property: namespaces * {Object} Mapping of namespace aliases to namespace URIs. */ namespaces: { ows: "http://www.opengis.net/ows/1.1", gml: "http://www.opengis.net/gml", wps: "http://www.opengis.net/wps/1.0.0", wfs: "http://www.opengis.net/wfs", ogc: "http://www.opengis.net/ogc", wcs: "http://www.opengis.net/wcs", xlink: "http://www.w3.org/1999/xlink", xsi: "http://www.w3.org/2001/XMLSchema-instance" }, /** * Property: regExes * Compiled regular expressions for manipulating strings. */ regExes: { trimSpace: (/^\s*|\s*$/g), removeSpace: (/\s*/g), splitSpace: (/\s+/), trimComma: (/\s*,\s*/g) }, /** * Constant: VERSION * {String} 1.0.0 */ VERSION: "1.0.0", /** * Property: schemaLocation * {String} Schema location */ schemaLocation: "http://www.opengis.net/wps/1.0.0 http://schemas.opengis.net/wps/1.0.0/wpsAll.xsd", schemaLocationAttr: function(options) { return undefined; }, /** * Constructor: OpenLayers.Format.WPSExecute * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ /** * Method: write * * Parameters: * options - {Object} Optional object. * * Returns: * {String} An WPS Execute request XML string. */ write: function(options) { var doc; if (window.ActiveXObject) { doc = new ActiveXObject("Microsoft.XMLDOM"); this.xmldom = doc; } else { doc = document.implementation.createDocument("", "", null); } var node = this.writeNode("wps:Execute", options, doc); this.setAttributeNS( node, this.namespaces.xsi, "xsi:schemaLocation", this.schemaLocation ); return OpenLayers.Format.XML.prototype.write.apply(this, [node]); }, /** * APIMethod: read * Parse a WPS Execute and return an object with its information. * * Parameters: * data - {String} or {DOMElement} data to read/parse. * * Returns: * {Object} */ 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 info = {}; this.readNode(data, info); return info; }, /** * 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: { "wps": { "Execute": function(options) { var node = this.createElementNSPlus("wps:Execute", { attributes: { version: this.VERSION, service: 'WPS' } }); this.writeNode("ows:Identifier", options.identifier, node); this.writeNode("wps:DataInputs", options.dataInputs, node); this.writeNode("wps:ResponseForm", options.responseForm, node); return node; }, "ResponseForm": function(responseForm) { var node = this.createElementNSPlus("wps:ResponseForm", {}); if (responseForm.rawDataOutput) { this.writeNode("wps:RawDataOutput", responseForm.rawDataOutput, node); } if (responseForm.responseDocument) { this.writeNode("wps:ResponseDocument", responseForm.responseDocument, node); } return node; }, "ResponseDocument": function(responseDocument) { var node = this.createElementNSPlus("wps:ResponseDocument", { attributes: { storeExecuteResponse: responseDocument.storeExecuteResponse, lineage: responseDocument.lineage, status: responseDocument.status } }); if (responseDocument.outputs) { for (var i = 0, len = responseDocument.outputs.length; i < len; i++) { this.writeNode("wps:Output", responseDocument.outputs[i], node); } } return node; }, "Output": function(output) { var node = this.createElementNSPlus("wps:Output", { attributes: { asReference: output.asReference, mimeType: output.mimeType, encoding: output.encoding, schema: output.schema } }); this.writeNode("ows:Identifier", output.identifier, node); this.writeNode("ows:Title", output.title, node); this.writeNode("ows:Abstract", output["abstract"], node); return node; }, "RawDataOutput": function(rawDataOutput) { var node = this.createElementNSPlus("wps:RawDataOutput", { attributes: { mimeType: rawDataOutput.mimeType, encoding: rawDataOutput.encoding, schema: rawDataOutput.schema } }); this.writeNode("ows:Identifier", rawDataOutput.identifier, node); return node; }, "DataInputs": function(dataInputs) { var node = this.createElementNSPlus("wps:DataInputs", {}); for (var i=0, ii=dataInputs.length; i<ii; ++i) { this.writeNode("wps:Input", dataInputs[i], node); } return node; }, "Input": function(input) { var node = this.createElementNSPlus("wps:Input", {}); this.writeNode("ows:Identifier", input.identifier, node); if (input.title) { this.writeNode("ows:Title", input.title, node); } if (input.data) { this.writeNode("wps:Data", input.data, node); } if (input.reference) { this.writeNode("wps:Reference", input.reference, node); } if (input.boundingBoxData) { this.writeNode("wps:BoundingBoxData", input.boundingBoxData, node); } return node; }, "Data": function(data) { var node = this.createElementNSPlus("wps:Data", {}); if (data.literalData) { this.writeNode("wps:LiteralData", data.literalData, node); } else if (data.complexData) { this.writeNode("wps:ComplexData", data.complexData, node); } else if (data.boundingBoxData) { this.writeNode("ows:BoundingBox", data.boundingBoxData, node); } return node; }, "LiteralData": function(literalData) { var node = this.createElementNSPlus("wps:LiteralData", { attributes: { uom: literalData.uom }, value: literalData.value }); return node; }, "ComplexData": function(complexData) { var node = this.createElementNSPlus("wps:ComplexData", { attributes: { mimeType: complexData.mimeType, encoding: complexData.encoding, schema: complexData.schema } }); var data = complexData.value; if (typeof data === "string") { node.appendChild( this.getXMLDoc().createCDATASection(complexData.value) ); } else { node.appendChild(data); } return node; }, "Reference": function(reference) { var node = this.createElementNSPlus("wps:Reference", { attributes: { mimeType: reference.mimeType, "xlink:href": reference.href, method: reference.method, encoding: reference.encoding, schema: reference.schema } }); if (reference.body) { this.writeNode("wps:Body", reference.body, node); } return node; }, "BoundingBoxData": function(node, obj) { this.writers['ows']['BoundingBox'].apply(this, [node, obj, "wps:BoundingBoxData"]); }, "Body": function(body) { var node = this.createElementNSPlus("wps:Body", {}); if (body.wcs) { this.writeNode("wcs:GetCoverage", body.wcs, node); } else if (body.wfs) { // OpenLayers.Format.WFST expects these to be on the // instance and not in the options this.featureType = body.wfs.featureType; this.version = body.wfs.version; this.writeNode("wfs:GetFeature", body.wfs, node); } else { this.writeNode("wps:Execute", body, node); } return node; } }, "wcs": OpenLayers.Format.WCSGetCoverage.prototype.writers.wcs, "wfs": OpenLayers.Format.WFST.v1_1_0.prototype.writers.wfs, "ogc": OpenLayers.Format.Filter.v1_1_0.prototype.writers.ogc, "ows": OpenLayers.Format.OWSCommon.v1_1_0.prototype.writers.ows }, /** * 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: { "wps": { "ExecuteResponse": function(node, obj) { obj.executeResponse = { lang: node.getAttribute("lang"), statusLocation: node.getAttribute("statusLocation"), serviceInstance: node.getAttribute("serviceInstance"), service: node.getAttribute("service") }; this.readChildNodes(node, obj.executeResponse); }, "Process":function(node,obj) { obj.process = {}; this.readChildNodes(node, obj.process); }, "Status":function(node,obj) { obj.status = { creationTime: node.getAttribute("creationTime") }; this.readChildNodes(node, obj.status); }, "ProcessSucceeded": function(node,obj) { obj.processSucceeded = true; }, "ProcessOutputs": function(node, processDescription) { processDescription.processOutputs = []; this.readChildNodes(node, processDescription.processOutputs); }, "Output": function(node, processOutputs) { var output = {}; this.readChildNodes(node, output); processOutputs.push(output); }, "Reference": function(node, output) { output.reference = { href: node.getAttribute("href"), mimeType: node.getAttribute("mimeType"), encoding: node.getAttribute("encoding"), schema: node.getAttribute("schema") }; }, "Data": function(node, output) { output.data = {}; this.readChildNodes(node, output); }, "LiteralData": function(node, output) { output.literalData = { dataType: node.getAttribute("dataType"), uom: node.getAttribute("uom"), value: this.getChildValue(node) }; }, "ComplexData": function(node, output) { output.complexData = { mimeType: node.getAttribute("mimeType"), schema: node.getAttribute("schema"), encoding: node.getAttribute("encoding"), value: "" }; // try to get *some* value, ignore the empty text values if (this.isSimpleContent(node)) { var child; for(child=node.firstChild; child; child=child.nextSibling) { switch(child.nodeType) { case 3: // text node case 4: // cdata section output.complexData.value += child.nodeValue; } } } else { for(child=node.firstChild; child; child=child.nextSibling) { if (child.nodeType == 1) { output.complexData.value = child; } } } }, "BoundingBox": function(node, output) { output.boundingBoxData = { dimensions: node.getAttribute("dimensions"), crs: node.getAttribute("crs") }; this.readChildNodes(node, output.boundingBoxData); } }, // TODO: we should add Exception parsing here "ows": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers["ows"] }, CLASS_NAME: "OpenLayers.Format.WPSExecute" }); /* ====================================================================== 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/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/WPSProcess.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/Geometry.js * @requires OpenLayers/Feature/Vector.js * @requires OpenLayers/Format/WKT.js * @requires OpenLayers/Format/GeoJSON.js * @requires OpenLayers/Format/WPSExecute.js * @requires OpenLayers/Request.js */ /** * Class: OpenLayers.WPSProcess * Representation of a WPS process. Usually instances of * <OpenLayers.WPSProcess> are created by calling 'getProcess' on an * <OpenLayers.WPSClient> instance. * * Currently <OpenLayers.WPSProcess> supports processes that have geometries * or features as output, using WKT or GeoJSON as output format. It also * supports chaining of processes by using the <output> method to create a * handle that is used as process input instead of a static value. */ OpenLayers.WPSProcess = OpenLayers.Class({ /** * Property: client * {<OpenLayers.WPSClient>} The client that manages this process. */ client: null, /** * Property: server * {String} Local client identifier for this process's server. */ server: null, /** * Property: identifier * {String} Process identifier known to the server. */ identifier: null, /** * Property: description * {Object} DescribeProcess response for this process. */ description: null, /** * APIProperty: localWPS * {String} Service endpoint for locally chained WPS processes. Default is * 'http://geoserver/wps'. */ localWPS: 'http://geoserver/wps', /** * Property: formats * {Object} OpenLayers.Format instances keyed by mimetype. */ formats: null, /** * Property: chained * {Integer} Number of chained processes for pending execute requests that * don't have a full configuration yet. */ chained: 0, /** * Property: executeCallbacks * {Array} Callbacks waiting to be executed until all chained processes * are configured; */ executeCallbacks: null, /** * Constructor: OpenLayers.WPSProcess * * Parameters: * options - {Object} Object whose properties will be set on the instance. * * Avaliable options: * client - {<OpenLayers.WPSClient>} Mandatory. Client that manages this * process. * server - {String} Mandatory. Local client identifier of this process's * server. * identifier - {String} Mandatory. Process identifier known to the server. */ initialize: function(options) { OpenLayers.Util.extend(this, options); this.executeCallbacks = []; this.formats = { 'application/wkt': new OpenLayers.Format.WKT(), 'application/json': new OpenLayers.Format.GeoJSON() }; }, /** * Method: describe * Makes the client issue a DescribeProcess request asynchronously. * * Parameters: * options - {Object} Configuration for the method call * * Available options: * callback - {Function} Callback to execute when the description is * available. Will be called with the parsed description as argument. * Optional. * scope - {Object} The scope in which the callback will be executed. * Default is the global object. */ describe: function(options) { options = options || {}; if (!this.description) { this.client.describeProcess(this.server, this.identifier, function(description) { if (!this.description) { this.parseDescription(description); } if (options.callback) { options.callback.call(options.scope, this.description); } }, this); } else if (options.callback) { var description = this.description; window.setTimeout(function() { options.callback.call(options.scope, description); }, 0); } }, /** * APIMethod: configure * Configure the process, but do not execute it. Use this for processes * that are chained as input of a different process by means of the * <output> method. * * Parameters: * options - {Object} * * Returns: * {<OpenLayers.WPSProcess>} this process. * * Available options: * inputs - {Object} The inputs for the process, keyed by input identifier. * For spatial data inputs, the value of an input is usually an * <OpenLayers.Geometry>, an <OpenLayers.Feature.Vector> or an array of * geometries or features. * callback - {Function} Callback to call when the configuration is * complete. Optional. * scope - {Object} Optional scope for the callback. */ configure: function(options) { this.describe({ callback: function() { var description = this.description, inputs = options.inputs, input, i, ii; for (i=0, ii=description.dataInputs.length; i<ii; ++i) { input = description.dataInputs[i]; this.setInputData(input, inputs[input.identifier]); } if (options.callback) { options.callback.call(options.scope); } }, scope: this }); return this; }, /** * APIMethod: execute * Configures and executes the process * * Parameters: * options - {Object} * * Available options: * inputs - {Object} The inputs for the process, keyed by input identifier. * For spatial data inputs, the value of an input is usually an * <OpenLayers.Geometry>, an <OpenLayers.Feature.Vector> or an array of * geometries or features. * output - {String} The identifier of the output to request and parse. * Optional. If not provided, the first output will be requested. * success - {Function} Callback to call when the process is complete. * This function is called with an outputs object as argument, which * will have a property with the identifier of the requested output * (or 'result' if output was not configured). For processes that * generate spatial output, the value will be an array of * <OpenLayers.Feature.Vector> instances. * scope - {Object} Optional scope for the success callback. */ execute: function(options) { this.configure({ inputs: options.inputs, callback: function() { var me = this; //TODO For now we only deal with a single output var outputIndex = this.getOutputIndex( me.description.processOutputs, options.output ); me.setResponseForm({outputIndex: outputIndex}); (function callback() { OpenLayers.Util.removeItem(me.executeCallbacks, callback); if (me.chained !== 0) { // need to wait until chained processes have a // description and configuration - see chainProcess me.executeCallbacks.push(callback); return; } // all chained processes are added as references now, so // let's proceed. OpenLayers.Request.POST({ url: me.client.servers[me.server].url, data: new OpenLayers.Format.WPSExecute().write(me.description), success: function(response) { var output = me.description.processOutputs[outputIndex]; var mimeType = me.findMimeType( output.complexOutput.supported.formats ); //TODO For now we assume a spatial output var features = me.formats[mimeType].read(response.responseText); if (features instanceof OpenLayers.Feature.Vector) { features = [features]; } if (options.success) { var outputs = {}; outputs[options.output || 'result'] = features; options.success.call(options.scope, outputs); } }, scope: me }); })(); }, scope: this }); }, /** * APIMethod: output * Chain an output of a configured process (see <configure>) as input to * another process. * * (code) * intersect = client.getProcess('opengeo', 'JTS:intersection'); * intersect.configure({ * // ... * }); * buffer = client.getProcess('opengeo', 'JTS:buffer'); * buffer.execute({ * inputs: { * geom: intersect.output('result'), // <-- here we're chaining * distance: 1 * }, * // ... * }); * (end) * * Parameters: * identifier - {String} Identifier of the output that we're chaining. If * not provided, the first output will be used. */ output: function(identifier) { return new OpenLayers.WPSProcess.ChainLink({ process: this, output: identifier }); }, /** * Method: parseDescription * Parses the DescribeProcess response * * Parameters: * description - {Object} */ parseDescription: function(description) { var server = this.client.servers[this.server]; this.description = new OpenLayers.Format.WPSDescribeProcess() .read(server.processDescription[this.identifier]) .processDescriptions[this.identifier]; }, /** * Method: setInputData * Sets the data for a single input * * Parameters: * input - {Object} An entry from the dataInputs array of the process * description. * data - {Mixed} For spatial data inputs, this is usually an * <OpenLayers.Geometry>, an <OpenLayers.Feature.Vector> or an array of * geometries or features. */ setInputData: function(input, data) { // clear any previous data delete input.data; delete input.reference; if (data instanceof OpenLayers.WPSProcess.ChainLink) { ++this.chained; input.reference = { method: 'POST', href: data.process.server === this.server ? this.localWPS : this.client.servers[data.process.server].url }; data.process.describe({ callback: function() { --this.chained; this.chainProcess(input, data); }, scope: this }); } else { input.data = {}; var complexData = input.complexData; if (complexData) { var format = this.findMimeType(complexData.supported.formats); input.data.complexData = { mimeType: format, value: this.formats[format].write(this.toFeatures(data)) }; } else { input.data.literalData = { value: data }; } } }, /** * Method: setResponseForm * Sets the responseForm property of the <execute> payload. * * Parameters: * options - {Object} See below. * * Available options: * outputIndex - {Integer} The index of the output to use. Optional. * supportedFormats - {Object} Object with supported mime types as key, * and true as value for supported types. Optional. */ setResponseForm: function(options) { options = options || {}; var output = this.description.processOutputs[options.outputIndex || 0]; this.description.responseForm = { rawDataOutput: { identifier: output.identifier, mimeType: this.findMimeType(output.complexOutput.supported.formats, options.supportedFormats) } }; }, /** * Method: getOutputIndex * Gets the index of a processOutput by its identifier * * Parameters: * outputs - {Array} The processOutputs array to look at * identifier - {String} The identifier of the output * * Returns * {Integer} The index of the processOutput with the provided identifier * in the outputs array. */ getOutputIndex: function(outputs, identifier) { var output; if (identifier) { for (var i=outputs.length-1; i>=0; --i) { if (outputs[i].identifier === identifier) { output = i; break; } } } else { output = 0; } return output; }, /** * Method: chainProcess * Sets a fully configured chained process as input for this process. * * Parameters: * input - {Object} The dataInput that the chained process provides. * chainLink - {<OpenLayers.WPSProcess.ChainLink>} The process to chain. */ chainProcess: function(input, chainLink) { var output = this.getOutputIndex( chainLink.process.description.processOutputs, chainLink.output ); input.reference.mimeType = this.findMimeType( input.complexData.supported.formats, chainLink.process.description.processOutputs[output].complexOutput.supported.formats ); var formats = {}; formats[input.reference.mimeType] = true; chainLink.process.setResponseForm({ outputIndex: output, supportedFormats: formats }); input.reference.body = chainLink.process.description; while (this.executeCallbacks.length > 0) { this.executeCallbacks[0](); } }, /** * Method: toFeatures * Converts spatial input into features so it can be processed by * <OpenLayers.Format> instances. * * Parameters: * source - {Mixed} An <OpenLayers.Geometry>, an * <OpenLayers.Feature.Vector>, or an array of geometries or features * * Returns: * {Array(<OpenLayers.Feature.Vector>)} */ toFeatures: function(source) { var isArray = OpenLayers.Util.isArray(source); if (!isArray) { source = [source]; } var target = new Array(source.length), current; for (var i=0, ii=source.length; i<ii; ++i) { current = source[i]; target[i] = current instanceof OpenLayers.Feature.Vector ? current : new OpenLayers.Feature.Vector(current); } return isArray ? target : target[0]; }, /** * Method: findMimeType * Finds a supported mime type. * * Parameters: * sourceFormats - {Object} An object literal with mime types as key and * true as value for supported formats. * targetFormats - {Object} Like <sourceFormats>, but optional to check for * supported mime types on a different target than this process. * Default is to check against this process's supported formats. * * Returns: * {String} A supported mime type. */ findMimeType: function(sourceFormats, targetFormats) { targetFormats = targetFormats || this.formats; for (var f in sourceFormats) { if (f in targetFormats) { return f; } } }, CLASS_NAME: "OpenLayers.WPSProcess" }); /** * Class: OpenLayers.WPSProcess.ChainLink * Type for chaining processes. */ OpenLayers.WPSProcess.ChainLink = OpenLayers.Class({ /** * Property: process * {<OpenLayers.WPSProcess>} The process to chain */ process: null, /** * Property: output * {String} The output identifier of the output we are going to use as * input for another process. */ output: null, /** * Constructor: OpenLayers.WPSProcess.ChainLink * * Parameters: * options - {Object} Properties to set on the instance. */ initialize: function(options) { OpenLayers.Util.extend(this, options); }, CLASS_NAME: "OpenLayers.WPSProcess.ChainLink" }); /* ====================================================================== OpenLayers/Format/WPSDescribeProcess.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/OWSCommon/v1_1_0.js */ /** * Class: OpenLayers.Format.WPSDescribeProcess * Read WPS DescribeProcess responses. * * Inherits from: * - <OpenLayers.Format.XML> */ OpenLayers.Format.WPSDescribeProcess = OpenLayers.Class( OpenLayers.Format.XML, { /** * Constant: VERSION * {String} 1.0.0 */ VERSION: "1.0.0", /** * Property: namespaces * {Object} Mapping of namespace aliases to namespace URIs. */ namespaces: { wps: "http://www.opengis.net/wps/1.0.0", ows: "http://www.opengis.net/ows/1.1", xsi: "http://www.w3.org/2001/XMLSchema-instance" }, /** * Property: schemaLocation * {String} Schema location */ schemaLocation: "http://www.opengis.net/wps/1.0.0 http://schemas.opengis.net/wps/1.0.0/wpsAll.xsd", /** * Property: defaultPrefix */ defaultPrefix: "wps", /** * Property: regExes * Compiled regular expressions for manipulating strings. */ regExes: { trimSpace: (/^\s*|\s*$/g), removeSpace: (/\s*/g), splitSpace: (/\s+/), trimComma: (/\s*,\s*/g) }, /** * Constructor: OpenLayers.Format.WPSDescribeProcess * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ /** * APIMethod: read * Parse a WPS DescribeProcess and return an object with its information. * * Parameters: * data - {String} or {DOMElement} data to read/parse. * * Returns: * {Object} */ 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 info = {}; this.readNode(data, info); return info; }, /** * 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: { "wps": { "ProcessDescriptions": function(node, obj) { obj.processDescriptions = {}; this.readChildNodes(node, obj.processDescriptions); }, "ProcessDescription": function(node, processDescriptions) { var processVersion = this.getAttributeNS(node, this.namespaces.wps, "processVersion"); var processDescription = { processVersion: processVersion, statusSupported: (node.getAttribute("statusSupported") === "true"), storeSupported: (node.getAttribute("storeSupported") === "true") }; this.readChildNodes(node, processDescription); processDescriptions[processDescription.identifier] = processDescription; }, "DataInputs": function(node, processDescription) { processDescription.dataInputs = []; this.readChildNodes(node, processDescription.dataInputs); }, "ProcessOutputs": function(node, processDescription) { processDescription.processOutputs = []; this.readChildNodes(node, processDescription.processOutputs); }, "Output": function(node, processOutputs) { var output = {}; this.readChildNodes(node, output); processOutputs.push(output); }, "ComplexOutput": function(node, output) { output.complexOutput = {}; this.readChildNodes(node, output.complexOutput); }, "LiteralOutput": function(node, output) { output.literalOutput = {}; this.readChildNodes(node, output.literalOutput); }, "Input": function(node, dataInputs) { var input = { maxOccurs: parseInt(node.getAttribute("maxOccurs")), minOccurs: parseInt(node.getAttribute("minOccurs")) }; this.readChildNodes(node, input); dataInputs.push(input); }, "BoundingBoxData": function(node, input) { input.boundingBoxData = {}; this.readChildNodes(node, input.boundingBoxData); }, "CRS": function(node, obj) { if (!obj.CRSs) { obj.CRSs = {}; } obj.CRSs[this.getChildValue(node)] = true; }, "LiteralData": function(node, input) { input.literalData = {}; this.readChildNodes(node, input.literalData); }, "ComplexData": function(node, input) { input.complexData = {}; this.readChildNodes(node, input.complexData); }, "Default": function(node, complexData) { complexData["default"] = {}; this.readChildNodes(node, complexData["default"]); }, "Supported": function(node, complexData) { complexData["supported"] = {}; this.readChildNodes(node, complexData["supported"]); }, "Format": function(node, obj) { var format = {}; this.readChildNodes(node, format); if (!obj.formats) { obj.formats = {}; } obj.formats[format.mimeType] = true; }, "MimeType": function(node, format) { format.mimeType = this.getChildValue(node); } }, "ows": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers["ows"] }, CLASS_NAME: "OpenLayers.Format.WPSDescribeProcess" }); /* ====================================================================== OpenLayers/WPSClient.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/Events.js * @requires OpenLayers/WPSProcess.js * @requires OpenLayers/Format/WPSDescribeProcess.js * @requires OpenLayers/Request.js */ /** * Class: OpenLayers.WPSClient * High level API for interaction with Web Processing Services (WPS). * An <OpenLayers.WPSClient> instance is used to create <OpenLayers.WPSProcess> * instances for servers known to the WPSClient. The WPSClient also caches * DescribeProcess responses to reduce the number of requests sent to servers * when processes are created. */ OpenLayers.WPSClient = OpenLayers.Class({ /** * Property: servers * {Object} Service metadata, keyed by a local identifier. * * Properties: * url - {String} the url of the server * version - {String} WPS version of the server * processDescription - {Object} Cache of raw DescribeProcess * responses, keyed by process identifier. */ servers: null, /** * Property: version * {String} The default WPS version to use if none is configured. Default * is '1.0.0'. */ version: '1.0.0', /** * Property: lazy * {Boolean} Should the DescribeProcess be deferred until a process is * fully configured? Default is false. */ lazy: false, /** * Property: events * {<OpenLayers.Events>} * * Supported event types: * describeprocess - Fires when the process description is available. * Listeners receive an object with a 'raw' property holding the raw * DescribeProcess response, and an 'identifier' property holding the * process identifier of the described process. */ events: null, /** * Constructor: OpenLayers.WPSClient * * Parameters: * options - {Object} Object whose properties will be set on the instance. * * Avaliable options: * servers - {Object} Mandatory. Service metadata, keyed by a local * identifier. Can either be a string with the service url or an * object literal with additional metadata: * * (code) * servers: { * local: '/geoserver/wps' * }, { * opengeo: { * url: 'http://demo.opengeo.org/geoserver/wps', * version: '1.0.0' * } * } * (end) * * lazy - {Boolean} Optional. Set to true if DescribeProcess should not be * requested until a process is fully configured. Default is false. */ initialize: function(options) { OpenLayers.Util.extend(this, options); this.events = new OpenLayers.Events(this); this.servers = {}; for (var s in options.servers) { this.servers[s] = typeof options.servers[s] == 'string' ? { url: options.servers[s], version: this.version, processDescription: {} } : options.servers[s]; } }, /** * APIMethod: execute * Shortcut to execute a process with a single function call. This is * equivalent to using <getProcess> and then calling execute on the * process. * * Parameters: * options - {Object} Options for the execute operation. * * Available options: * server - {String} Mandatory. One of the local identifiers of the * configured servers. * process - {String} Mandatory. A process identifier known to the * server. * inputs - {Object} The inputs for the process, keyed by input identifier. * For spatial data inputs, the value of an input is usually an * <OpenLayers.Geometry>, an <OpenLayers.Feature.Vector> or an array of * geometries or features. * output - {String} The identifier of an output to parse. Optional. If not * provided, the first output will be parsed. * success - {Function} Callback to call when the process is complete. * This function is called with an outputs object as argument, which * will have a property with the identifier of the requested output * (e.g. 'result'). For processes that generate spatial output, the * value will either be a single <OpenLayers.Feature.Vector> or an * array of features. * scope - {Object} Optional scope for the success callback. */ execute: function(options) { var process = this.getProcess(options.server, options.process); process.execute({ inputs: options.inputs, success: options.success, scope: options.scope }); }, /** * APIMethod: getProcess * Creates an <OpenLayers.WPSProcess>. * * Parameters: * serverID - {String} Local identifier from the servers that this instance * was constructed with. * processID - {String} Process identifier known to the server. * * Returns: * {<OpenLayers.WPSProcess>} */ getProcess: function(serverID, processID) { var process = new OpenLayers.WPSProcess({ client: this, server: serverID, identifier: processID }); if (!this.lazy) { process.describe(); } return process; }, /** * Method: describeProcess * * Parameters: * serverID - {String} Identifier of the server * processID - {String} Identifier of the requested process * callback - {Function} Callback to call when the description is available * scope - {Object} Optional execution scope for the callback function */ describeProcess: function(serverID, processID, callback, scope) { var server = this.servers[serverID]; if (!server.processDescription[processID]) { if (!(processID in server.processDescription)) { // set to null so we know a describeFeature request is pending server.processDescription[processID] = null; OpenLayers.Request.GET({ url: server.url, params: { SERVICE: 'WPS', VERSION: server.version, REQUEST: 'DescribeProcess', IDENTIFIER: processID }, success: function(response) { server.processDescription[processID] = response.responseText; this.events.triggerEvent('describeprocess', { identifier: processID, raw: response.responseText }); }, scope: this }); } else { // pending request this.events.register('describeprocess', this, function describe(evt) { if (evt.identifier === processID) { this.events.unregister('describeprocess', this, describe); callback.call(scope, evt.raw); } }); } } else { window.setTimeout(function() { callback.call(scope, server.processDescription[processID]); }, 0); } }, /** * Method: destroy */ destroy: function() { this.events.destroy(); this.events = null; this.servers = null; }, CLASS_NAME: 'OpenLayers.WPSClient' }); /* ====================================================================== 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/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/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/Rule.js ====================================================================== */ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for * full list of contributors). Published under the 2-clause BSD license. * See license.txt in the OpenLayers distribution or repository for the * full text of the license. */ /** * @requires OpenLayers/BaseTypes/Class.js * @requires OpenLayers/Util.js * @requires OpenLayers/Style.js */ /** * Class: OpenLayers.Rule * This class represents an SLD Rule, as being used for rule-based SLD styling. */ OpenLayers.Rule = OpenLayers.Class({ /** * Property: id * {String} A unique id for this session. */ id: null, /** * APIProperty: name * {String} name of this rule */ name: null, /** * Property: title * {String} Title of this rule (set if included in SLD) */ title: null, /** * Property: description * {String} Description of this rule (set if abstract is included in SLD) */ description: null, /** * Property: context * {Object} An optional object with properties that the rule should be * evaluated against. If no context is specified, feature.attributes will * be used. */ context: null, /** * Property: filter * {<OpenLayers.Filter>} Optional filter for the rule. */ filter: null, /** * Property: elseFilter * {Boolean} Determines whether this rule is only to be applied only if * no other rules match (ElseFilter according to the SLD specification). * Default is false. For instances of OpenLayers.Rule, if elseFilter is * false, the rule will always apply. For subclasses, the else property is * ignored. */ elseFilter: false, /** * Property: symbolizer * {Object} Symbolizer or hash of symbolizers for this rule. If hash of * symbolizers, keys are one or more of ["Point", "Line", "Polygon"]. The * latter if useful if it is required to style e.g. vertices of a line * with a point symbolizer. Note, however, that this is not implemented * yet in OpenLayers, but it is the way how symbolizers are defined in * SLD. */ symbolizer: null, /** * Property: symbolizers * {Array} Collection of symbolizers associated with this rule. If * provided at construction, the symbolizers array has precedence * over the deprecated symbolizer property. Note that multiple * symbolizers are not currently supported by the vector renderers. * Rules with multiple symbolizers are currently only useful for * maintaining elements in an SLD document. */ symbolizers: null, /** * APIProperty: minScaleDenominator * {Number} or {String} minimum scale at which to draw the feature. * In the case of a String, this can be a combination of text and * propertyNames in the form "literal ${propertyName}" */ minScaleDenominator: null, /** * APIProperty: maxScaleDenominator * {Number} or {String} maximum scale at which to draw the feature. * In the case of a String, this can be a combination of text and * propertyNames in the form "literal ${propertyName}" */ maxScaleDenominator: null, /** * Constructor: OpenLayers.Rule * Creates a Rule. * * Parameters: * options - {Object} An optional object with properties to set on the * rule * * Returns: * {<OpenLayers.Rule>} */ initialize: function(options) { this.symbolizer = {}; OpenLayers.Util.extend(this, options); if (this.symbolizers) { delete this.symbolizer; } this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_"); }, /** * APIMethod: destroy * nullify references to prevent circular references and memory leaks */ destroy: function() { for (var i in this.symbolizer) { this.symbolizer[i] = null; } this.symbolizer = null; delete this.symbolizers; }, /** * APIMethod: evaluate * evaluates this rule for a specific feature * * Parameters: * feature - {<OpenLayers.Feature>} feature to apply the rule to. * * Returns: * {Boolean} true if the rule applies, false if it does not. * This rule is the default rule and always returns true. */ evaluate: function(feature) { var context = this.getContext(feature); var applies = true; if (this.minScaleDenominator || this.maxScaleDenominator) { var scale = feature.layer.map.getScale(); } // check if within minScale/maxScale bounds if (this.minScaleDenominator) { applies = scale >= OpenLayers.Style.createLiteral( this.minScaleDenominator, context); } if (applies && this.maxScaleDenominator) { applies = scale < OpenLayers.Style.createLiteral( this.maxScaleDenominator, context); } // check if optional filter applies if(applies && this.filter) { // feature id filters get the feature, others get the context if(this.filter.CLASS_NAME == "OpenLayers.Filter.FeatureId") { applies = this.filter.evaluate(feature); } else { applies = this.filter.evaluate(context); } } return applies; }, /** * Method: getContext * Gets the context for evaluating this rule * * Paramters: * feature - {<OpenLayers.Feature>} feature to take the context from if * none is specified. */ getContext: function(feature) { var context = this.context; if (!context) { context = feature.attributes || feature.data; } if (typeof this.context == "function") { context = this.context(feature); } return context; }, /** * APIMethod: clone * Clones this rule. * * Returns: * {<OpenLayers.Rule>} Clone of this rule. */ clone: function() { var options = OpenLayers.Util.extend({}, this); if (this.symbolizers) { // clone symbolizers var len = this.symbolizers.length; options.symbolizers = new Array(len); for (var i=0; i<len; ++i) { options.symbolizers[i] = this.symbolizers[i].clone(); } } else { // clone symbolizer options.symbolizer = {}; var value, type; for(var key in this.symbolizer) { value = this.symbolizer[key]; type = typeof value; if(type === "object") { options.symbolizer[key] = OpenLayers.Util.extend({}, value); } else if(type === "string") { options.symbolizer[key] = value; } } } // clone filter options.filter = this.filter && this.filter.clone(); // clone context options.context = this.context && OpenLayers.Util.extend({}, this.context); return new OpenLayers.Rule(options); }, CLASS_NAME: "OpenLayers.Rule" }); /* ====================================================================== OpenLayers/Spherical.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: Spherical * The OpenLayers.Spherical namespace includes utility functions for * calculations on the basis of a spherical earth (ignoring ellipsoidal * effects), which is accurate enough for most purposes. * * Relevant links: * * http://www.movable-type.co.uk/scripts/latlong.html * * http://code.google.com/apis/maps/documentation/javascript/reference.html#spherical */ OpenLayers.Spherical = OpenLayers.Spherical || {}; OpenLayers.Spherical.DEFAULT_RADIUS = 6378137; /** * APIFunction: computeDistanceBetween * Computes the distance between two LonLats. * * Parameters: * from - {<OpenLayers.LonLat>} or {Object} Starting point. A LonLat or * a JavaScript literal with lon lat properties. * to - {<OpenLayers.LonLat>} or {Object} Ending point. A LonLat or a * JavaScript literal with lon lat properties. * radius - {Float} The radius. Optional. Defaults to 6378137 meters. * * Returns: * {Float} The distance in meters. */ OpenLayers.Spherical.computeDistanceBetween = function(from, to, radius) { var R = radius || OpenLayers.Spherical.DEFAULT_RADIUS; var sinHalfDeltaLon = Math.sin(Math.PI * (to.lon - from.lon) / 360); var sinHalfDeltaLat = Math.sin(Math.PI * (to.lat - from.lat) / 360); var a = sinHalfDeltaLat * sinHalfDeltaLat + sinHalfDeltaLon * sinHalfDeltaLon * Math.cos(Math.PI * from.lat / 180) * Math.cos(Math.PI * to.lat / 180); return 2 * R * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); }; /** * APIFunction: computeHeading * Computes the heading from one LonLat to another LonLat. * * Parameters: * from - {<OpenLayers.LonLat>} or {Object} Starting point. A LonLat or * a JavaScript literal with lon lat properties. * to - {<OpenLayers.LonLat>} or {Object} Ending point. A LonLat or a * JavaScript literal with lon lat properties. * * Returns: * {Float} The heading in degrees. */ OpenLayers.Spherical.computeHeading = function(from, to) { var y = Math.sin(Math.PI * (from.lon - to.lon) / 180) * Math.cos(Math.PI * to.lat / 180); var x = Math.cos(Math.PI * from.lat / 180) * Math.sin(Math.PI * to.lat / 180) - Math.sin(Math.PI * from.lat / 180) * Math.cos(Math.PI * to.lat / 180) * Math.cos(Math.PI * (from.lon - to.lon) / 180); return 180 * Math.atan2(y, x) / Math.PI; }; /* ====================================================================== 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/Icon.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.Icon * * The icon represents a graphical icon on the screen. Typically used in * conjunction with a <OpenLayers.Marker> to represent markers on a screen. * * An icon has a url, size and position. It also contains an offset which * allows the center point to be represented correctly. This can be * provided either as a fixed offset or a function provided to calculate * the desired offset. * */ OpenLayers.Icon = OpenLayers.Class({ /** * Property: url * {String} image url */ url: null, /** * Property: size * {<OpenLayers.Size>|Object} An OpenLayers.Size or * an object with a 'w' and 'h' properties. */ size: null, /** * Property: offset * {<OpenLayers.Pixel>|Object} distance in pixels to offset the * image when being rendered. An OpenLayers.Pixel or an object * with a 'x' and 'y' properties. */ offset: null, /** * Property: calculateOffset * {Function} Function to calculate the offset (based on the size) */ calculateOffset: null, /** * Property: imageDiv * {DOMElement} */ imageDiv: null, /** * Property: px * {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or an object * with a 'x' and 'y' properties. */ px: null, /** * Constructor: OpenLayers.Icon * Creates an icon, which is an image tag in a div. * * url - {String} * size - {<OpenLayers.Size>|Object} An OpenLayers.Size or an * object with a 'w' and 'h' * properties. * offset - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or an * object with a 'x' and 'y' * properties. * calculateOffset - {Function} */ initialize: function(url, size, offset, calculateOffset) { this.url = url; this.size = size || {w: 20, h: 20}; this.offset = offset || {x: -(this.size.w/2), y: -(this.size.h/2)}; this.calculateOffset = calculateOffset; var id = OpenLayers.Util.createUniqueID("OL_Icon_"); this.imageDiv = OpenLayers.Util.createAlphaImageDiv(id); }, /** * Method: destroy * Nullify references and remove event listeners to prevent circular * references and memory leaks */ destroy: function() { // erase any drawn elements this.erase(); OpenLayers.Event.stopObservingElement(this.imageDiv.firstChild); this.imageDiv.innerHTML = ""; this.imageDiv = null; }, /** * Method: clone * * Returns: * {<OpenLayers.Icon>} A fresh copy of the icon. */ clone: function() { return new OpenLayers.Icon(this.url, this.size, this.offset, this.calculateOffset); }, /** * Method: setSize * * Parameters: * size - {<OpenLayers.Size>|Object} An OpenLayers.Size or * an object with a 'w' and 'h' properties. */ setSize: function(size) { if (size != null) { this.size = size; } this.draw(); }, /** * Method: setUrl * * Parameters: * url - {String} */ setUrl: function(url) { if (url != null) { this.url = url; } this.draw(); }, /** * Method: draw * Move the div to the given pixel. * * Parameters: * px - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or an * object with a 'x' and 'y' properties. * * Returns: * {DOMElement} A new DOM Image of this icon set at the location passed-in */ draw: function(px) { OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, null, this.size, this.url, "absolute"); this.moveTo(px); return this.imageDiv; }, /** * Method: erase * Erase the underlying image element. */ erase: function() { if (this.imageDiv != null && this.imageDiv.parentNode != null) { OpenLayers.Element.remove(this.imageDiv); } }, /** * Method: setOpacity * Change the icon's opacity * * Parameters: * opacity - {float} */ setOpacity: function(opacity) { OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, null, null, null, null, null, null, opacity); }, /** * Method: moveTo * move icon to passed in px. * * Parameters: * px - {<OpenLayers.Pixel>|Object} the pixel position to move to. * An OpenLayers.Pixel or an object with a 'x' and 'y' properties. */ moveTo: function (px) { //if no px passed in, use stored location if (px != null) { this.px = px; } if (this.imageDiv != null) { if (this.px == null) { this.display(false); } else { if (this.calculateOffset) { this.offset = this.calculateOffset(this.size); } OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, { x: this.px.x + this.offset.x, y: this.px.y + this.offset.y }); } } }, /** * Method: display * Hide or show the icon * * Parameters: * display - {Boolean} */ display: function(display) { this.imageDiv.style.display = (display) ? "" : "none"; }, /** * APIMethod: isDrawn * * Returns: * {Boolean} Whether or not the icon is drawn. */ isDrawn: function() { // nodeType 11 for ie, whose nodes *always* have a parentNode // (of type document fragment) var isDrawn = (this.imageDiv && this.imageDiv.parentNode && (this.imageDiv.parentNode.nodeType != 11)); return isDrawn; }, CLASS_NAME: "OpenLayers.Icon" }); /* ====================================================================== 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/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/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/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; } }); /* ====================================================================== OpenLayers/Symbolizer.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.Symbolizer * Base class representing a symbolizer used for feature rendering. */ OpenLayers.Symbolizer = OpenLayers.Class({ /** * APIProperty: zIndex * {Number} The zIndex determines the rendering order for a symbolizer. * Symbolizers with larger zIndex values are rendered over symbolizers * with smaller zIndex values. Default is 0. */ zIndex: 0, /** * Constructor: OpenLayers.Symbolizer * Instances of this class are not useful. See one of the subclasses. * * Parameters: * config - {Object} An object containing properties to be set on the * symbolizer. Any documented symbolizer property can be set at * construction. * * Returns: * A new symbolizer. */ initialize: function(config) { OpenLayers.Util.extend(this, config); }, /** * APIMethod: clone * Create a copy of this symbolizer. * * Returns a symbolizer of the same type with the same properties. */ clone: function() { var Type = eval(this.CLASS_NAME); return new Type(OpenLayers.Util.extend({}, this)); }, CLASS_NAME: "OpenLayers.Symbolizer" }); /* ====================================================================== OpenLayers/Popup.js ====================================================================== */ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for * full list of contributors). Published under the 2-clause BSD license. * See license.txt in the OpenLayers distribution or repository for the * full text of the license. */ /** * @requires OpenLayers/BaseTypes/Class.js */ /** * Class: OpenLayers.Popup * A popup is a small div that can opened and closed on the map. * Typically opened in response to clicking on a marker. * See <OpenLayers.Marker>. Popup's don't require their own * layer and are added the the map using the <OpenLayers.Map.addPopup> * method. * * Example: * (code) * popup = new OpenLayers.Popup("chicken", * new OpenLayers.LonLat(5,40), * new OpenLayers.Size(200,200), * "example popup", * true); * * map.addPopup(popup); * (end) */ OpenLayers.Popup = OpenLayers.Class({ /** * Property: events * {<OpenLayers.Events>} custom event manager */ events: null, /** Property: id * {String} the unique identifier assigned to this popup. */ id: "", /** * Property: lonlat * {<OpenLayers.LonLat>} the position of this popup on the map */ lonlat: null, /** * Property: div * {DOMElement} the div that contains this popup. */ div: null, /** * Property: contentSize * {<OpenLayers.Size>} the width and height of the content. */ contentSize: null, /** * Property: size * {<OpenLayers.Size>} the width and height of the popup. */ size: null, /** * Property: contentHTML * {String} An HTML string for this popup to display. */ contentHTML: null, /** * Property: backgroundColor * {String} the background color used by the popup. */ backgroundColor: "", /** * Property: opacity * {float} the opacity of this popup (between 0.0 and 1.0) */ opacity: "", /** * Property: border * {String} the border size of the popup. (eg 2px) */ border: "", /** * Property: contentDiv * {DOMElement} a reference to the element that holds the content of * the div. */ contentDiv: null, /** * Property: groupDiv * {DOMElement} First and only child of 'div'. The group Div contains the * 'contentDiv' and the 'closeDiv'. */ groupDiv: null, /** * Property: closeDiv * {DOMElement} the optional closer image */ closeDiv: null, /** * APIProperty: autoSize * {Boolean} Resize the popup to auto-fit the contents. * Default is false. */ autoSize: false, /** * APIProperty: minSize * {<OpenLayers.Size>} Minimum size allowed for the popup's contents. */ minSize: null, /** * APIProperty: maxSize * {<OpenLayers.Size>} Maximum size allowed for the popup's contents. */ maxSize: null, /** * Property: displayClass * {String} The CSS class of the popup. */ displayClass: "olPopup", /** * Property: contentDisplayClass * {String} The CSS class of the popup content div. */ contentDisplayClass: "olPopupContent", /** * Property: padding * {int or <OpenLayers.Bounds>} An extra opportunity to specify internal * padding of the content div inside the popup. This was originally * confused with the css padding as specified in style.css's * 'olPopupContent' class. We would like to get rid of this altogether, * except that it does come in handy for the framed and anchoredbubble * popups, who need to maintain yet another barrier between their * content and the outer border of the popup itself. * * Note that in order to not break API, we must continue to support * this property being set as an integer. Really, though, we'd like to * have this specified as a Bounds object so that user can specify * distinct left, top, right, bottom paddings. With the 3.0 release * we can make this only a bounds. */ padding: 0, /** * Property: disableFirefoxOverflowHack * {Boolean} The hack for overflow in Firefox causes all elements * to be re-drawn, which causes Flash elements to be * re-initialized, which is troublesome. * With this property the hack can be disabled. */ disableFirefoxOverflowHack: false, /** * Method: fixPadding * To be removed in 3.0, this function merely helps us to deal with the * case where the user may have set an integer value for padding, * instead of an <OpenLayers.Bounds> object. */ fixPadding: function() { if (typeof this.padding == "number") { this.padding = new OpenLayers.Bounds( this.padding, this.padding, this.padding, this.padding ); } }, /** * APIProperty: panMapIfOutOfView * {Boolean} When drawn, pan map such that the entire popup is visible in * the current viewport (if necessary). * Default is false. */ panMapIfOutOfView: false, /** * APIProperty: keepInMap * {Boolean} If panMapIfOutOfView is false, and this property is true, * contrain the popup such that it always fits in the available map * space. By default, this is not set on the base class. If you are * creating popups that are near map edges and not allowing pannning, * and especially if you have a popup which has a * fixedRelativePosition, setting this to false may be a smart thing to * do. Subclasses may want to override this setting. * * Default is false. */ keepInMap: false, /** * APIProperty: closeOnMove * {Boolean} When map pans, close the popup. * Default is false. */ closeOnMove: false, /** * Property: map * {<OpenLayers.Map>} this gets set in Map.js when the popup is added to the map */ map: null, /** * Constructor: OpenLayers.Popup * Create a popup. * * Parameters: * id - {String} a unqiue identifier for this popup. If null is passed * an identifier will be automatically generated. * lonlat - {<OpenLayers.LonLat>} The position on the map the popup will * be shown. * contentSize - {<OpenLayers.Size>} The size of the content. * contentHTML - {String} An HTML string to display inside the * popup. * closeBox - {Boolean} Whether to display a close box inside * the popup. * closeBoxCallback - {Function} Function to be called on closeBox click. */ initialize:function(id, lonlat, contentSize, contentHTML, closeBox, closeBoxCallback) { if (id == null) { id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_"); } this.id = id; this.lonlat = lonlat; this.contentSize = (contentSize != null) ? contentSize : new OpenLayers.Size( OpenLayers.Popup.WIDTH, OpenLayers.Popup.HEIGHT); if (contentHTML != null) { this.contentHTML = contentHTML; } this.backgroundColor = OpenLayers.Popup.COLOR; this.opacity = OpenLayers.Popup.OPACITY; this.border = OpenLayers.Popup.BORDER; this.div = OpenLayers.Util.createDiv(this.id, null, null, null, null, null, "hidden"); this.div.className = this.displayClass; var groupDivId = this.id + "_GroupDiv"; this.groupDiv = OpenLayers.Util.createDiv(groupDivId, null, null, null, "relative", null, "hidden"); var id = this.div.id + "_contentDiv"; this.contentDiv = OpenLayers.Util.createDiv(id, null, this.contentSize.clone(), null, "relative"); this.contentDiv.className = this.contentDisplayClass; this.groupDiv.appendChild(this.contentDiv); this.div.appendChild(this.groupDiv); if (closeBox) { this.addCloseBox(closeBoxCallback); } this.registerEvents(); }, /** * Method: destroy * nullify references to prevent circular references and memory leaks */ destroy: function() { this.id = null; this.lonlat = null; this.size = null; this.contentHTML = null; this.backgroundColor = null; this.opacity = null; this.border = null; if (this.closeOnMove && this.map) { this.map.events.unregister("movestart", this, this.hide); } this.events.destroy(); this.events = null; if (this.closeDiv) { OpenLayers.Event.stopObservingElement(this.closeDiv); this.groupDiv.removeChild(this.closeDiv); } this.closeDiv = null; this.div.removeChild(this.groupDiv); this.groupDiv = null; if (this.map != null) { this.map.removePopup(this); } this.map = null; this.div = null; this.autoSize = null; this.minSize = null; this.maxSize = null; this.padding = null; this.panMapIfOutOfView = null; }, /** * Method: draw * Constructs the elements that make up the popup. * * Parameters: * px - {<OpenLayers.Pixel>} the position the popup in pixels. * * Returns: * {DOMElement} Reference to a div that contains the drawn popup */ draw: function(px) { if (px == null) { if ((this.lonlat != null) && (this.map != null)) { px = this.map.getLayerPxFromLonLat(this.lonlat); } } // this assumes that this.map already exists, which is okay because // this.draw is only called once the popup has been added to the map. if (this.closeOnMove) { this.map.events.register("movestart", this, this.hide); } //listen to movestart, moveend to disable overflow (FF bug) if (!this.disableFirefoxOverflowHack && OpenLayers.BROWSER_NAME == 'firefox') { this.map.events.register("movestart", this, function() { var style = document.defaultView.getComputedStyle( this.contentDiv, null ); var currentOverflow = style.getPropertyValue("overflow"); if (currentOverflow != "hidden") { this.contentDiv._oldOverflow = currentOverflow; this.contentDiv.style.overflow = "hidden"; } }); this.map.events.register("moveend", this, function() { var oldOverflow = this.contentDiv._oldOverflow; if (oldOverflow) { this.contentDiv.style.overflow = oldOverflow; this.contentDiv._oldOverflow = null; } }); } this.moveTo(px); if (!this.autoSize && !this.size) { this.setSize(this.contentSize); } this.setBackgroundColor(); this.setOpacity(); this.setBorder(); this.setContentHTML(); if (this.panMapIfOutOfView) { this.panIntoView(); } return this.div; }, /** * Method: updatePosition * if the popup has a lonlat and its map members set, * then have it move itself to its proper position */ updatePosition: function() { if ((this.lonlat) && (this.map)) { var px = this.map.getLayerPxFromLonLat(this.lonlat); if (px) { this.moveTo(px); } } }, /** * Method: moveTo * * Parameters: * px - {<OpenLayers.Pixel>} the top and left position of the popup div. */ moveTo: function(px) { if ((px != null) && (this.div != null)) { this.div.style.left = px.x + "px"; this.div.style.top = px.y + "px"; } }, /** * Method: visible * * Returns: * {Boolean} Boolean indicating whether or not the popup is visible */ visible: function() { return OpenLayers.Element.visible(this.div); }, /** * Method: toggle * Toggles visibility of the popup. */ toggle: function() { if (this.visible()) { this.hide(); } else { this.show(); } }, /** * Method: show * Makes the popup visible. */ show: function() { this.div.style.display = ''; if (this.panMapIfOutOfView) { this.panIntoView(); } }, /** * Method: hide * Makes the popup invisible. */ hide: function() { this.div.style.display = 'none'; }, /** * Method: setSize * Used to adjust the size of the popup. * * Parameters: * contentSize - {<OpenLayers.Size>} the new size for the popup's * contents div (in pixels). */ setSize:function(contentSize) { this.size = contentSize.clone(); // if our contentDiv has a css 'padding' set on it by a stylesheet, we // must add that to the desired "size". var contentDivPadding = this.getContentDivPadding(); var wPadding = contentDivPadding.left + contentDivPadding.right; var hPadding = contentDivPadding.top + contentDivPadding.bottom; // take into account the popup's 'padding' property this.fixPadding(); wPadding += this.padding.left + this.padding.right; hPadding += this.padding.top + this.padding.bottom; // make extra space for the close div if (this.closeDiv) { var closeDivWidth = parseInt(this.closeDiv.style.width); wPadding += closeDivWidth + contentDivPadding.right; } //increase size of the main popup div to take into account the // users's desired padding and close div. this.size.w += wPadding; this.size.h += hPadding; //now if our browser is IE, we need to actually make the contents // div itself bigger to take its own padding into effect. this makes // me want to shoot someone, but so it goes. if (OpenLayers.BROWSER_NAME == "msie") { this.contentSize.w += contentDivPadding.left + contentDivPadding.right; this.contentSize.h += contentDivPadding.bottom + contentDivPadding.top; } if (this.div != null) { this.div.style.width = this.size.w + "px"; this.div.style.height = this.size.h + "px"; } if (this.contentDiv != null){ this.contentDiv.style.width = contentSize.w + "px"; this.contentDiv.style.height = contentSize.h + "px"; } }, /** * APIMethod: updateSize * Auto size the popup so that it precisely fits its contents (as * determined by this.contentDiv.innerHTML). Popup size will, of * course, be limited by the available space on the current map */ updateSize: function() { // determine actual render dimensions of the contents by putting its // contents into a fake contentDiv (for the CSS) and then measuring it var preparedHTML = "<div class='" + this.contentDisplayClass+ "'>" + this.contentDiv.innerHTML + "</div>"; var containerElement = (this.map) ? this.map.div : document.body; var realSize = OpenLayers.Util.getRenderedDimensions( preparedHTML, null, { displayClass: this.displayClass, containerElement: containerElement } ); // is the "real" size of the div is safe to display in our map? var safeSize = this.getSafeContentSize(realSize); var newSize = null; if (safeSize.equals(realSize)) { //real size of content is small enough to fit on the map, // so we use real size. newSize = realSize; } else { // make a new 'size' object with the clipped dimensions // set or null if not clipped. var fixedSize = { w: (safeSize.w < realSize.w) ? safeSize.w : null, h: (safeSize.h < realSize.h) ? safeSize.h : null }; if (fixedSize.w && fixedSize.h) { //content is too big in both directions, so we will use // max popup size (safeSize), knowing well that it will // overflow both ways. newSize = safeSize; } else { //content is clipped in only one direction, so we need to // run getRenderedDimensions() again with a fixed dimension var clippedSize = OpenLayers.Util.getRenderedDimensions( preparedHTML, fixedSize, { displayClass: this.contentDisplayClass, containerElement: containerElement } ); //if the clipped size is still the same as the safeSize, // that means that our content must be fixed in the // offending direction. If overflow is 'auto', this means // we are going to have a scrollbar for sure, so we must // adjust for that. // var currentOverflow = OpenLayers.Element.getStyle( this.contentDiv, "overflow" ); if ( (currentOverflow != "hidden") && (clippedSize.equals(safeSize)) ) { var scrollBar = OpenLayers.Util.getScrollbarWidth(); if (fixedSize.w) { clippedSize.h += scrollBar; } else { clippedSize.w += scrollBar; } } newSize = this.getSafeContentSize(clippedSize); } } this.setSize(newSize); }, /** * Method: setBackgroundColor * Sets the background color of the popup. * * Parameters: * color - {String} the background color. eg "#FFBBBB" */ setBackgroundColor:function(color) { if (color != undefined) { this.backgroundColor = color; } if (this.div != null) { this.div.style.backgroundColor = this.backgroundColor; } }, /** * Method: setOpacity * Sets the opacity of the popup. * * Parameters: * opacity - {float} A value between 0.0 (transparent) and 1.0 (solid). */ setOpacity:function(opacity) { if (opacity != undefined) { this.opacity = opacity; } if (this.div != null) { // for Mozilla and Safari this.div.style.opacity = this.opacity; // for IE this.div.style.filter = 'alpha(opacity=' + this.opacity*100 + ')'; } }, /** * Method: setBorder * Sets the border style of the popup. * * Parameters: * border - {String} The border style value. eg 2px */ setBorder:function(border) { if (border != undefined) { this.border = border; } if (this.div != null) { this.div.style.border = this.border; } }, /** * Method: setContentHTML * Allows the user to set the HTML content of the popup. * * Parameters: * contentHTML - {String} HTML for the div. */ setContentHTML:function(contentHTML) { if (contentHTML != null) { this.contentHTML = contentHTML; } if ((this.contentDiv != null) && (this.contentHTML != null) && (this.contentHTML != this.contentDiv.innerHTML)) { this.contentDiv.innerHTML = this.contentHTML; if (this.autoSize) { //if popup has images, listen for when they finish // loading and resize accordingly this.registerImageListeners(); //auto size the popup to its current contents this.updateSize(); } } }, /** * Method: registerImageListeners * Called when an image contained by the popup loaded. this function * updates the popup size, then unregisters the image load listener. */ registerImageListeners: function() { // As the images load, this function will call updateSize() to // resize the popup to fit the content div (which presumably is now // bigger than when the image was not loaded). // // If the 'panMapIfOutOfView' property is set, we will pan the newly // resized popup back into view. // // Note that this function, when called, will have 'popup' and // 'img' properties in the context. // var onImgLoad = function() { if (this.popup.id === null) { // this.popup has been destroyed! return; } this.popup.updateSize(); if ( this.popup.visible() && this.popup.panMapIfOutOfView ) { this.popup.panIntoView(); } OpenLayers.Event.stopObserving( this.img, "load", this.img._onImgLoad ); }; //cycle through the images and if their size is 0x0, that means that // they haven't been loaded yet, so we attach the listener, which // will fire when the images finish loading and will resize the // popup accordingly to its new size. var images = this.contentDiv.getElementsByTagName("img"); for (var i = 0, len = images.length; i < len; i++) { var img = images[i]; if (img.width == 0 || img.height == 0) { var context = { 'popup': this, 'img': img }; //expando this function to the image itself before registering // it. This way we can easily and properly unregister it. img._onImgLoad = OpenLayers.Function.bind(onImgLoad, context); OpenLayers.Event.observe(img, 'load', img._onImgLoad); } } }, /** * APIMethod: getSafeContentSize * * Parameters: * size - {<OpenLayers.Size>} Desired size to make the popup. * * Returns: * {<OpenLayers.Size>} A size to make the popup which is neither smaller * than the specified minimum size, nor bigger than the maximum * size (which is calculated relative to the size of the viewport). */ getSafeContentSize: function(size) { var safeContentSize = size.clone(); // if our contentDiv has a css 'padding' set on it by a stylesheet, we // must add that to the desired "size". var contentDivPadding = this.getContentDivPadding(); var wPadding = contentDivPadding.left + contentDivPadding.right; var hPadding = contentDivPadding.top + contentDivPadding.bottom; // take into account the popup's 'padding' property this.fixPadding(); wPadding += this.padding.left + this.padding.right; hPadding += this.padding.top + this.padding.bottom; if (this.closeDiv) { var closeDivWidth = parseInt(this.closeDiv.style.width); wPadding += closeDivWidth + contentDivPadding.right; } // prevent the popup from being smaller than a specified minimal size if (this.minSize) { safeContentSize.w = Math.max(safeContentSize.w, (this.minSize.w - wPadding)); safeContentSize.h = Math.max(safeContentSize.h, (this.minSize.h - hPadding)); } // prevent the popup from being bigger than a specified maximum size if (this.maxSize) { safeContentSize.w = Math.min(safeContentSize.w, (this.maxSize.w - wPadding)); safeContentSize.h = Math.min(safeContentSize.h, (this.maxSize.h - hPadding)); } //make sure the desired size to set doesn't result in a popup that // is bigger than the map's viewport. // if (this.map && this.map.size) { var extraX = 0, extraY = 0; if (this.keepInMap && !this.panMapIfOutOfView) { var px = this.map.getPixelFromLonLat(this.lonlat); switch (this.relativePosition) { case "tr": extraX = px.x; extraY = this.map.size.h - px.y; break; case "tl": extraX = this.map.size.w - px.x; extraY = this.map.size.h - px.y; break; case "bl": extraX = this.map.size.w - px.x; extraY = px.y; break; case "br": extraX = px.x; extraY = px.y; break; default: extraX = px.x; extraY = this.map.size.h - px.y; break; } } var maxY = this.map.size.h - this.map.paddingForPopups.top - this.map.paddingForPopups.bottom - hPadding - extraY; var maxX = this.map.size.w - this.map.paddingForPopups.left - this.map.paddingForPopups.right - wPadding - extraX; safeContentSize.w = Math.min(safeContentSize.w, maxX); safeContentSize.h = Math.min(safeContentSize.h, maxY); } return safeContentSize; }, /** * Method: getContentDivPadding * Glorious, oh glorious hack in order to determine the css 'padding' of * the contentDiv. IE/Opera return null here unless we actually add the * popup's main 'div' element (which contains contentDiv) to the DOM. * So we make it invisible and then add it to the document temporarily. * * Once we've taken the padding readings we need, we then remove it * from the DOM (it will actually get added to the DOM in * Map.js's addPopup) * * Returns: * {<OpenLayers.Bounds>} */ getContentDivPadding: function() { //use cached value if we have it var contentDivPadding = this._contentDivPadding; if (!contentDivPadding) { if (this.div.parentNode == null) { //make the div invisible and add it to the page this.div.style.display = "none"; document.body.appendChild(this.div); } //read the padding settings from css, put them in an OL.Bounds contentDivPadding = new OpenLayers.Bounds( OpenLayers.Element.getStyle(this.contentDiv, "padding-left"), OpenLayers.Element.getStyle(this.contentDiv, "padding-bottom"), OpenLayers.Element.getStyle(this.contentDiv, "padding-right"), OpenLayers.Element.getStyle(this.contentDiv, "padding-top") ); //cache the value this._contentDivPadding = contentDivPadding; if (this.div.parentNode == document.body) { //remove the div from the page and make it visible again document.body.removeChild(this.div); this.div.style.display = ""; } } return contentDivPadding; }, /** * Method: addCloseBox * * Parameters: * callback - {Function} The callback to be called when the close button * is clicked. */ addCloseBox: function(callback) { this.closeDiv = OpenLayers.Util.createDiv( this.id + "_close", null, {w: 17, h: 17} ); this.closeDiv.className = "olPopupCloseBox"; // use the content div's css padding to determine if we should // padd the close div var contentDivPadding = this.getContentDivPadding(); this.closeDiv.style.right = contentDivPadding.right + "px"; this.closeDiv.style.top = contentDivPadding.top + "px"; this.groupDiv.appendChild(this.closeDiv); var closePopup = callback || function(e) { this.hide(); OpenLayers.Event.stop(e); }; OpenLayers.Event.observe(this.closeDiv, "touchend", OpenLayers.Function.bindAsEventListener(closePopup, this)); OpenLayers.Event.observe(this.closeDiv, "click", OpenLayers.Function.bindAsEventListener(closePopup, this)); }, /** * Method: panIntoView * Pans the map such that the popup is totaly viewable (if necessary) */ panIntoView: function() { var mapSize = this.map.getSize(); //start with the top left corner of the popup, in px, // relative to the viewport var origTL = this.map.getViewPortPxFromLayerPx( new OpenLayers.Pixel( parseInt(this.div.style.left), parseInt(this.div.style.top) )); var newTL = origTL.clone(); //new left (compare to margins, using this.size to calculate right) if (origTL.x < this.map.paddingForPopups.left) { newTL.x = this.map.paddingForPopups.left; } else if ( (origTL.x + this.size.w) > (mapSize.w - this.map.paddingForPopups.right)) { newTL.x = mapSize.w - this.map.paddingForPopups.right - this.size.w; } //new top (compare to margins, using this.size to calculate bottom) if (origTL.y < this.map.paddingForPopups.top) { newTL.y = this.map.paddingForPopups.top; } else if ( (origTL.y + this.size.h) > (mapSize.h - this.map.paddingForPopups.bottom)) { newTL.y = mapSize.h - this.map.paddingForPopups.bottom - this.size.h; } var dx = origTL.x - newTL.x; var dy = origTL.y - newTL.y; this.map.pan(dx, dy); }, /** * Method: registerEvents * Registers events on the popup. * * Do this in a separate function so that subclasses can * choose to override it if they wish to deal differently * with mouse events * * Note in the following handler functions that some special * care is needed to deal correctly with mousing and popups. * * Because the user might select the zoom-rectangle option and * then drag it over a popup, we need a safe way to allow the * mousemove and mouseup events to pass through the popup when * they are initiated from outside. The same procedure is needed for * touchmove and touchend events. * * Otherwise, we want to essentially kill the event propagation * for all other events, though we have to do so carefully, * without disabling basic html functionality, like clicking on * hyperlinks or drag-selecting text. */ registerEvents:function() { this.events = new OpenLayers.Events(this, this.div, null, true); function onTouchstart(evt) { OpenLayers.Event.stop(evt, true); } this.events.on({ "mousedown": this.onmousedown, "mousemove": this.onmousemove, "mouseup": this.onmouseup, "click": this.onclick, "mouseout": this.onmouseout, "dblclick": this.ondblclick, "touchstart": onTouchstart, scope: this }); }, /** * Method: onmousedown * When mouse goes down within the popup, make a note of * it locally, and then do not propagate the mousedown * (but do so safely so that user can select text inside) * * Parameters: * evt - {Event} */ onmousedown: function (evt) { this.mousedown = true; OpenLayers.Event.stop(evt, true); }, /** * Method: onmousemove * If the drag was started within the popup, then * do not propagate the mousemove (but do so safely * so that user can select text inside) * * Parameters: * evt - {Event} */ onmousemove: function (evt) { if (this.mousedown) { OpenLayers.Event.stop(evt, true); } }, /** * Method: onmouseup * When mouse comes up within the popup, after going down * in it, reset the flag, and then (once again) do not * propagate the event, but do so safely so that user can * select text inside * * Parameters: * evt - {Event} */ onmouseup: function (evt) { if (this.mousedown) { this.mousedown = false; OpenLayers.Event.stop(evt, true); } }, /** * Method: onclick * Ignore clicks, but allowing default browser handling * * Parameters: * evt - {Event} */ onclick: function (evt) { OpenLayers.Event.stop(evt, true); }, /** * Method: onmouseout * When mouse goes out of the popup set the flag to false so that * if they let go and then drag back in, we won't be confused. * * Parameters: * evt - {Event} */ onmouseout: function (evt) { this.mousedown = false; }, /** * Method: ondblclick * Ignore double-clicks, but allowing default browser handling * * Parameters: * evt - {Event} */ ondblclick: function (evt) { OpenLayers.Event.stop(evt, true); }, CLASS_NAME: "OpenLayers.Popup" }); OpenLayers.Popup.WIDTH = 200; OpenLayers.Popup.HEIGHT = 200; OpenLayers.Popup.COLOR = "white"; OpenLayers.Popup.OPACITY = 1; OpenLayers.Popup.BORDER = "0px"; /* ====================================================================== OpenLayers/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/Symbolizer/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/Symbolizer.js */ /** * Class: OpenLayers.Symbolizer.Point * A symbolizer used to render point features. */ OpenLayers.Symbolizer.Point = OpenLayers.Class(OpenLayers.Symbolizer, { /** * APIProperty: strokeColor * {String} Color for line stroke. This is a RGB hex value (e.g. "#ff0000" * for red). * * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. */ /** * APIProperty: strokeOpacity * {Number} Stroke opacity (0-1). * * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. */ /** * APIProperty: strokeWidth * {Number} Pixel stroke width. * * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. */ /** * APIProperty: strokeLinecap * {String} Stroke cap type ("butt", "round", or "square"). * * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. */ /** * Property: strokeDashstyle * {String} Stroke dash style according to the SLD spec. Note that the * OpenLayers values for strokeDashstyle ("dot", "dash", "dashdot", * "longdash", "longdashdot", or "solid") will not work in SLD, but * most SLD patterns will render correctly in OpenLayers. * * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. */ /** * APIProperty: fillColor * {String} RGB hex fill color (e.g. "#ff0000" for red). * * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. */ /** * APIProperty: fillOpacity * {Number} Fill opacity (0-1). * * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. */ /** * APIProperty: pointRadius * {Number} Pixel point radius. * * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. */ /** * APIProperty: externalGraphic * {String} Url to an external graphic that will be used for rendering * points. * * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. */ /** * APIProperty: graphicWidth * {Number} Pixel width for sizing an external graphic. * * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. */ /** * APIProperty: graphicHeight * {Number} Pixel height for sizing an external graphic. * * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. */ /** * APIProperty: graphicOpacity * {Number} Opacity (0-1) for an external graphic. * * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. */ /** * APIProperty: graphicXOffset * {Number} Pixel offset along the positive x axis for displacing an * external graphic. * * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. */ /** * APIProperty: graphicYOffset * {Number} Pixel offset along the positive y axis for displacing an * external graphic. * * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. */ /** * APIProperty: rotation * {Number} The rotation of a graphic in the clockwise direction about its * center point (or any point off center as specified by * <graphicXOffset> and <graphicYOffset>). * * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. */ /** * APIProperty: graphicName * {String} Named graphic to use when rendering points. Supported values * include "circle", "square", "star", "x", "cross", and "triangle". * * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. */ /** * Constructor: OpenLayers.Symbolizer.Point * Create a symbolizer for rendering points. * * Parameters: * config - {Object} An object containing properties to be set on the * symbolizer. Any documented symbolizer property can be set at * construction. * * Returns: * A new point symbolizer. */ initialize: function(config) { OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments); }, CLASS_NAME: "OpenLayers.Symbolizer.Point" }); /* ====================================================================== OpenLayers/Symbolizer/Line.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/Symbolizer.js */ /** * Class: OpenLayers.Symbolizer.Line * A symbolizer used to render line features. */ OpenLayers.Symbolizer.Line = OpenLayers.Class(OpenLayers.Symbolizer, { /** * APIProperty: strokeColor * {String} Color for line stroke. This is a RGB hex value (e.g. "#ff0000" * for red). * * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. */ /** * APIProperty: strokeOpacity * {Number} Stroke opacity (0-1). * * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. */ /** * APIProperty: strokeWidth * {Number} Pixel stroke width. * * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. */ /** * APIProperty: strokeLinecap * {String} Stroke cap type ("butt", "round", or "square"). * * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. */ /** * Property: strokeDashstyle * {String} Stroke dash style according to the SLD spec. Note that the * OpenLayers values for strokeDashstyle ("dot", "dash", "dashdot", * "longdash", "longdashdot", or "solid") will not work in SLD, but * most SLD patterns will render correctly in OpenLayers. * * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. */ /** * Constructor: OpenLayers.Symbolizer.Line * Create a symbolizer for rendering lines. * * Parameters: * config - {Object} An object containing properties to be set on the * symbolizer. Any documented symbolizer property can be set at * construction. * * Returns: * A new line symbolizer. */ initialize: function(config) { OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments); }, CLASS_NAME: "OpenLayers.Symbolizer.Line" }); /* ====================================================================== OpenLayers/Symbolizer/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/Symbolizer.js */ /** * Class: OpenLayers.Symbolizer.Polygon * A symbolizer used to render line features. */ OpenLayers.Symbolizer.Polygon = OpenLayers.Class(OpenLayers.Symbolizer, { /** * APIProperty: strokeColor * {String} Color for line stroke. This is a RGB hex value (e.g. "#ff0000" * for red). * * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. */ /** * APIProperty: strokeOpacity * {Number} Stroke opacity (0-1). * * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. */ /** * APIProperty: strokeWidth * {Number} Pixel stroke width. * * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. */ /** * APIProperty: strokeLinecap * {String} Stroke cap type ("butt", "round", or "square"). * * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. */ /** * Property: strokeDashstyle * {String} Stroke dash style according to the SLD spec. Note that the * OpenLayers values for strokeDashstyle ("dot", "dash", "dashdot", * "longdash", "longdashdot", or "solid") will not work in SLD, but * most SLD patterns will render correctly in OpenLayers. * * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. */ /** * APIProperty: fillColor * {String} RGB hex fill color (e.g. "#ff0000" for red). * * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. */ /** * APIProperty: fillOpacity * {Number} Fill opacity (0-1). * * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. */ /** * Constructor: OpenLayers.Symbolizer.Polygon * Create a symbolizer for rendering polygons. * * Parameters: * config - {Object} An object containing properties to be set on the * symbolizer. Any documented symbolizer property can be set at * construction. * * Returns: * A new polygon symbolizer. */ initialize: function(config) { OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments); }, CLASS_NAME: "OpenLayers.Symbolizer.Polygon" }); /* ====================================================================== OpenLayers/Symbolizer/Text.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/Symbolizer.js */ /** * Class: OpenLayers.Symbolizer.Text * A symbolizer used to render text labels for features. */ OpenLayers.Symbolizer.Text = OpenLayers.Class(OpenLayers.Symbolizer, { /** * APIProperty: label * {String} The text for the label. * * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. */ /** * APIProperty: fontFamily * {String} The font family for the label. * * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. */ /** * APIProperty: fontSize * {String} The font size for the label. * * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. */ /** * APIProperty: fontWeight * {String} The font weight for the label. * * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. */ /** * Property: fontStyle * {String} The font style for the label. * * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. */ /** * Constructor: OpenLayers.Symbolizer.Text * Create a symbolizer for rendering text labels. * * Parameters: * config - {Object} An object containing properties to be set on the * symbolizer. Any documented symbolizer property can be set at * construction. * * Returns: * A new text symbolizer. */ initialize: function(config) { OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments); }, CLASS_NAME: "OpenLayers.Symbolizer.Text" }); /* ====================================================================== OpenLayers/Symbolizer/Raster.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/Symbolizer.js */ /** * Class: OpenLayers.Symbolizer.Raster * A symbolizer used to render raster images. */ OpenLayers.Symbolizer.Raster = OpenLayers.Class(OpenLayers.Symbolizer, { /** * Constructor: OpenLayers.Symbolizer.Raster * Create a symbolizer for rendering rasters. * * Parameters: * config - {Object} An object containing properties to be set on the * symbolizer. Any documented symbolizer property can be set at * construction. * * Returns: * A new raster symbolizer. */ initialize: function(config) { OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments); }, CLASS_NAME: "OpenLayers.Symbolizer.Raster" }); /* ====================================================================== OpenLayers/Style2.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/Rule.js * @requires OpenLayers/Symbolizer/Point.js * @requires OpenLayers/Symbolizer/Line.js * @requires OpenLayers/Symbolizer/Polygon.js * @requires OpenLayers/Symbolizer/Text.js * @requires OpenLayers/Symbolizer/Raster.js */ /** * Class: OpenLayers.Style2 * This class represents a collection of rules for rendering features. */ OpenLayers.Style2 = OpenLayers.Class({ /** * Property: id * {String} A unique id for this session. */ id: null, /** * APIProperty: name * {String} Style identifier. */ name: null, /** * APIProperty: title * {String} Title of this style. */ title: null, /** * APIProperty: description * {String} Description of this style. */ 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, /** * APIProperty: rules * {Array(<OpenLayers.Rule>)} Collection of rendering rules. */ rules: null, /** * Constructor: OpenLayers.Style2 * Creates a style representing a collection of rendering rules. * * Parameters: * config - {Object} An object containing properties to be set on the * style. Any documented properties may be set at construction. * * Returns: * {<OpenLayers.Style2>} A new style object. */ initialize: function(config) { OpenLayers.Util.extend(this, config); 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(); } delete this.rules; }, /** * APIMethod: clone * Clones this style. * * Returns: * {<OpenLayers.Style2>} Clone of this style. */ clone: function() { var config = OpenLayers.Util.extend({}, this); // clone rules if (this.rules) { config.rules = []; for (var i=0, len=this.rules.length; i<len; ++i) { config.rules.push(this.rules[i].clone()); } } return new OpenLayers.Style2(config); }, CLASS_NAME: "OpenLayers.Style2" }); /* ====================================================================== 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/Marker.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 * @requires OpenLayers/Icon.js */ /** * Class: OpenLayers.Marker * Instances of OpenLayers.Marker are a combination of a * <OpenLayers.LonLat> and an <OpenLayers.Icon>. * * Markers are generally added to a special layer called * <OpenLayers.Layer.Markers>. * * Example: * (code) * var markers = new OpenLayers.Layer.Markers( "Markers" ); * map.addLayer(markers); * * var size = new OpenLayers.Size(21,25); * var offset = new OpenLayers.Pixel(-(size.w/2), -size.h); * var icon = new OpenLayers.Icon('http://www.openlayers.org/dev/img/marker.png', size, offset); * markers.addMarker(new OpenLayers.Marker(new OpenLayers.LonLat(0,0),icon)); * markers.addMarker(new OpenLayers.Marker(new OpenLayers.LonLat(0,0),icon.clone())); * * (end) * * Note that if you pass an icon into the Marker constructor, it will take * that icon and use it. This means that you should not share icons between * markers -- you use them once, but you should clone() for any additional * markers using that same icon. */ OpenLayers.Marker = OpenLayers.Class({ /** * Property: icon * {<OpenLayers.Icon>} The icon used by this marker. */ icon: null, /** * Property: lonlat * {<OpenLayers.LonLat>} location of object */ lonlat: null, /** * Property: events * {<OpenLayers.Events>} the event handler. */ events: null, /** * Property: map * {<OpenLayers.Map>} the map this marker is attached to */ map: null, /** * Constructor: OpenLayers.Marker * * Parameters: * lonlat - {<OpenLayers.LonLat>} the position of this marker * icon - {<OpenLayers.Icon>} the icon for this marker */ initialize: function(lonlat, icon) { this.lonlat = lonlat; var newIcon = (icon) ? icon : OpenLayers.Marker.defaultIcon(); if (this.icon == null) { this.icon = newIcon; } else { this.icon.url = newIcon.url; this.icon.size = newIcon.size; this.icon.offset = newIcon.offset; this.icon.calculateOffset = newIcon.calculateOffset; } this.events = new OpenLayers.Events(this, this.icon.imageDiv); }, /** * APIMethod: destroy * Destroy the marker. You must first remove the marker from any * layer which it has been added to, or you will get buggy behavior. * (This can not be done within the marker since the marker does not * know which layer it is attached to.) */ destroy: function() { // erase any drawn features this.erase(); this.map = null; this.events.destroy(); this.events = null; if (this.icon != null) { this.icon.destroy(); this.icon = null; } }, /** * Method: draw * Calls draw on the icon, and returns that output. * * Parameters: * px - {<OpenLayers.Pixel>} * * Returns: * {DOMElement} A new DOM Image with this marker's icon set at the * location passed-in */ draw: function(px) { return this.icon.draw(px); }, /** * Method: erase * Erases any drawn elements for this marker. */ erase: function() { if (this.icon != null) { this.icon.erase(); } }, /** * Method: moveTo * Move the marker to the new location. * * Parameters: * px - {<OpenLayers.Pixel>|Object} the pixel position to move to. * An OpenLayers.Pixel or an object with a 'x' and 'y' properties. */ moveTo: function (px) { if ((px != null) && (this.icon != null)) { this.icon.moveTo(px); } this.lonlat = this.map.getLonLatFromLayerPx(px); }, /** * APIMethod: isDrawn * * Returns: * {Boolean} Whether or not the marker is drawn. */ isDrawn: function() { var isDrawn = (this.icon && this.icon.isDrawn()); return isDrawn; }, /** * Method: onScreen * * Returns: * {Boolean} Whether or not the marker is currently visible on screen. */ onScreen:function() { var onScreen = false; if (this.map) { var screenBounds = this.map.getExtent(); onScreen = screenBounds.containsLonLat(this.lonlat); } return onScreen; }, /** * Method: inflate * Englarges the markers icon by the specified ratio. * * Parameters: * inflate - {float} the ratio to enlarge the marker by (passing 2 * will double the size). */ inflate: function(inflate) { if (this.icon) { this.icon.setSize({ w: this.icon.size.w * inflate, h: this.icon.size.h * inflate }); } }, /** * Method: setOpacity * Change the opacity of the marker by changin the opacity of * its icon * * Parameters: * opacity - {float} Specified as fraction (0.4, etc) */ setOpacity: function(opacity) { this.icon.setOpacity(opacity); }, /** * Method: setUrl * Change URL of the Icon Image. * * url - {String} */ setUrl: function(url) { this.icon.setUrl(url); }, /** * Method: display * Hide or show the icon * * display - {Boolean} */ display: function(display) { this.icon.display(display); }, CLASS_NAME: "OpenLayers.Marker" }); /** * Function: defaultIcon * Creates a default <OpenLayers.Icon>. * * Returns: * {<OpenLayers.Icon>} A default OpenLayers.Icon to use for a marker */ OpenLayers.Marker.defaultIcon = function() { return new OpenLayers.Icon(OpenLayers.Util.getImageLocation("marker.png"), {w: 21, h: 25}, {x: -10.5, y: -25}); }; /* ====================================================================== OpenLayers/Popup/Anchored.js ====================================================================== */ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for * full list of contributors). Published under the 2-clause BSD license. * See license.txt in the OpenLayers distribution or repository for the * full text of the license. */ /** * @requires OpenLayers/Popup.js */ /** * Class: OpenLayers.Popup.Anchored * * Inherits from: * - <OpenLayers.Popup> */ OpenLayers.Popup.Anchored = OpenLayers.Class(OpenLayers.Popup, { /** * Property: relativePosition * {String} Relative position of the popup ("br", "tr", "tl" or "bl"). */ relativePosition: null, /** * APIProperty: keepInMap * {Boolean} If panMapIfOutOfView is false, and this property is true, * contrain the popup such that it always fits in the available map * space. By default, this is set. If you are creating popups that are * near map edges and not allowing pannning, and especially if you have * a popup which has a fixedRelativePosition, setting this to false may * be a smart thing to do. * * For anchored popups, default is true, since subclasses will * usually want this functionality. */ keepInMap: true, /** * Property: anchor * {Object} Object to which we'll anchor the popup. Must expose a * 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>). */ anchor: null, /** * Constructor: OpenLayers.Popup.Anchored * * Parameters: * id - {String} * lonlat - {<OpenLayers.LonLat>} * contentSize - {<OpenLayers.Size>} * contentHTML - {String} * anchor - {Object} Object which must expose a 'size' <OpenLayers.Size> * and 'offset' <OpenLayers.Pixel> (generally an <OpenLayers.Icon>). * closeBox - {Boolean} * closeBoxCallback - {Function} Function to be called on closeBox click. */ initialize:function(id, lonlat, contentSize, contentHTML, anchor, closeBox, closeBoxCallback) { var newArguments = [ id, lonlat, contentSize, contentHTML, closeBox, closeBoxCallback ]; OpenLayers.Popup.prototype.initialize.apply(this, newArguments); this.anchor = (anchor != null) ? anchor : { size: new OpenLayers.Size(0,0), offset: new OpenLayers.Pixel(0,0)}; }, /** * APIMethod: destroy */ destroy: function() { this.anchor = null; this.relativePosition = null; OpenLayers.Popup.prototype.destroy.apply(this, arguments); }, /** * APIMethod: show * Overridden from Popup since user might hide popup and then show() it * in a new location (meaning we might want to update the relative * position on the show) */ show: function() { this.updatePosition(); OpenLayers.Popup.prototype.show.apply(this, arguments); }, /** * Method: moveTo * Since the popup is moving to a new px, it might need also to be moved * relative to where the marker is. We first calculate the new * relativePosition, and then we calculate the new px where we will * put the popup, based on the new relative position. * * If the relativePosition has changed, we must also call * updateRelativePosition() to make any visual changes to the popup * which are associated with putting it in a new relativePosition. * * Parameters: * px - {<OpenLayers.Pixel>} */ moveTo: function(px) { var oldRelativePosition = this.relativePosition; this.relativePosition = this.calculateRelativePosition(px); OpenLayers.Popup.prototype.moveTo.call(this, this.calculateNewPx(px)); //if this move has caused the popup to change its relative position, // we need to make the appropriate cosmetic changes. if (this.relativePosition != oldRelativePosition) { this.updateRelativePosition(); } }, /** * APIMethod: setSize * * Parameters: * contentSize - {<OpenLayers.Size>} the new size for the popup's * contents div (in pixels). */ setSize:function(contentSize) { OpenLayers.Popup.prototype.setSize.apply(this, arguments); if ((this.lonlat) && (this.map)) { var px = this.map.getLayerPxFromLonLat(this.lonlat); this.moveTo(px); } }, /** * Method: calculateRelativePosition * * Parameters: * px - {<OpenLayers.Pixel>} * * Returns: * {String} The relative position ("br" "tr" "tl" "bl") at which the popup * should be placed. */ calculateRelativePosition:function(px) { var lonlat = this.map.getLonLatFromLayerPx(px); var extent = this.map.getExtent(); var quadrant = extent.determineQuadrant(lonlat); return OpenLayers.Bounds.oppositeQuadrant(quadrant); }, /** * Method: updateRelativePosition * The popup has been moved to a new relative location, so we may want to * make some cosmetic adjustments to it. * * Note that in the classic Anchored popup, there is nothing to do * here, since the popup looks exactly the same in all four positions. * Subclasses such as Framed, however, will want to do something * special here. */ updateRelativePosition: function() { //to be overridden by subclasses }, /** * Method: calculateNewPx * * Parameters: * px - {<OpenLayers.Pixel>} * * Returns: * {<OpenLayers.Pixel>} The the new px position of the popup on the screen * relative to the passed-in px. */ calculateNewPx:function(px) { var newPx = px.offset(this.anchor.offset); //use contentSize if size is not already set var size = this.size || this.contentSize; var top = (this.relativePosition.charAt(0) == 't'); newPx.y += (top) ? -size.h : this.anchor.size.h; var left = (this.relativePosition.charAt(1) == 'l'); newPx.x += (left) ? -size.w : this.anchor.size.w; return newPx; }, CLASS_NAME: "OpenLayers.Popup.Anchored" }); /* ====================================================================== OpenLayers/Popup/Framed.js ====================================================================== */ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for * full list of contributors). Published under the 2-clause BSD license. * See license.txt in the OpenLayers distribution or repository for the * full text of the license. */ /** * @requires OpenLayers/Popup/Anchored.js */ /** * Class: OpenLayers.Popup.Framed * * Inherits from: * - <OpenLayers.Popup.Anchored> */ OpenLayers.Popup.Framed = OpenLayers.Class(OpenLayers.Popup.Anchored, { /** * Property: imageSrc * {String} location of the image to be used as the popup frame */ imageSrc: null, /** * Property: imageSize * {<OpenLayers.Size>} Size (measured in pixels) of the image located * by the 'imageSrc' property. */ imageSize: null, /** * APIProperty: isAlphaImage * {Boolean} The image has some alpha and thus needs to use the alpha * image hack. Note that setting this to true will have no noticeable * effect in FF or IE7 browsers, but will all but crush the ie6 * browser. * Default is false. */ isAlphaImage: false, /** * Property: positionBlocks * {Object} Hash of different position blocks (Object/Hashs). Each block * will be keyed by a two-character 'relativePosition' * code string (ie "tl", "tr", "bl", "br"). Block properties are * 'offset', 'padding' (self-explanatory), and finally the 'blocks' * parameter, which is an array of the block objects. * * Each block object must have 'size', 'anchor', and 'position' * properties. * * Note that positionBlocks should never be modified at runtime. */ positionBlocks: null, /** * Property: blocks * {Array[Object]} Array of objects, each of which is one "block" of the * popup. Each block has a 'div' and an 'image' property, both of * which are DOMElements, and the latter of which is appended to the * former. These are reused as the popup goes changing positions for * great economy and elegance. */ blocks: null, /** * APIProperty: fixedRelativePosition * {Boolean} We want the framed popup to work dynamically placed relative * to its anchor but also in just one fixed position. A well designed * framed popup will have the pixels and logic to display itself in * any of the four relative positions, but (understandably), this will * not be the case for all of them. By setting this property to 'true', * framed popup will not recalculate for the best placement each time * it's open, but will always open the same way. * Note that if this is set to true, it is generally advisable to also * set the 'panIntoView' property to true so that the popup can be * scrolled into view (since it will often be offscreen on open) * Default is false. */ fixedRelativePosition: false, /** * Constructor: OpenLayers.Popup.Framed * * Parameters: * id - {String} * lonlat - {<OpenLayers.LonLat>} * contentSize - {<OpenLayers.Size>} * contentHTML - {String} * anchor - {Object} Object to which we'll anchor the popup. Must expose * a 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>) * (Note that this is generally an <OpenLayers.Icon>). * closeBox - {Boolean} * closeBoxCallback - {Function} Function to be called on closeBox click. */ initialize:function(id, lonlat, contentSize, contentHTML, anchor, closeBox, closeBoxCallback) { OpenLayers.Popup.Anchored.prototype.initialize.apply(this, arguments); if (this.fixedRelativePosition) { //based on our decided relativePostion, set the current padding // this keeps us from getting into trouble this.updateRelativePosition(); //make calculateRelativePosition always return the specified // fixed position. this.calculateRelativePosition = function(px) { return this.relativePosition; }; } this.contentDiv.style.position = "absolute"; this.contentDiv.style.zIndex = 1; if (closeBox) { this.closeDiv.style.zIndex = 1; } this.groupDiv.style.position = "absolute"; this.groupDiv.style.top = "0px"; this.groupDiv.style.left = "0px"; this.groupDiv.style.height = "100%"; this.groupDiv.style.width = "100%"; }, /** * APIMethod: destroy */ destroy: function() { this.imageSrc = null; this.imageSize = null; this.isAlphaImage = null; this.fixedRelativePosition = false; this.positionBlocks = null; //remove our blocks for(var i = 0; i < this.blocks.length; i++) { var block = this.blocks[i]; if (block.image) { block.div.removeChild(block.image); } block.image = null; if (block.div) { this.groupDiv.removeChild(block.div); } block.div = null; } this.blocks = null; OpenLayers.Popup.Anchored.prototype.destroy.apply(this, arguments); }, /** * APIMethod: setBackgroundColor */ setBackgroundColor:function(color) { //does nothing since the framed popup's entire scheme is based on a // an image -- changing the background color makes no sense. }, /** * APIMethod: setBorder */ setBorder:function() { //does nothing since the framed popup's entire scheme is based on a // an image -- changing the popup's border makes no sense. }, /** * Method: setOpacity * Sets the opacity of the popup. * * Parameters: * opacity - {float} A value between 0.0 (transparent) and 1.0 (solid). */ setOpacity:function(opacity) { //does nothing since we suppose that we'll never apply an opacity // to a framed popup }, /** * APIMethod: setSize * Overridden here, because we need to update the blocks whenever the size * of the popup has changed. * * Parameters: * contentSize - {<OpenLayers.Size>} the new size for the popup's * contents div (in pixels). */ setSize:function(contentSize) { OpenLayers.Popup.Anchored.prototype.setSize.apply(this, arguments); this.updateBlocks(); }, /** * Method: updateRelativePosition * When the relative position changes, we need to set the new padding * BBOX on the popup, reposition the close div, and update the blocks. */ updateRelativePosition: function() { //update the padding this.padding = this.positionBlocks[this.relativePosition].padding; //update the position of our close box to new padding if (this.closeDiv) { // use the content div's css padding to determine if we should // padd the close div var contentDivPadding = this.getContentDivPadding(); this.closeDiv.style.right = contentDivPadding.right + this.padding.right + "px"; this.closeDiv.style.top = contentDivPadding.top + this.padding.top + "px"; } this.updateBlocks(); }, /** * Method: calculateNewPx * Besides the standard offset as determined by the Anchored class, our * Framed popups have a special 'offset' property for each of their * positions, which is used to offset the popup relative to its anchor. * * Parameters: * px - {<OpenLayers.Pixel>} * * Returns: * {<OpenLayers.Pixel>} The the new px position of the popup on the screen * relative to the passed-in px. */ calculateNewPx:function(px) { var newPx = OpenLayers.Popup.Anchored.prototype.calculateNewPx.apply( this, arguments ); newPx = newPx.offset(this.positionBlocks[this.relativePosition].offset); return newPx; }, /** * Method: createBlocks */ createBlocks: function() { this.blocks = []; //since all positions contain the same number of blocks, we can // just pick the first position and use its blocks array to create // our blocks array var firstPosition = null; for(var key in this.positionBlocks) { firstPosition = key; break; } var position = this.positionBlocks[firstPosition]; for (var i = 0; i < position.blocks.length; i++) { var block = {}; this.blocks.push(block); var divId = this.id + '_FrameDecorationDiv_' + i; block.div = OpenLayers.Util.createDiv(divId, null, null, null, "absolute", null, "hidden", null ); var imgId = this.id + '_FrameDecorationImg_' + i; var imageCreator = (this.isAlphaImage) ? OpenLayers.Util.createAlphaImageDiv : OpenLayers.Util.createImage; block.image = imageCreator(imgId, null, this.imageSize, this.imageSrc, "absolute", null, null, null ); block.div.appendChild(block.image); this.groupDiv.appendChild(block.div); } }, /** * Method: updateBlocks * Internal method, called on initialize and when the popup's relative * position has changed. This function takes care of re-positioning * the popup's blocks in their appropropriate places. */ updateBlocks: function() { if (!this.blocks) { this.createBlocks(); } if (this.size && this.relativePosition) { var position = this.positionBlocks[this.relativePosition]; for (var i = 0; i < position.blocks.length; i++) { var positionBlock = position.blocks[i]; var block = this.blocks[i]; // adjust sizes var l = positionBlock.anchor.left; var b = positionBlock.anchor.bottom; var r = positionBlock.anchor.right; var t = positionBlock.anchor.top; //note that we use the isNaN() test here because if the // size object is initialized with a "auto" parameter, the // size constructor calls parseFloat() on the string, // which will turn it into NaN // var w = (isNaN(positionBlock.size.w)) ? this.size.w - (r + l) : positionBlock.size.w; var h = (isNaN(positionBlock.size.h)) ? this.size.h - (b + t) : positionBlock.size.h; block.div.style.width = (w < 0 ? 0 : w) + 'px'; block.div.style.height = (h < 0 ? 0 : h) + 'px'; block.div.style.left = (l != null) ? l + 'px' : ''; block.div.style.bottom = (b != null) ? b + 'px' : ''; block.div.style.right = (r != null) ? r + 'px' : ''; block.div.style.top = (t != null) ? t + 'px' : ''; block.image.style.left = positionBlock.position.x + 'px'; block.image.style.top = positionBlock.position.y + 'px'; } this.contentDiv.style.left = this.padding.left + "px"; this.contentDiv.style.top = this.padding.top + "px"; } }, CLASS_NAME: "OpenLayers.Popup.Framed" }); /* ====================================================================== OpenLayers/Popup/FramedCloud.js ====================================================================== */ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for * full list of contributors). Published under the 2-clause BSD license. * See license.txt in the OpenLayers distribution or repository for the * full text of the license. */ /** * @requires OpenLayers/Popup/Framed.js * @requires OpenLayers/Util.js * @requires OpenLayers/BaseTypes/Bounds.js * @requires OpenLayers/BaseTypes/Pixel.js * @requires OpenLayers/BaseTypes/Size.js */ /** * Class: OpenLayers.Popup.FramedCloud * * Inherits from: * - <OpenLayers.Popup.Framed> */ OpenLayers.Popup.FramedCloud = OpenLayers.Class(OpenLayers.Popup.Framed, { /** * Property: contentDisplayClass * {String} The CSS class of the popup content div. */ contentDisplayClass: "olFramedCloudPopupContent", /** * APIProperty: autoSize * {Boolean} Framed Cloud is autosizing by default. */ autoSize: true, /** * APIProperty: panMapIfOutOfView * {Boolean} Framed Cloud does pan into view by default. */ panMapIfOutOfView: true, /** * APIProperty: imageSize * {<OpenLayers.Size>} */ imageSize: new OpenLayers.Size(1276, 736), /** * APIProperty: isAlphaImage * {Boolean} The FramedCloud does not use an alpha image (in honor of the * good ie6 folk out there) */ isAlphaImage: false, /** * APIProperty: fixedRelativePosition * {Boolean} The Framed Cloud popup works in just one fixed position. */ fixedRelativePosition: false, /** * Property: positionBlocks * {Object} Hash of differen position blocks, keyed by relativePosition * two-character code string (ie "tl", "tr", "bl", "br") */ positionBlocks: { "tl": { 'offset': new OpenLayers.Pixel(44, 0), 'padding': new OpenLayers.Bounds(8, 40, 8, 9), 'blocks': [ { // top-left size: new OpenLayers.Size('auto', 'auto'), anchor: new OpenLayers.Bounds(0, 51, 22, 0), position: new OpenLayers.Pixel(0, 0) }, { //top-right size: new OpenLayers.Size(22, 'auto'), anchor: new OpenLayers.Bounds(null, 50, 0, 0), position: new OpenLayers.Pixel(-1238, 0) }, { //bottom-left size: new OpenLayers.Size('auto', 19), anchor: new OpenLayers.Bounds(0, 32, 22, null), position: new OpenLayers.Pixel(0, -631) }, { //bottom-right size: new OpenLayers.Size(22, 18), anchor: new OpenLayers.Bounds(null, 32, 0, null), position: new OpenLayers.Pixel(-1238, -632) }, { // stem size: new OpenLayers.Size(81, 35), anchor: new OpenLayers.Bounds(null, 0, 0, null), position: new OpenLayers.Pixel(0, -688) } ] }, "tr": { 'offset': new OpenLayers.Pixel(-45, 0), 'padding': new OpenLayers.Bounds(8, 40, 8, 9), 'blocks': [ { // top-left size: new OpenLayers.Size('auto', 'auto'), anchor: new OpenLayers.Bounds(0, 51, 22, 0), position: new OpenLayers.Pixel(0, 0) }, { //top-right size: new OpenLayers.Size(22, 'auto'), anchor: new OpenLayers.Bounds(null, 50, 0, 0), position: new OpenLayers.Pixel(-1238, 0) }, { //bottom-left size: new OpenLayers.Size('auto', 19), anchor: new OpenLayers.Bounds(0, 32, 22, null), position: new OpenLayers.Pixel(0, -631) }, { //bottom-right size: new OpenLayers.Size(22, 19), anchor: new OpenLayers.Bounds(null, 32, 0, null), position: new OpenLayers.Pixel(-1238, -631) }, { // stem size: new OpenLayers.Size(81, 35), anchor: new OpenLayers.Bounds(0, 0, null, null), position: new OpenLayers.Pixel(-215, -687) } ] }, "bl": { 'offset': new OpenLayers.Pixel(45, 0), 'padding': new OpenLayers.Bounds(8, 9, 8, 40), 'blocks': [ { // top-left size: new OpenLayers.Size('auto', 'auto'), anchor: new OpenLayers.Bounds(0, 21, 22, 32), position: new OpenLayers.Pixel(0, 0) }, { //top-right size: new OpenLayers.Size(22, 'auto'), anchor: new OpenLayers.Bounds(null, 21, 0, 32), position: new OpenLayers.Pixel(-1238, 0) }, { //bottom-left size: new OpenLayers.Size('auto', 21), anchor: new OpenLayers.Bounds(0, 0, 22, null), position: new OpenLayers.Pixel(0, -629) }, { //bottom-right size: new OpenLayers.Size(22, 21), anchor: new OpenLayers.Bounds(null, 0, 0, null), position: new OpenLayers.Pixel(-1238, -629) }, { // stem size: new OpenLayers.Size(81, 33), anchor: new OpenLayers.Bounds(null, null, 0, 0), position: new OpenLayers.Pixel(-101, -674) } ] }, "br": { 'offset': new OpenLayers.Pixel(-44, 0), 'padding': new OpenLayers.Bounds(8, 9, 8, 40), 'blocks': [ { // top-left size: new OpenLayers.Size('auto', 'auto'), anchor: new OpenLayers.Bounds(0, 21, 22, 32), position: new OpenLayers.Pixel(0, 0) }, { //top-right size: new OpenLayers.Size(22, 'auto'), anchor: new OpenLayers.Bounds(null, 21, 0, 32), position: new OpenLayers.Pixel(-1238, 0) }, { //bottom-left size: new OpenLayers.Size('auto', 21), anchor: new OpenLayers.Bounds(0, 0, 22, null), position: new OpenLayers.Pixel(0, -629) }, { //bottom-right size: new OpenLayers.Size(22, 21), anchor: new OpenLayers.Bounds(null, 0, 0, null), position: new OpenLayers.Pixel(-1238, -629) }, { // stem size: new OpenLayers.Size(81, 33), anchor: new OpenLayers.Bounds(0, null, null, 0), position: new OpenLayers.Pixel(-311, -674) } ] } }, /** * APIProperty: minSize * {<OpenLayers.Size>} */ minSize: new OpenLayers.Size(105, 10), /** * APIProperty: maxSize * {<OpenLayers.Size>} */ maxSize: new OpenLayers.Size(1200, 660), /** * Constructor: OpenLayers.Popup.FramedCloud * * Parameters: * id - {String} * lonlat - {<OpenLayers.LonLat>} * contentSize - {<OpenLayers.Size>} * contentHTML - {String} * anchor - {Object} Object to which we'll anchor the popup. Must expose * a 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>) * (Note that this is generally an <OpenLayers.Icon>). * closeBox - {Boolean} * closeBoxCallback - {Function} Function to be called on closeBox click. */ initialize:function(id, lonlat, contentSize, contentHTML, anchor, closeBox, closeBoxCallback) { this.imageSrc = OpenLayers.Util.getImageLocation('cloud-popup-relative.png'); OpenLayers.Popup.Framed.prototype.initialize.apply(this, arguments); this.contentDiv.className = this.contentDisplayClass; }, CLASS_NAME: "OpenLayers.Popup.FramedCloud" }); /* ====================================================================== OpenLayers/Marker/Box.js ====================================================================== */ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for * full list of contributors). Published under the 2-clause BSD license. * See license.txt in the OpenLayers distribution or repository for the * full text of the license. */ /** * @requires OpenLayers/Marker.js */ /** * Class: OpenLayers.Marker.Box * * Inherits from: * - <OpenLayers.Marker> */ OpenLayers.Marker.Box = OpenLayers.Class(OpenLayers.Marker, { /** * Property: bounds * {<OpenLayers.Bounds>} */ bounds: null, /** * Property: div * {DOMElement} */ div: null, /** * Constructor: OpenLayers.Marker.Box * * Parameters: * bounds - {<OpenLayers.Bounds>} * borderColor - {String} * borderWidth - {int} */ initialize: function(bounds, borderColor, borderWidth) { this.bounds = bounds; this.div = OpenLayers.Util.createDiv(); this.div.style.overflow = 'hidden'; this.events = new OpenLayers.Events(this, this.div); this.setBorder(borderColor, borderWidth); }, /** * Method: destroy */ destroy: function() { this.bounds = null; this.div = null; OpenLayers.Marker.prototype.destroy.apply(this, arguments); }, /** * Method: setBorder * Allow the user to change the box's color and border width * * Parameters: * color - {String} Default is "red" * width - {int} Default is 2 */ setBorder: function (color, width) { if (!color) { color = "red"; } if (!width) { width = 2; } this.div.style.border = width + "px solid " + color; }, /** * Method: draw * * Parameters: * px - {<OpenLayers.Pixel>} * sz - {<OpenLayers.Size>} * * Returns: * {DOMElement} A new DOM Image with this marker's icon set at the * location passed-in */ draw: function(px, sz) { OpenLayers.Util.modifyDOMElement(this.div, null, px, sz); return this.div; }, /** * Method: onScreen * * Rreturn: * {Boolean} Whether or not the marker is currently visible on screen. */ onScreen:function() { var onScreen = false; if (this.map) { var screenBounds = this.map.getExtent(); onScreen = screenBounds.containsBounds(this.bounds, true, true); } return onScreen; }, /** * Method: display * Hide or show the icon * * Parameters: * display - {Boolean} */ display: function(display) { this.div.style.display = (display) ? "" : "none"; }, CLASS_NAME: "OpenLayers.Marker.Box" }); /* ====================================================================== OpenLayers/Handler/MouseWheel.js ====================================================================== */ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for * full list of contributors). Published under the 2-clause BSD license. * See license.txt in the OpenLayers distribution or repository for the * full text of the license. */ /** * @requires OpenLayers/Handler.js */ /** * Class: OpenLayers.Handler.MouseWheel * Handler for wheel up/down events. * * Inherits from: * - <OpenLayers.Handler> */ OpenLayers.Handler.MouseWheel = OpenLayers.Class(OpenLayers.Handler, { /** * Property: wheelListener * {function} */ wheelListener: null, /** * Property: interval * {Integer} In order to increase server performance, an interval (in * milliseconds) can be set to reduce the number of up/down events * called. If set, a new up/down event will not be set until the * interval has passed. * Defaults to 0, meaning no interval. */ interval: 0, /** * Property: maxDelta * {Integer} Maximum delta to collect before breaking from the current * interval. In cumulative mode, this also limits the maximum delta * returned from the handler. Default is Number.POSITIVE_INFINITY. */ maxDelta: Number.POSITIVE_INFINITY, /** * Property: delta * {Integer} When interval is set, delta collects the mousewheel z-deltas * of the events that occur within the interval. * See also the cumulative option */ delta: 0, /** * Property: cumulative * {Boolean} When interval is set: true to collect all the mousewheel * z-deltas, false to only record the delta direction (positive or * negative) */ cumulative: true, /** * Constructor: OpenLayers.Handler.MouseWheel * * Parameters: * control - {<OpenLayers.Control>} * callbacks - {Object} An object containing a single function to be * called when the drag operation is finished. * The callback should expect to recieve a single * argument, the point geometry. * options - {Object} */ initialize: function(control, callbacks, options) { OpenLayers.Handler.prototype.initialize.apply(this, arguments); this.wheelListener = OpenLayers.Function.bindAsEventListener( this.onWheelEvent, this ); }, /** * Method: destroy */ destroy: function() { OpenLayers.Handler.prototype.destroy.apply(this, arguments); this.wheelListener = null; }, /** * Mouse ScrollWheel code thanks to http://adomas.org/javascript-mouse-wheel/ */ /** * Method: onWheelEvent * Catch the wheel event and handle it xbrowserly * * Parameters: * e - {Event} */ onWheelEvent: function(e){ // make sure we have a map and check keyboard modifiers if (!this.map || !this.checkModifiers(e)) { return; } // Ride up the element's DOM hierarchy to determine if it or any of // its ancestors was: // * specifically marked as scrollable (CSS overflow property) // * one of our layer divs or a div marked as scrollable // ('olScrollable' CSS class) // * the map div // var overScrollableDiv = false; var allowScroll = false; var overMapDiv = false; var elem = OpenLayers.Event.element(e); while((elem != null) && !overMapDiv && !overScrollableDiv) { if (!overScrollableDiv) { try { var overflow; if (elem.currentStyle) { overflow = elem.currentStyle["overflow"]; } else { var style = document.defaultView.getComputedStyle(elem, null); overflow = style.getPropertyValue("overflow"); } overScrollableDiv = ( overflow && (overflow == "auto") || (overflow == "scroll") ); } catch(err) { //sometimes when scrolling in a popup, this causes // obscure browser error } } if (!allowScroll) { allowScroll = OpenLayers.Element.hasClass(elem, 'olScrollable'); if (!allowScroll) { for (var i = 0, len = this.map.layers.length; i < len; i++) { // Are we in the layer div? Note that we have two cases // here: one is to catch EventPane layers, which have a // pane above the layer (layer.pane) var layer = this.map.layers[i]; if (elem == layer.div || elem == layer.pane) { allowScroll = true; break; } } } } overMapDiv = (elem == this.map.div); elem = elem.parentNode; } // Logic below is the following: // // If we are over a scrollable div or not over the map div: // * do nothing (let the browser handle scrolling) // // otherwise // // If we are over the layer div or a 'olScrollable' div: // * zoom/in out // then // * kill event (so as not to also scroll the page after zooming) // // otherwise // // Kill the event (dont scroll the page if we wheel over the // layerswitcher or the pan/zoom control) // if (!overScrollableDiv && overMapDiv) { if (allowScroll) { var delta = 0; if (e.wheelDelta) { delta = e.wheelDelta; if (delta % 160 === 0) { // opera have steps of 160 instead of 120 delta = delta * 0.75; } delta = delta / 120; } else if (e.detail) { // detail in Firefox on OS X is 1/3 of Windows // so force delta 1 / -1 delta = - (e.detail / Math.abs(e.detail)); } this.delta += delta; window.clearTimeout(this._timeoutId); if(this.interval && Math.abs(this.delta) < this.maxDelta) { // store e because window.event might change during delay var evt = OpenLayers.Util.extend({}, e); this._timeoutId = window.setTimeout( OpenLayers.Function.bind(function(){ this.wheelZoom(evt); }, this), this.interval ); } else { this.wheelZoom(e); } } OpenLayers.Event.stop(e); } }, /** * Method: wheelZoom * Given the wheel event, we carry out the appropriate zooming in or out, * based on the 'wheelDelta' or 'detail' property of the event. * * Parameters: * e - {Event} */ wheelZoom: function(e) { var delta = this.delta; this.delta = 0; if (delta) { e.xy = this.map.events.getMousePosition(e); if (delta < 0) { this.callback("down", [e, this.cumulative ? Math.max(-this.maxDelta, delta) : -1]); } else { this.callback("up", [e, this.cumulative ? Math.min(this.maxDelta, delta) : 1]); } } }, /** * Method: activate */ activate: function (evt) { if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) { //register mousewheel events specifically on the window and document var wheelListener = this.wheelListener; OpenLayers.Event.observe(window, "DOMMouseScroll", wheelListener); OpenLayers.Event.observe(window, "mousewheel", wheelListener); OpenLayers.Event.observe(document, "mousewheel", wheelListener); return true; } else { return false; } }, /** * Method: deactivate */ deactivate: function (evt) { if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) { // unregister mousewheel events specifically on the window and document var wheelListener = this.wheelListener; OpenLayers.Event.stopObserving(window, "DOMMouseScroll", wheelListener); OpenLayers.Event.stopObserving(window, "mousewheel", wheelListener); OpenLayers.Event.stopObserving(document, "mousewheel", wheelListener); return true; } else { return false; } }, CLASS_NAME: "OpenLayers.Handler.MouseWheel" }); /* ====================================================================== OpenLayers/Handler/Hover.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.Hover * The hover handler is to be used to emulate mouseovers on objects * on the map that aren't DOM elements. For example one can use * this handler to send WMS/GetFeatureInfo requests as the user * moves the mouve over the map. * * Inherits from: * - <OpenLayers.Handler> */ OpenLayers.Handler.Hover = OpenLayers.Class(OpenLayers.Handler, { /** * APIProperty: delay * {Integer} - Number of milliseconds between mousemoves before * the event is considered a hover. Default is 500. */ delay: 500, /** * APIProperty: pixelTolerance * {Integer} - Maximum number of pixels between mousemoves for * an event to be considered a hover. Default is null. */ pixelTolerance: null, /** * APIProperty: stopMove * {Boolean} - Stop other listeners from being notified on mousemoves. * Default is false. */ stopMove: false, /** * Property: px * {<OpenLayers.Pixel>} - The location of the last mousemove, expressed * in pixels. */ px: null, /** * Property: timerId * {Number} - The id of the timer. */ timerId: null, /** * Constructor: OpenLayers.Handler.Hover * Construct a hover handler. * * Parameters: * control - {<OpenLayers.Control>} The control that initialized this * handler. The control is assumed to have a valid map property; that * map is used in the handler's own setMap method. * callbacks - {Object} An object with keys corresponding to callbacks * that will be called by the handler. The callbacks should * expect to receive a single argument, the event. Callbacks for * 'move', the mouse is moving, and 'pause', the mouse is pausing, * are supported. * options - {Object} An optional object whose properties will be set on * the handler. */ /** * Method: mousemove * Called when the mouse moves on the map. * * Parameters: * evt - {<OpenLayers.Event>} * * Returns: * {Boolean} Continue propagating this event. */ mousemove: function(evt) { if(this.passesTolerance(evt.xy)) { this.clearTimer(); this.callback('move', [evt]); this.px = evt.xy; // clone the evt so original properties can be accessed even // if the browser deletes them during the delay evt = OpenLayers.Util.extend({}, evt); this.timerId = window.setTimeout( OpenLayers.Function.bind(this.delayedCall, this, evt), this.delay ); } return !this.stopMove; }, /** * Method: mouseout * Called when the mouse goes out of the map. * * Parameters: * evt - {<OpenLayers.Event>} * * Returns: * {Boolean} Continue propagating this event. */ mouseout: function(evt) { if (OpenLayers.Util.mouseLeft(evt, this.map.viewPortDiv)) { this.clearTimer(); this.callback('move', [evt]); } return true; }, /** * Method: passesTolerance * Determine whether the mouse move is within the optional pixel tolerance. * * Parameters: * px - {<OpenLayers.Pixel>} * * Returns: * {Boolean} The mouse move is within the pixel tolerance. */ passesTolerance: function(px) { var passes = true; if(this.pixelTolerance && this.px) { var dpx = Math.sqrt( Math.pow(this.px.x - px.x, 2) + Math.pow(this.px.y - px.y, 2) ); if(dpx < this.pixelTolerance) { passes = false; } } return passes; }, /** * Method: clearTimer * Clear the timer and set <timerId> to null. */ clearTimer: function() { if(this.timerId != null) { window.clearTimeout(this.timerId); this.timerId = null; } }, /** * Method: delayedCall * Triggers pause callback. * * Parameters: * evt - {<OpenLayers.Event>} */ delayedCall: function(evt) { this.callback('pause', [evt]); }, /** * APIMethod: deactivate * Deactivate the handler. * * Returns: * {Boolean} The handler was successfully deactivated. */ deactivate: function() { var deactivated = false; if(OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) { this.clearTimer(); deactivated = true; } return deactivated; }, CLASS_NAME: "OpenLayers.Handler.Hover" }); /* ====================================================================== OpenLayers/Handler/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/RegularPolygon.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/Drag.js */ /** * Class: OpenLayers.Handler.RegularPolygon * Handler to draw a regular polygon on the map. Polygon is displayed on mouse * down, moves or is modified on mouse move, and is finished on mouse up. * The handler triggers callbacks for 'done' and 'cancel'. Create a new * instance with the <OpenLayers.Handler.RegularPolygon> constructor. * * Inherits from: * - <OpenLayers.Handler.Drag> */ OpenLayers.Handler.RegularPolygon = OpenLayers.Class(OpenLayers.Handler.Drag, { /** * APIProperty: sides * {Integer} Number of sides for the regular polygon. Needs to be greater * than 2. Defaults to 4. */ sides: 4, /** * APIProperty: radius * {Float} Optional radius in map units of the regular polygon. If this is * set to some non-zero value, a polygon with a fixed radius will be * drawn and dragged with mose movements. If this property is not * set, dragging changes the radius of the polygon. Set to null by * default. */ radius: null, /** * APIProperty: snapAngle * {Float} If set to a non-zero value, the handler will snap the polygon * rotation to multiples of the snapAngle. Value is an angle measured * in degrees counterclockwise from the positive x-axis. */ snapAngle: null, /** * APIProperty: snapToggle * {String} If set, snapToggle is checked on mouse events and will set * the snap mode to the opposite of what it currently is. To disallow * toggling between snap and non-snap mode, set freehandToggle to * null. Acceptable toggle values are 'shiftKey', 'ctrlKey', and * 'altKey'. Snap mode is only possible if this.snapAngle is set to a * non-zero value. */ snapToggle: 'shiftKey', /** * Property: layerOptions * {Object} Any optional properties to be set on the sketch layer. */ layerOptions: null, /** * APIProperty: persist * {Boolean} Leave the feature rendered until clear is called. Default * is false. If set to true, the feature remains rendered until * clear is called, typically by deactivating the handler or starting * another drawing. */ persist: false, /** * APIProperty: irregular * {Boolean} Draw an irregular polygon instead of a regular polygon. * Default is false. If true, the initial mouse down will represent * one corner of the polygon bounds and with each mouse movement, the * polygon will be stretched so the opposite corner of its bounds * follows the mouse position. This property takes precedence over * the radius property. If set to true, the radius property will * be ignored. */ irregular: false, /** * 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: angle * {Float} The angle from the origin (mouse down) to the current mouse * position, in radians. This is measured counterclockwise from the * positive x-axis. */ angle: null, /** * Property: fixedRadius * {Boolean} The polygon has a fixed radius. True if a radius is set before * drawing begins. False otherwise. */ fixedRadius: false, /** * Property: feature * {<OpenLayers.Feature.Vector>} The currently drawn polygon feature */ feature: null, /** * Property: layer * {<OpenLayers.Layer.Vector>} The temporary drawing layer */ layer: null, /** * Property: origin * {<OpenLayers.Geometry.Point>} Location of the first mouse down */ origin: null, /** * Constructor: OpenLayers.Handler.RegularPolygon * Create a new regular polygon handler. * * Parameters: * control - {<OpenLayers.Control>} The control that owns this handler * callbacks - {Object} An object with a properties whose values are * functions. Various callbacks described below. * options - {Object} An object with properties to be set on the handler. * If the options.sides property is not specified, the number of sides * will default to 4. * * Named callbacks: * create - Called when a sketch is first created. Callback called with * the creation point geometry and sketch feature. * done - Called when the sketch drawing is finished. The callback will * recieve a single argument, the sketch 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.Drag.prototype.initialize.apply(this, [control, callbacks, options]); this.options = (options) ? options : {}; }, /** * APIMethod: setOptions * * Parameters: * newOptions - {Object} */ setOptions: function (newOptions) { OpenLayers.Util.extend(this.options, newOptions); OpenLayers.Util.extend(this, newOptions); }, /** * APIMethod: activate * Turn on the handler. * * Returns: * {Boolean} The handler was successfully activated */ activate: function() { var activated = false; if(OpenLayers.Handler.Drag.prototype.activate.apply(this, arguments)) { // create temporary vector layer for rendering geometry sketch 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); activated = true; } return activated; }, /** * APIMethod: deactivate * Turn off the handler. * * Returns: * {Boolean} The handler was successfully deactivated */ deactivate: function() { var deactivated = false; if(OpenLayers.Handler.Drag.prototype.deactivate.apply(this, arguments)) { // call the cancel callback if mid-drawing if(this.dragging) { this.cancel(); } // If a layer's map property is set to null, it means that that // layer isn't added to the map. Since we ourself added the layer // to the map in activate(), we can assume that if this.layer.map // is null it means that the layer has been destroyed (as a result // of map.destroy() for example. if (this.layer.map != null) { this.layer.destroy(false); if (this.feature) { this.feature.destroy(); } } this.layer = null; this.feature = null; deactivated = true; } return deactivated; }, /** * Method: down * Start drawing a new feature * * Parameters: * evt - {Event} The drag start event */ down: function(evt) { this.fixedRadius = !!(this.radius); var maploc = this.layer.getLonLatFromViewPortPx(evt.xy); this.origin = new OpenLayers.Geometry.Point(maploc.lon, maploc.lat); // create the new polygon if(!this.fixedRadius || this.irregular) { // smallest radius should not be less one pixel in map units // VML doesn't behave well with smaller this.radius = this.map.getResolution(); } if(this.persist) { this.clear(); } this.feature = new OpenLayers.Feature.Vector(); this.createGeometry(); this.callback("create", [this.origin, this.feature]); this.layer.addFeatures([this.feature], {silent: true}); this.layer.drawFeature(this.feature, this.style); }, /** * Method: move * Respond to drag move events * * Parameters: * evt - {Evt} The move event */ move: function(evt) { var maploc = this.layer.getLonLatFromViewPortPx(evt.xy); var point = new OpenLayers.Geometry.Point(maploc.lon, maploc.lat); if(this.irregular) { var ry = Math.sqrt(2) * Math.abs(point.y - this.origin.y) / 2; this.radius = Math.max(this.map.getResolution() / 2, ry); } else if(this.fixedRadius) { this.origin = point; } else { this.calculateAngle(point, evt); this.radius = Math.max(this.map.getResolution() / 2, point.distanceTo(this.origin)); } this.modifyGeometry(); if(this.irregular) { var dx = point.x - this.origin.x; var dy = point.y - this.origin.y; var ratio; if(dy == 0) { ratio = dx / (this.radius * Math.sqrt(2)); } else { ratio = dx / dy; } this.feature.geometry.resize(1, this.origin, ratio); this.feature.geometry.move(dx / 2, dy / 2); } this.layer.drawFeature(this.feature, this.style); }, /** * Method: up * Finish drawing the feature * * Parameters: * evt - {Event} The mouse up event */ up: function(evt) { this.finalize(); // the mouseup method of superclass doesn't call the // "done" callback if there's been no move between // down and up if (this.start == this.last) { this.callback("done", [evt.xy]); } }, /** * Method: out * Finish drawing the feature. * * Parameters: * evt - {Event} The mouse out event */ out: function(evt) { this.finalize(); }, /** * Method: createGeometry * Create the new polygon geometry. This is called at the start of the * drag and at any point during the drag if the number of sides * changes. */ createGeometry: function() { this.angle = Math.PI * ((1/this.sides) - (1/2)); if(this.snapAngle) { this.angle += this.snapAngle * (Math.PI / 180); } this.feature.geometry = OpenLayers.Geometry.Polygon.createRegularPolygon( this.origin, this.radius, this.sides, this.snapAngle ); }, /** * Method: modifyGeometry * Modify the polygon geometry in place. */ modifyGeometry: function() { var angle, point; var ring = this.feature.geometry.components[0]; // if the number of sides ever changes, create a new geometry if(ring.components.length != (this.sides + 1)) { this.createGeometry(); ring = this.feature.geometry.components[0]; } for(var i=0; i<this.sides; ++i) { point = ring.components[i]; angle = this.angle + (i * 2 * Math.PI / this.sides); point.x = this.origin.x + (this.radius * Math.cos(angle)); point.y = this.origin.y + (this.radius * Math.sin(angle)); point.clearBounds(); } }, /** * Method: calculateAngle * Calculate the angle based on settings. * * Parameters: * point - {<OpenLayers.Geometry.Point>} * evt - {Event} */ calculateAngle: function(point, evt) { var alpha = Math.atan2(point.y - this.origin.y, point.x - this.origin.x); if(this.snapAngle && (this.snapToggle && !evt[this.snapToggle])) { var snapAngleRad = (Math.PI / 180) * this.snapAngle; this.angle = Math.round(alpha / snapAngleRad) * snapAngleRad; } else { this.angle = alpha; } }, /** * APIMethod: cancel * Finish the geometry and call the "cancel" callback. */ cancel: function() { // the polygon geometry gets cloned in the callback method this.callback("cancel", null); this.finalize(); }, /** * Method: finalize * Finish the geometry and call the "done" callback. */ finalize: function() { this.origin = null; this.radius = this.options.radius; }, /** * APIMethod: clear * Clear any rendered features on the temporary layer. This is called * when the handler is deactivated, canceled, or done (unless persist * is true). */ clear: function() { if (this.layer) { this.layer.renderer.clear(); this.layer.destroyFeatures(); } }, /** * Method: callback * Trigger the control's named callback with the given arguments * * Parameters: * name - {String} The key for the callback that is one of the properties * of the handler's callbacks object. * args - {Array} An array of arguments with which to call the callback * (defined by the control). */ callback: function (name, args) { // override the callback method to always send the polygon geometry if (this.callbacks[name]) { this.callbacks[name].apply(this.control, [this.feature.geometry.clone()]); } // since sketch features are added to the temporary layer // they must be cleared here if done or cancel if(!this.persist && (name == "done" || name == "cancel")) { this.clear(); } }, CLASS_NAME: "OpenLayers.Handler.RegularPolygon" }); /* ====================================================================== OpenLayers/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/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/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/Handler/Box.js ====================================================================== */ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for * full list of contributors). Published under the 2-clause BSD license. * See license.txt in the OpenLayers distribution or repository for the * full text of the license. */ /** * @requires OpenLayers/Handler.js * @requires OpenLayers/Handler/Drag.js */ /** * Class: OpenLayers.Handler.Box * Handler for dragging a rectangle across the map. Box is displayed * on mouse down, moves on mouse move, and is finished on mouse up. * * Inherits from: * - <OpenLayers.Handler> */ OpenLayers.Handler.Box = OpenLayers.Class(OpenLayers.Handler, { /** * Property: dragHandler * {<OpenLayers.Handler.Drag>} */ dragHandler: null, /** * APIProperty: boxDivClassName * {String} The CSS class to use for drawing the box. Default is * olHandlerBoxZoomBox */ boxDivClassName: 'olHandlerBoxZoomBox', /** * Property: boxOffsets * {Object} Caches box offsets from css. This is used by the getBoxOffsets * method. */ boxOffsets: null, /** * Constructor: OpenLayers.Handler.Box * * Parameters: * control - {<OpenLayers.Control>} * callbacks - {Object} An object with a properties whose values are * functions. Various callbacks described below. * options - {Object} * * Named callbacks: * start - Called when the box drag operation starts. * done - Called when the box drag operation is finished. * The callback should expect to receive a single argument, the box * bounds or a pixel. If the box dragging didn't span more than a 5 * pixel distance, a pixel will be returned instead of a bounds object. */ initialize: function(control, callbacks, options) { OpenLayers.Handler.prototype.initialize.apply(this, arguments); this.dragHandler = new OpenLayers.Handler.Drag( this, { down: this.startBox, move: this.moveBox, out: this.removeBox, up: this.endBox }, {keyMask: this.keyMask} ); }, /** * Method: destroy */ destroy: function() { OpenLayers.Handler.prototype.destroy.apply(this, arguments); if (this.dragHandler) { this.dragHandler.destroy(); this.dragHandler = null; } }, /** * Method: setMap */ setMap: function (map) { OpenLayers.Handler.prototype.setMap.apply(this, arguments); if (this.dragHandler) { this.dragHandler.setMap(map); } }, /** * Method: startBox * * Parameters: * xy - {<OpenLayers.Pixel>} */ startBox: function (xy) { this.callback("start", []); this.zoomBox = OpenLayers.Util.createDiv('zoomBox', { x: -9999, y: -9999 }); this.zoomBox.className = this.boxDivClassName; this.zoomBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1; this.map.viewPortDiv.appendChild(this.zoomBox); OpenLayers.Element.addClass( this.map.viewPortDiv, "olDrawBox" ); }, /** * Method: moveBox */ moveBox: function (xy) { var startX = this.dragHandler.start.x; var startY = this.dragHandler.start.y; var deltaX = Math.abs(startX - xy.x); var deltaY = Math.abs(startY - xy.y); var offset = this.getBoxOffsets(); this.zoomBox.style.width = (deltaX + offset.width + 1) + "px"; this.zoomBox.style.height = (deltaY + offset.height + 1) + "px"; this.zoomBox.style.left = (xy.x < startX ? startX - deltaX - offset.left : startX - offset.left) + "px"; this.zoomBox.style.top = (xy.y < startY ? startY - deltaY - offset.top : startY - offset.top) + "px"; }, /** * Method: endBox */ endBox: function(end) { var result; if (Math.abs(this.dragHandler.start.x - end.x) > 5 || Math.abs(this.dragHandler.start.y - end.y) > 5) { var start = this.dragHandler.start; var top = Math.min(start.y, end.y); var bottom = Math.max(start.y, end.y); var left = Math.min(start.x, end.x); var right = Math.max(start.x, end.x); result = new OpenLayers.Bounds(left, bottom, right, top); } else { result = this.dragHandler.start.clone(); // i.e. OL.Pixel } this.removeBox(); this.callback("done", [result]); }, /** * Method: removeBox * Remove the zoombox from the screen and nullify our reference to it. */ removeBox: function() { this.map.viewPortDiv.removeChild(this.zoomBox); this.zoomBox = null; this.boxOffsets = null; OpenLayers.Element.removeClass( this.map.viewPortDiv, "olDrawBox" ); }, /** * Method: activate */ activate: function () { if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) { this.dragHandler.activate(); return true; } else { return false; } }, /** * Method: deactivate */ deactivate: function () { if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) { if (this.dragHandler.deactivate()) { if (this.zoomBox) { this.removeBox(); } } return true; } else { return false; } }, /** * Method: getBoxOffsets * Determines border offsets for a box, according to the box model. * * Returns: * {Object} an object with the following offsets: * - left * - right * - top * - bottom * - width * - height */ getBoxOffsets: function() { if (!this.boxOffsets) { // Determine the box model. If the testDiv's clientWidth is 3, then // the borders are outside and we are dealing with the w3c box // model. Otherwise, the browser uses the traditional box model and // the borders are inside the box bounds, leaving us with a // clientWidth of 1. var testDiv = document.createElement("div"); //testDiv.style.visibility = "hidden"; testDiv.style.position = "absolute"; testDiv.style.border = "1px solid black"; testDiv.style.width = "3px"; document.body.appendChild(testDiv); var w3cBoxModel = testDiv.clientWidth == 3; document.body.removeChild(testDiv); var left = parseInt(OpenLayers.Element.getStyle(this.zoomBox, "border-left-width")); var right = parseInt(OpenLayers.Element.getStyle( this.zoomBox, "border-right-width")); var top = parseInt(OpenLayers.Element.getStyle(this.zoomBox, "border-top-width")); var bottom = parseInt(OpenLayers.Element.getStyle( this.zoomBox, "border-bottom-width")); this.boxOffsets = { left: left, right: right, top: top, bottom: bottom, width: w3cBoxModel === false ? left + right : 0, height: w3cBoxModel === false ? top + bottom : 0 }; } return this.boxOffsets; }, CLASS_NAME: "OpenLayers.Handler.Box" }); /* ====================================================================== OpenLayers/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/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/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/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/Tile/UTFGrid.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/Format/JSON.js * @requires OpenLayers/Request.js */ /** * Class: OpenLayers.Tile.UTFGrid * Instances of OpenLayers.Tile.UTFGrid are used to manage * UTFGrids. This is an unusual tile type in that it doesn't have a * rendered image; only a 'hit grid' that can be used to * look up feature attributes. * * See the <OpenLayers.Tile.UTFGrid> constructor for details on constructing a * new instance. * * Inherits from: * - <OpenLayers.Tile> */ OpenLayers.Tile.UTFGrid = OpenLayers.Class(OpenLayers.Tile, { /** * Property: url * {String} * The URL of the UTFGrid file being requested. Provided by the <getURL> * method. */ url: null, /** * Property: utfgridResolution * {Number} * Ratio of the pixel width to the width of a UTFGrid data point. If an * entry in the grid represents a 4x4 block of pixels, the * utfgridResolution would be 4. Default is 2. */ utfgridResolution: 2, /** * Property: json * {Object} * Stores the parsed JSON tile data structure. */ json: null, /** * Property: format * {OpenLayers.Format.JSON} * Parser instance used to parse JSON for cross browser support. The native * JSON.parse method will be used where available (all except IE<8). */ format: null, /** * Constructor: OpenLayers.Tile.UTFGrid * Constructor for a new <OpenLayers.Tile.UTFGrid> 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} */ /** * APIMethod: destroy * Clean up. */ destroy: function() { this.clear(); OpenLayers.Tile.prototype.destroy.apply(this, arguments); }, /** * Method: draw * Check that a tile should be drawn, and draw it. * In the case of UTFGrids, "drawing" it means fetching and * parsing the json. * * Returns: * {Boolean} Was a tile drawn? */ draw: function() { var drawn = OpenLayers.Tile.prototype.draw.apply(this, arguments); if (drawn) { if (this.isLoading) { this.abortLoading(); //if we're already loading, send 'reload' instead of 'loadstart'. this.events.triggerEvent("reload"); } else { this.isLoading = true; this.events.triggerEvent("loadstart"); } this.url = this.layer.getURL(this.bounds); if (this.layer.useJSONP) { // Use JSONP method to avoid xbrowser policy var ols = new OpenLayers.Protocol.Script({ url: this.url, callback: function(response) { this.isLoading = false; this.events.triggerEvent("loadend"); this.json = response.data; }, scope: this }); ols.read(); this.request = ols; } else { // Use standard XHR this.request = OpenLayers.Request.GET({ url: this.url, callback: function(response) { this.isLoading = false; this.events.triggerEvent("loadend"); if (response.status === 200) { this.parseData(response.responseText); } }, scope: this }); } } else { this.unload(); } return drawn; }, /** * Method: abortLoading * Cancel a pending request. */ abortLoading: function() { if (this.request) { this.request.abort(); delete this.request; } this.isLoading = false; }, /** * Method: getFeatureInfo * Get feature information associated with a pixel offset. If the pixel * offset corresponds to a feature, the returned object will have id * and data properties. Otherwise, null will be returned. * * * Parameters: * i - {Number} X-axis pixel offset (from top left of tile) * j - {Number} Y-axis pixel offset (from top left of tile) * * Returns: * {Object} Object with feature id and data properties corresponding to the * given pixel offset. */ getFeatureInfo: function(i, j) { var info = null; if (this.json) { var id = this.getFeatureId(i, j); if (id !== null) { info = {id: id, data: this.json.data[id]}; } } return info; }, /** * Method: getFeatureId * Get the identifier for the feature associated with a pixel offset. * * Parameters: * i - {Number} X-axis pixel offset (from top left of tile) * j - {Number} Y-axis pixel offset (from top left of tile) * * Returns: * {Object} The feature identifier corresponding to the given pixel offset. * Returns null if pixel doesn't correspond to a feature. */ getFeatureId: function(i, j) { var id = null; if (this.json) { var resolution = this.utfgridResolution; var row = Math.floor(j / resolution); var col = Math.floor(i / resolution); var charCode = this.json.grid[row].charCodeAt(col); var index = this.indexFromCharCode(charCode); var keys = this.json.keys; if (!isNaN(index) && (index in keys)) { id = keys[index]; } } return id; }, /** * Method: indexFromCharCode * Given a character code for one of the UTFGrid "grid" characters, * resolve the integer index for the feature id in the UTFGrid "keys" * array. * * Parameters: * charCode - {Integer} * * Returns: * {Integer} Index for the feature id from the keys array. */ indexFromCharCode: function(charCode) { if (charCode >= 93) { charCode--; } if (charCode >= 35) { charCode --; } return charCode - 32; }, /** * Method: parseData * Parse the JSON from a request * * Parameters: * str - {String} UTFGrid as a JSON string. * * Returns: * {Object} parsed javascript data */ parseData: function(str) { if (!this.format) { this.format = new OpenLayers.Format.JSON(); } this.json = this.format.read(str); }, /** * Method: clear * Delete data stored with this tile. */ clear: function() { this.json = null; }, CLASS_NAME: "OpenLayers.Tile.UTFGrid" }); /* ====================================================================== OpenLayers/Tile/Image/IFrame.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/Image.js */ /** * Constant: OpenLayers.Tile.Image.IFrame * Mixin for tiles that use form-encoded POST requests to get images from * remote services. Images will be loaded using HTTP-POST into an IFrame. * * This mixin will be applied to <OpenLayers.Tile.Image> instances * configured with <OpenLayers.Tile.Image.maxGetUrlLength> set. */ OpenLayers.Tile.Image.IFrame = { /** * Property: useIFrame * {Boolean} true if we are currently using an IFrame to render POST * responses, false if we are using an img element to render GET responses. */ useIFrame: null, /** * Property: blankImageUrl * {String} Using a data scheme url is not supported by all browsers, but * we don't care because we either set it as css backgroundImage, or the * image's display style is set to "none" when we use it. */ blankImageUrl: "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAQAIBRAA7", /** * Method: draw * Set useIFrame in the instance, and operate the image/iframe switch. * Then call Tile.Image.draw. * * Returns: * {Boolean} */ draw: function() { var draw = OpenLayers.Tile.Image.prototype.shouldDraw.call(this); if(draw) { // this.url isn't set to the currect value yet, so we call getURL // on the layer and store the result in a local variable var url = this.layer.getURL(this.bounds); var usedIFrame = this.useIFrame; this.useIFrame = this.maxGetUrlLength !== null && !this.layer.async && url.length > this.maxGetUrlLength; var fromIFrame = usedIFrame && !this.useIFrame; var toIFrame = !usedIFrame && this.useIFrame; if(fromIFrame || toIFrame) { // Switching between GET (image) and POST (iframe). // We remove the imgDiv (really either an image or an iframe) // from the frame and set it to null to make sure initImage // will call getImage. if(this.imgDiv && this.imgDiv.parentNode === this.frame) { this.frame.removeChild(this.imgDiv); } this.imgDiv = null; // And if we had an iframe we also remove the event pane. if(fromIFrame) { this.frame.removeChild(this.frame.firstChild); } } } return OpenLayers.Tile.Image.prototype.draw.apply(this, arguments); }, /** * Method: getImage * Creates the content for the frame on the tile. */ getImage: function() { if (this.useIFrame === true) { if (!this.frame.childNodes.length) { var eventPane = document.createElement("div"), style = eventPane.style; style.position = "absolute"; style.width = "100%"; style.height = "100%"; style.zIndex = 1; style.backgroundImage = "url(" + this.blankImageUrl + ")"; this.frame.appendChild(eventPane); } var id = this.id + '_iFrame', iframe; if (parseFloat(navigator.appVersion.split("MSIE")[1]) < 9) { // Older IE versions do not set the name attribute of an iFrame // properly via DOM manipulation, so we need to do it on our own with // this hack. iframe = document.createElement('<iframe name="'+id+'">'); // IFrames in older IE versions are not transparent, if you set // the backgroundColor transparent. This is a workaround to get // transparent iframes. iframe.style.backgroundColor = '#FFFFFF'; iframe.style.filter = 'chroma(color=#FFFFFF)'; } else { iframe = document.createElement('iframe'); iframe.style.backgroundColor = 'transparent'; // iframe.name needs to be an unique id, otherwise it // could happen that other iframes are overwritten. iframe.name = id; } // some special properties to avoid scaling the images and scrollbars // in the iframe iframe.scrolling = 'no'; iframe.marginWidth = '0px'; iframe.marginHeight = '0px'; iframe.frameBorder = '0'; iframe.style.position = "absolute"; iframe.style.width = "100%"; iframe.style.height = "100%"; if (this.layer.opacity < 1) { OpenLayers.Util.modifyDOMElement(iframe, null, null, null, null, null, null, this.layer.opacity); } this.frame.appendChild(iframe); this.imgDiv = iframe; return iframe; } else { return OpenLayers.Tile.Image.prototype.getImage.apply(this, arguments); } }, /** * Method: createRequestForm * Create the html <form> element with width, height, bbox and all * parameters specified in the layer params. * * Returns: * {DOMElement} The form element which sends the HTTP-POST request to the * WMS. */ createRequestForm: function() { // creation of the form element var form = document.createElement('form'); form.method = 'POST'; var cacheId = this.layer.params["_OLSALT"]; cacheId = (cacheId ? cacheId + "_" : "") + this.bounds.toBBOX(); form.action = OpenLayers.Util.urlAppend(this.layer.url, cacheId); form.target = this.id + '_iFrame'; // adding all parameters in layer params as hidden fields to the html // form element var imageSize = this.layer.getImageSize(), params = OpenLayers.Util.getParameters(this.url), field; for(var par in params) { field = document.createElement('input'); field.type = 'hidden'; field.name = par; field.value = params[par]; form.appendChild(field); } return form; }, /** * Method: setImgSrc * Sets the source for the tile image * * Parameters: * url - {String} */ setImgSrc: function(url) { if (this.useIFrame === true) { if (url) { var form = this.createRequestForm(); this.frame.appendChild(form); form.submit(); this.frame.removeChild(form); } else if (this.imgDiv.parentNode === this.frame) { // we don't reuse iframes to avoid caching issues this.frame.removeChild(this.imgDiv); this.imgDiv = null; } } else { OpenLayers.Tile.Image.prototype.setImgSrc.apply(this, arguments); } }, /** * Method: onImageLoad * Handler for the image onload event */ onImageLoad: function() { //TODO de-uglify opacity handling OpenLayers.Tile.Image.prototype.onImageLoad.apply(this, arguments); if (this.useIFrame === true) { this.imgDiv.style.opacity = 1; this.frame.style.opacity = this.layer.opacity; } }, /** * Method: createBackBuffer * Override createBackBuffer to do nothing when we use an iframe. Moving an * iframe from one element to another makes it necessary to reload the iframe * because its content is lost. So we just give up. * * Returns: * {DOMElement} */ createBackBuffer: function() { var backBuffer; if(this.useIFrame === false) { backBuffer = OpenLayers.Tile.Image.prototype.createBackBuffer.call(this); } return backBuffer; } }; /* ====================================================================== 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/Strategy/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/Strategy.js * @requires OpenLayers/Filter.js */ /** * Class: OpenLayers.Strategy.Filter * Strategy for limiting features that get added to a layer by * evaluating a filter. The strategy maintains a cache of * all features until removeFeatures is called on the layer. * * Inherits from: * - <OpenLayers.Strategy> */ OpenLayers.Strategy.Filter = OpenLayers.Class(OpenLayers.Strategy, { /** * APIProperty: filter * {<OpenLayers.Filter>} Filter for limiting features sent to the layer. * Use the <setFilter> method to update this filter after construction. */ filter: null, /** * Property: cache * {Array(<OpenLayers.Feature.Vector>)} List of currently cached * features. */ cache: null, /** * Property: caching * {Boolean} The filter is currently caching features. */ caching: false, /** * Constructor: OpenLayers.Strategy.Filter * Create a new filter strategy. * * Parameters: * options - {Object} Optional object whose properties will be set on the * instance. */ /** * APIMethod: activate * Activate the strategy. Register any listeners, do appropriate setup. * By default, this strategy automatically activates itself when a layer * is added to a map. * * 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.cache = []; this.layer.events.on({ "beforefeaturesadded": this.handleAdd, "beforefeaturesremoved": this.handleRemove, scope: this }); } return activated; }, /** * APIMethod: deactivate * Deactivate the strategy. Clear the feature cache. * * Returns: * {Boolean} True if the strategy was successfully deactivated or false if * the strategy was already inactive. */ deactivate: function() { this.cache = null; if (this.layer && this.layer.events) { this.layer.events.un({ "beforefeaturesadded": this.handleAdd, "beforefeaturesremoved": this.handleRemove, scope: this }); } return OpenLayers.Strategy.prototype.deactivate.apply(this, arguments); }, /** * Method: handleAdd */ handleAdd: function(event) { if (!this.caching && this.filter) { var features = event.features; event.features = []; var feature; for (var i=0, ii=features.length; i<ii; ++i) { feature = features[i]; if (this.filter.evaluate(feature)) { event.features.push(feature); } else { this.cache.push(feature); } } } }, /** * Method: handleRemove */ handleRemove: function(event) { if (!this.caching) { this.cache = []; } }, /** * APIMethod: setFilter * Update the filter for this strategy. This will re-evaluate * any features on the layer and in the cache. Only features * for which filter.evalute(feature) returns true will be * added to the layer. Others will be cached by the strategy. * * Parameters: * filter - {<OpenLayers.Filter>} A filter for evaluating features. */ setFilter: function(filter) { this.filter = filter; var previousCache = this.cache; this.cache = []; // look through layer for features to remove from layer this.handleAdd({features: this.layer.features}); // cache now contains features to remove from layer if (this.cache.length > 0) { this.caching = true; this.layer.removeFeatures(this.cache.slice()); this.caching = false; } // now look through previous cache for features to add to layer if (previousCache.length > 0) { var event = {features: previousCache}; this.handleAdd(event); if (event.features.length > 0) { // event has features to add to layer this.caching = true; this.layer.addFeatures(event.features); this.caching = false; } } }, CLASS_NAME: "OpenLayers.Strategy.Filter" }); /* ====================================================================== OpenLayers/Strategy/Paging.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.Paging * Strategy for vector feature paging * * Inherits from: * - <OpenLayers.Strategy> */ OpenLayers.Strategy.Paging = OpenLayers.Class(OpenLayers.Strategy, { /** * Property: features * {Array(<OpenLayers.Feature.Vector>)} Cached features. */ features: null, /** * Property: length * {Integer} Number of features per page. Default is 10. */ length: 10, /** * Property: num * {Integer} The currently displayed page number. */ num: null, /** * Property: paging * {Boolean} The strategy is currently changing pages. */ paging: false, /** * Constructor: OpenLayers.Strategy.Paging * Create a new paging strategy. * * Parameters: * options - {Object} Optional object whose properties will be set on the * instance. */ /** * APIMethod: activate * Activate the strategy. Register any listeners, do appropriate setup. * * Returns: * {Boolean} The strategy was successfully activated. */ activate: function() { var activated = OpenLayers.Strategy.prototype.activate.call(this); if(activated) { this.layer.events.on({ "beforefeaturesadded": this.cacheFeatures, scope: this }); } return activated; }, /** * APIMethod: deactivate * Deactivate the strategy. Unregister any listeners, do appropriate * tear-down. * * Returns: * {Boolean} The strategy was successfully deactivated. */ deactivate: function() { var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this); if(deactivated) { this.clearCache(); this.layer.events.un({ "beforefeaturesadded": this.cacheFeatures, scope: this }); } return deactivated; }, /** * Method: cacheFeatures * Cache features before they are added to the layer. * * Parameters: * event - {Object} The event that this was listening for. This will come * with a batch of features to be paged. */ cacheFeatures: function(event) { if(!this.paging) { this.clearCache(); this.features = event.features; this.pageNext(event); } }, /** * Method: clearCache * Clear out the cached features. This destroys features, assuming * nothing else has a reference. */ clearCache: function() { if(this.features) { for(var i=0; i<this.features.length; ++i) { this.features[i].destroy(); } } this.features = null; this.num = null; }, /** * APIMethod: pageCount * Get the total count of pages given the current cache of features. * * Returns: * {Integer} The page count. */ pageCount: function() { var numFeatures = this.features ? this.features.length : 0; return Math.ceil(numFeatures / this.length); }, /** * APIMethod: pageNum * Get the zero based page number. * * Returns: * {Integer} The current page number being displayed. */ pageNum: function() { return this.num; }, /** * APIMethod: pageLength * Gets or sets page length. * * Parameters: * newLength - {Integer} Optional length to be set. * * Returns: * {Integer} The length of a page (number of features per page). */ pageLength: function(newLength) { if(newLength && newLength > 0) { this.length = newLength; } return this.length; }, /** * APIMethod: pageNext * Display the next page of features. * * Returns: * {Boolean} A new page was displayed. */ pageNext: function(event) { var changed = false; if(this.features) { if(this.num === null) { this.num = -1; } var start = (this.num + 1) * this.length; changed = this.page(start, event); } return changed; }, /** * APIMethod: pagePrevious * Display the previous page of features. * * Returns: * {Boolean} A new page was displayed. */ pagePrevious: function() { var changed = false; if(this.features) { if(this.num === null) { this.num = this.pageCount(); } var start = (this.num - 1) * this.length; changed = this.page(start); } return changed; }, /** * Method: page * Display the page starting at the given index from the cache. * * Returns: * {Boolean} A new page was displayed. */ page: function(start, event) { var changed = false; if(this.features) { if(start >= 0 && start < this.features.length) { var num = Math.floor(start / this.length); if(num != this.num) { this.paging = true; var features = this.features.slice(start, start + this.length); this.layer.removeFeatures(this.layer.features); this.num = num; // modify the event if any if(event && event.features) { // this.was called by an event listener event.features = features; } else { // this was called directly on the strategy this.layer.addFeatures(features); } this.paging = false; changed = true; } } } return changed; }, CLASS_NAME: "OpenLayers.Strategy.Paging" }); /* ====================================================================== OpenLayers/Strategy/BBOX.js ====================================================================== */ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for * full list of contributors). Published under the 2-clause BSD license. * See license.txt in the OpenLayers distribution or repository for the * full text of the license. */ /** * @requires OpenLayers/Strategy.js * @requires OpenLayers/Filter/Spatial.js */ /** * Class: OpenLayers.Strategy.BBOX * A simple strategy that reads new features when the viewport invalidates * some bounds. * * Inherits from: * - <OpenLayers.Strategy> */ OpenLayers.Strategy.BBOX = OpenLayers.Class(OpenLayers.Strategy, { /** * Property: bounds * {<OpenLayers.Bounds>} The current data bounds (in the same projection * as the layer - not always the same projection as the map). */ bounds: null, /** * Property: resolution * {Float} The current data resolution. */ resolution: null, /** * APIProperty: ratio * {Float} The ratio of the data bounds to the viewport bounds (in each * dimension). Default is 2. */ ratio: 2, /** * Property: resFactor * {Float} Optional factor used to determine when previously requested * features are invalid. If set, the resFactor will be compared to the * resolution of the previous request to the current map resolution. * If resFactor > (old / new) and 1/resFactor < (old / new). If you * set a resFactor of 1, data will be requested every time the * resolution changes. If you set a resFactor of 3, data will be * requested if the old resolution is 3 times the new, or if the new is * 3 times the old. If the old bounds do not contain the new bounds * new data will always be requested (with or without considering * resFactor). */ resFactor: null, /** * Property: response * {<OpenLayers.Protocol.Response>} The protocol response object returned * by the layer protocol. */ response: null, /** * Constructor: OpenLayers.Strategy.BBOX * Create a new BBOX strategy. * * Parameters: * options - {Object} Optional object whose properties will be set on the * instance. */ /** * Method: activate * Set up strategy with regard to reading new batches of remote data. * * Returns: * {Boolean} The strategy was successfully activated. */ activate: function() { var activated = OpenLayers.Strategy.prototype.activate.call(this); if(activated) { this.layer.events.on({ "moveend": this.update, "refresh": this.update, "visibilitychanged": this.update, scope: this }); this.update(); } return activated; }, /** * Method: deactivate * Tear down strategy with regard to reading new batches of remote data. * * Returns: * {Boolean} The strategy was successfully deactivated. */ deactivate: function() { var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this); if(deactivated) { this.layer.events.un({ "moveend": this.update, "refresh": this.update, "visibilitychanged": this.update, scope: this }); } return deactivated; }, /** * Method: update * Callback function called on "moveend" or "refresh" layer events. * * Parameters: * options - {Object} Optional object whose properties will determine * the behaviour of this Strategy * * Valid options include: * force - {Boolean} if true, new data must be unconditionally read. * noAbort - {Boolean} if true, do not abort previous requests. */ update: function(options) { var mapBounds = this.getMapBounds(); if (mapBounds !== null && ((options && options.force) || (this.layer.visibility && this.layer.calculateInRange() && this.invalidBounds(mapBounds)))) { this.calculateBounds(mapBounds); this.resolution = this.layer.map.getResolution(); this.triggerRead(options); } }, /** * Method: getMapBounds * Get the map bounds expressed in the same projection as this layer. * * Returns: * {<OpenLayers.Bounds>} Map bounds in the projection of the layer. */ getMapBounds: function() { if (this.layer.map === null) { return null; } var bounds = this.layer.map.getExtent(); if(bounds && !this.layer.projection.equals( this.layer.map.getProjectionObject())) { bounds = bounds.clone().transform( this.layer.map.getProjectionObject(), this.layer.projection ); } return bounds; }, /** * Method: invalidBounds * Determine whether the previously requested set of features is invalid. * This occurs when the new map bounds do not contain the previously * requested bounds. In addition, if <resFactor> is set, it will be * considered. * * Parameters: * mapBounds - {<OpenLayers.Bounds>} the current map extent, will be * retrieved from the map object if not provided * * Returns: * {Boolean} */ invalidBounds: function(mapBounds) { if(!mapBounds) { mapBounds = this.getMapBounds(); } var invalid = !this.bounds || !this.bounds.containsBounds(mapBounds); if(!invalid && this.resFactor) { var ratio = this.resolution / this.layer.map.getResolution(); invalid = (ratio >= this.resFactor || ratio <= (1 / this.resFactor)); } return invalid; }, /** * Method: calculateBounds * * Parameters: * mapBounds - {<OpenLayers.Bounds>} the current map extent, will be * retrieved from the map object if not provided */ calculateBounds: function(mapBounds) { if(!mapBounds) { mapBounds = this.getMapBounds(); } var center = mapBounds.getCenterLonLat(); var dataWidth = mapBounds.getWidth() * this.ratio; var dataHeight = mapBounds.getHeight() * this.ratio; this.bounds = new OpenLayers.Bounds( center.lon - (dataWidth / 2), center.lat - (dataHeight / 2), center.lon + (dataWidth / 2), center.lat + (dataHeight / 2) ); }, /** * Method: triggerRead * * Parameters: * options - {Object} Additional options for the protocol's read method * (optional) * * Returns: * {<OpenLayers.Protocol.Response>} The protocol response object * returned by the layer protocol. */ triggerRead: function(options) { if (this.response && !(options && options.noAbort === true)) { this.layer.protocol.abort(this.response); this.layer.events.triggerEvent("loadend"); } var evt = {filter: this.createFilter()}; this.layer.events.triggerEvent("loadstart", evt); this.response = this.layer.protocol.read( OpenLayers.Util.applyDefaults({ filter: evt.filter, callback: this.merge, scope: this }, options)); }, /** * Method: createFilter * Creates a spatial BBOX filter. If the layer that this strategy belongs * to has a filter property, this filter will be combined with the BBOX * filter. * * Returns * {<OpenLayers.Filter>} The filter object. */ createFilter: function() { var filter = new OpenLayers.Filter.Spatial({ type: OpenLayers.Filter.Spatial.BBOX, value: this.bounds, projection: this.layer.projection }); if (this.layer.filter) { filter = new OpenLayers.Filter.Logical({ type: OpenLayers.Filter.Logical.AND, filters: [this.layer.filter, filter] }); } return filter; }, /** * Method: merge * Given a list of features, determine which ones to add to the layer. * If the layer projection differs from the map projection, features * will be transformed from the layer projection to the map projection. * * Parameters: * resp - {<OpenLayers.Protocol.Response>} The response object passed * by the protocol. */ merge: function(resp) { this.layer.destroyFeatures(); if (resp.success()) { var features = resp.features; if(features && features.length > 0) { var remote = this.layer.projection; var local = this.layer.map.getProjectionObject(); if(!local.equals(remote)) { var geom; for(var i=0, len=features.length; i<len; ++i) { geom = features[i].geometry; if(geom) { geom.transform(remote, local); } } } this.layer.addFeatures(features); } } else { this.bounds = null; } this.response = null; this.layer.events.triggerEvent("loadend", {response: resp}); }, CLASS_NAME: "OpenLayers.Strategy.BBOX" }); /* ====================================================================== OpenLayers/Strategy/Save.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.Save * A strategy that commits newly created or modified features. By default * the strategy waits for a call to <save> before persisting changes. By * configuring the strategy with the <auto> option, changes can be saved * automatically. * * Inherits from: * - <OpenLayers.Strategy> */ OpenLayers.Strategy.Save = OpenLayers.Class(OpenLayers.Strategy, { /** * APIProperty: events * {<OpenLayers.Events>} An events object that handles all * events on the strategy object. * * Register a listener for a particular event with the following syntax: * (code) * strategy.events.register(type, obj, listener); * (end) * * Supported event types: * start - Triggered before saving * success - Triggered after a successful transaction * fail - Triggered after a failed transaction * */ /** * Property: events * {<OpenLayers.Events>} Events instance for triggering this protocol * events. */ events: null, /** * APIProperty: auto * {Boolean | Number} Auto-save. Default is false. If true, features will be * saved immediately after being added to the layer and with each * modification or deletion. If auto is a number, features will be * saved on an interval provided by the value (in seconds). */ auto: false, /** * Property: timer * {Number} The id of the timer. */ timer: null, /** * Constructor: OpenLayers.Strategy.Save * Create a new Save strategy. * * Parameters: * options - {Object} Optional object whose properties will be set on the * instance. */ initialize: function(options) { OpenLayers.Strategy.prototype.initialize.apply(this, [options]); this.events = new OpenLayers.Events(this); }, /** * APIMethod: activate * Activate the strategy. Register any listeners, do appropriate setup. * * Returns: * {Boolean} The strategy was successfully activated. */ activate: function() { var activated = OpenLayers.Strategy.prototype.activate.call(this); if(activated) { if(this.auto) { if(typeof this.auto === "number") { this.timer = window.setInterval( OpenLayers.Function.bind(this.save, this), this.auto * 1000 ); } else { this.layer.events.on({ "featureadded": this.triggerSave, "afterfeaturemodified": this.triggerSave, scope: this }); } } } return activated; }, /** * APIMethod: deactivate * Deactivate the strategy. Unregister any listeners, do appropriate * tear-down. * * Returns: * {Boolean} The strategy was successfully deactivated. */ deactivate: function() { var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this); if(deactivated) { if(this.auto) { if(typeof this.auto === "number") { window.clearInterval(this.timer); } else { this.layer.events.un({ "featureadded": this.triggerSave, "afterfeaturemodified": this.triggerSave, scope: this }); } } } return deactivated; }, /** * Method: triggerSave * Registered as a listener. Calls save if a feature has insert, update, * or delete state. * * Parameters: * event - {Object} The event this function is listening for. */ triggerSave: function(event) { var feature = event.feature; if(feature.state === OpenLayers.State.INSERT || feature.state === OpenLayers.State.UPDATE || feature.state === OpenLayers.State.DELETE) { this.save([event.feature]); } }, /** * APIMethod: save * Tell the layer protocol to commit unsaved features. If the layer * projection differs from the map projection, features will be * transformed into the layer projection before being committed. * * Parameters: * features - {Array} Features to be saved. If null, then default is all * features in the layer. Features are assumed to be in the map * projection. */ save: function(features) { if(!features) { features = this.layer.features; } this.events.triggerEvent("start", {features:features}); var remote = this.layer.projection; var local = this.layer.map.getProjectionObject(); if(!local.equals(remote)) { var len = features.length; var clones = new Array(len); var orig, clone; for(var i=0; i<len; ++i) { orig = features[i]; clone = orig.clone(); clone.fid = orig.fid; clone.state = orig.state; if(orig.url) { clone.url = orig.url; } clone._original = orig; clone.geometry.transform(local, remote); clones[i] = clone; } features = clones; } this.layer.protocol.commit(features, { callback: this.onCommit, scope: this }); }, /** * Method: onCommit * Called after protocol commit. * * Parameters: * response - {<OpenLayers.Protocol.Response>} A response object. */ onCommit: function(response) { var evt = {"response": response}; if(response.success()) { var features = response.reqFeatures; // deal with inserts, updates, and deletes var state, feature; var destroys = []; var insertIds = response.insertIds || []; var j = 0; for(var i=0, len=features.length; i<len; ++i) { feature = features[i]; // if projection was different, we may be dealing with clones feature = feature._original || feature; state = feature.state; if(state) { if(state == OpenLayers.State.DELETE) { destroys.push(feature); } else if(state == OpenLayers.State.INSERT) { feature.fid = insertIds[j]; ++j; } feature.state = null; } } if(destroys.length > 0) { this.layer.destroyFeatures(destroys); } this.events.triggerEvent("success", evt); } else { this.events.triggerEvent("fail", evt); } }, CLASS_NAME: "OpenLayers.Strategy.Save" }); /* ====================================================================== OpenLayers/Strategy/Refresh.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.Refresh * A strategy that refreshes the layer. By default the strategy waits for a * call to <refresh> before refreshing. By configuring the strategy with * the <interval> option, refreshing can take place automatically. * * Inherits from: * - <OpenLayers.Strategy> */ OpenLayers.Strategy.Refresh = OpenLayers.Class(OpenLayers.Strategy, { /** * Property: force * {Boolean} Force a refresh on the layer. Default is false. */ force: false, /** * Property: interval * {Number} Auto-refresh. Default is 0. If > 0, layer will be refreshed * every N milliseconds. */ interval: 0, /** * Property: timer * {Number} The id of the timer. */ timer: null, /** * Constructor: OpenLayers.Strategy.Refresh * Create a new Refresh strategy. * * Parameters: * options - {Object} Optional object whose properties will be set on the * instance. */ /** * APIMethod: activate * Activate the strategy. Register any listeners, do appropriate setup. * * Returns: * {Boolean} True if the strategy was successfully activated. */ activate: function() { var activated = OpenLayers.Strategy.prototype.activate.call(this); if(activated) { if(this.layer.visibility === true) { this.start(); } this.layer.events.on({ "visibilitychanged": this.reset, scope: this }); } return activated; }, /** * APIMethod: deactivate * Deactivate the strategy. Unregister any listeners, do appropriate * tear-down. * * Returns: * {Boolean} True if the strategy was successfully deactivated. */ deactivate: function() { var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this); if(deactivated) { this.stop(); this.layer.events.un({ "visibilitychanged": this.reset, scope: this }); } return deactivated; }, /** * Method: reset * Start or cancel the refresh interval depending on the visibility of * the layer. */ reset: function() { if(this.layer.visibility === true) { this.start(); } else { this.stop(); } }, /** * Method: start * Start the refresh interval. */ start: function() { if(this.interval && typeof this.interval === "number" && this.interval > 0) { this.timer = window.setInterval( OpenLayers.Function.bind(this.refresh, this), this.interval); } }, /** * APIMethod: refresh * Tell the strategy to refresh which will refresh the layer. */ refresh: function() { if (this.layer && this.layer.refresh && typeof this.layer.refresh == "function") { this.layer.refresh({force: this.force}); } }, /** * Method: stop * Cancels the refresh interval. */ stop: function() { if(this.timer !== null) { window.clearInterval(this.timer); this.timer = null; } }, CLASS_NAME: "OpenLayers.Strategy.Refresh" }); /* ====================================================================== OpenLayers/Strategy/Cluster.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.Cluster * Strategy for vector feature clustering. * * Inherits from: * - <OpenLayers.Strategy> */ OpenLayers.Strategy.Cluster = OpenLayers.Class(OpenLayers.Strategy, { /** * APIProperty: distance * {Integer} Pixel distance between features that should be considered a * single cluster. Default is 20 pixels. */ distance: 20, /** * APIProperty: threshold * {Integer} Optional threshold below which original features will be * added to the layer instead of clusters. For example, a threshold * of 3 would mean that any time there are 2 or fewer features in * a cluster, those features will be added directly to the layer instead * of a cluster representing those features. Default is null (which is * equivalent to 1 - meaning that clusters may contain just one feature). */ threshold: null, /** * Property: features * {Array(<OpenLayers.Feature.Vector>)} Cached features. */ features: null, /** * Property: clusters * {Array(<OpenLayers.Feature.Vector>)} Calculated clusters. */ clusters: null, /** * Property: clustering * {Boolean} The strategy is currently clustering features. */ clustering: false, /** * Property: resolution * {Float} The resolution (map units per pixel) of the current cluster set. */ resolution: null, /** * Constructor: OpenLayers.Strategy.Cluster * Create a new clustering strategy. * * Parameters: * options - {Object} Optional object whose properties will be set on the * instance. */ /** * APIMethod: activate * Activate the strategy. Register any listeners, do appropriate setup. * * Returns: * {Boolean} The strategy was successfully activated. */ activate: function() { var activated = OpenLayers.Strategy.prototype.activate.call(this); if(activated) { this.layer.events.on({ "beforefeaturesadded": this.cacheFeatures, "featuresremoved": this.clearCache, "moveend": this.cluster, scope: this }); } return activated; }, /** * APIMethod: deactivate * Deactivate the strategy. Unregister any listeners, do appropriate * tear-down. * * Returns: * {Boolean} The strategy was successfully deactivated. */ deactivate: function() { var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this); if(deactivated) { this.clearCache(); this.layer.events.un({ "beforefeaturesadded": this.cacheFeatures, "featuresremoved": this.clearCache, "moveend": this.cluster, scope: this }); } return deactivated; }, /** * Method: cacheFeatures * Cache features before they are added to the layer. * * Parameters: * event - {Object} The event that this was listening for. This will come * with a batch of features to be clustered. * * Returns: * {Boolean} False to stop features from being added to the layer. */ cacheFeatures: function(event) { var propagate = true; if(!this.clustering) { this.clearCache(); this.features = event.features; this.cluster(); propagate = false; } return propagate; }, /** * Method: clearCache * Clear out the cached features. */ clearCache: function() { if(!this.clustering) { this.features = null; } }, /** * Method: cluster * Cluster features based on some threshold distance. * * Parameters: * event - {Object} The event received when cluster is called as a * result of a moveend event. */ cluster: function(event) { if((!event || event.zoomChanged) && this.features) { var resolution = this.layer.map.getResolution(); if(resolution != this.resolution || !this.clustersExist()) { this.resolution = resolution; var clusters = []; var feature, clustered, cluster; for(var i=0; i<this.features.length; ++i) { feature = this.features[i]; if(feature.geometry) { clustered = false; for(var j=clusters.length-1; j>=0; --j) { cluster = clusters[j]; if(this.shouldCluster(cluster, feature)) { this.addToCluster(cluster, feature); clustered = true; break; } } if(!clustered) { clusters.push(this.createCluster(this.features[i])); } } } this.clustering = true; this.layer.removeAllFeatures(); this.clustering = false; if(clusters.length > 0) { if(this.threshold > 1) { var clone = clusters.slice(); clusters = []; var candidate; for(var i=0, len=clone.length; i<len; ++i) { candidate = clone[i]; if(candidate.attributes.count < this.threshold) { Array.prototype.push.apply(clusters, candidate.cluster); } else { clusters.push(candidate); } } } this.clustering = true; // A legitimate feature addition could occur during this // addFeatures call. For clustering to behave well, features // should be removed from a layer before requesting a new batch. this.layer.addFeatures(clusters); this.clustering = false; } this.clusters = clusters; } } }, /** * Method: clustersExist * Determine whether calculated clusters are already on the layer. * * Returns: * {Boolean} The calculated clusters are already on the layer. */ clustersExist: function() { var exist = false; if(this.clusters && this.clusters.length > 0 && this.clusters.length == this.layer.features.length) { exist = true; for(var i=0; i<this.clusters.length; ++i) { if(this.clusters[i] != this.layer.features[i]) { exist = false; break; } } } return exist; }, /** * Method: shouldCluster * Determine whether to include a feature in a given cluster. * * Parameters: * cluster - {<OpenLayers.Feature.Vector>} A cluster. * feature - {<OpenLayers.Feature.Vector>} A feature. * * Returns: * {Boolean} The feature should be included in the cluster. */ shouldCluster: function(cluster, feature) { var cc = cluster.geometry.getBounds().getCenterLonLat(); var fc = feature.geometry.getBounds().getCenterLonLat(); var distance = ( Math.sqrt( Math.pow((cc.lon - fc.lon), 2) + Math.pow((cc.lat - fc.lat), 2) ) / this.resolution ); return (distance <= this.distance); }, /** * Method: addToCluster * Add a feature to a cluster. * * Parameters: * cluster - {<OpenLayers.Feature.Vector>} A cluster. * feature - {<OpenLayers.Feature.Vector>} A feature. */ addToCluster: function(cluster, feature) { cluster.cluster.push(feature); cluster.attributes.count += 1; }, /** * Method: createCluster * Given a feature, create a cluster. * * Parameters: * feature - {<OpenLayers.Feature.Vector>} * * Returns: * {<OpenLayers.Feature.Vector>} A cluster. */ createCluster: function(feature) { var center = feature.geometry.getBounds().getCenterLonLat(); var cluster = new OpenLayers.Feature.Vector( new OpenLayers.Geometry.Point(center.lon, center.lat), {count: 1} ); cluster.cluster = [feature]; return cluster; }, CLASS_NAME: "OpenLayers.Strategy.Cluster" }); /* ====================================================================== 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/Events/featureclick.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.featureclick * * Extension event type for handling feature click events, including overlapping * features. * * Event types provided by this extension: * - featureclick */ OpenLayers.Events.featureclick = OpenLayers.Class({ /** * Property: cache * {Object} A cache of features under the mouse. */ cache: null, /** * Property: map * {<OpenLayers.Map>} The map to register browser events on. */ map: null, /** * Property: provides * {Array(String)} The event types provided by this extension. */ provides: ["featureclick", "nofeatureclick", "featureover", "featureout"], /** * Constructor: OpenLayers.Events.featureclick * Create a new featureclick event type. * * Parameters: * target - {<OpenLayers.Events>} The events instance to create the events * for. */ initialize: function(target) { this.target = target; if (target.object instanceof OpenLayers.Map) { this.setMap(target.object); } else if (target.object instanceof OpenLayers.Layer.Vector) { if (target.object.map) { this.setMap(target.object.map); } else { target.object.events.register("added", this, function(evt) { this.setMap(target.object.map); }); } } else { throw("Listeners for '" + this.provides.join("', '") + "' events can only be registered for OpenLayers.Layer.Vector " + "or OpenLayers.Map instances"); } for (var i=this.provides.length-1; i>=0; --i) { target.extensions[this.provides[i]] = true; } }, /** * Method: setMap * * Parameters: * map - {<OpenLayers.Map>} The map to register browser events on. */ setMap: function(map) { this.map = map; this.cache = {}; map.events.register("mousedown", this, this.start, {extension: true}); map.events.register("mouseup", this, this.onClick, {extension: true}); map.events.register("touchstart", this, this.start, {extension: true}); map.events.register("touchmove", this, this.cancel, {extension: true}); map.events.register("touchend", this, this.onClick, {extension: true}); map.events.register("mousemove", this, this.onMousemove, {extension: true}); }, /** * Method: start * Sets startEvt = evt. * * Parameters: * evt - {<OpenLayers.Event>} */ start: function(evt) { this.startEvt = evt; }, /** * Method: cancel * Deletes the start event. * * Parameters: * evt - {<OpenLayers.Event>} */ cancel: function(evt) { delete this.startEvt; }, /** * Method: onClick * Listener for the click event. * * Parameters: * evt - {<OpenLayers.Event>} */ onClick: function(evt) { if (!this.startEvt || evt.type !== "touchend" && !OpenLayers.Event.isLeftClick(evt)) { return; } var features = this.getFeatures(this.startEvt); delete this.startEvt; // fire featureclick events var feature, layer, more, clicked = {}; for (var i=0, len=features.length; i<len; ++i) { feature = features[i]; layer = feature.layer; clicked[layer.id] = true; more = this.triggerEvent("featureclick", {feature: feature}); if (more === false) { break; } } // fire nofeatureclick events on all vector layers with no targets for (i=0, len=this.map.layers.length; i<len; ++i) { layer = this.map.layers[i]; if (layer instanceof OpenLayers.Layer.Vector && !clicked[layer.id]) { this.triggerEvent("nofeatureclick", {layer: layer}); } } }, /** * Method: onMousemove * Listener for the mousemove event. * * Parameters: * evt - {<OpenLayers.Event>} */ onMousemove: function(evt) { delete this.startEvt; var features = this.getFeatures(evt); var over = {}, newly = [], feature; for (var i=0, len=features.length; i<len; ++i) { feature = features[i]; over[feature.id] = feature; if (!this.cache[feature.id]) { newly.push(feature); } } // check if already over features var out = []; for (var id in this.cache) { feature = this.cache[id]; if (feature.layer && feature.layer.map) { if (!over[feature.id]) { out.push(feature); } } else { // removed delete this.cache[id]; } } // fire featureover events var more; for (i=0, len=newly.length; i<len; ++i) { feature = newly[i]; this.cache[feature.id] = feature; more = this.triggerEvent("featureover", {feature: feature}); if (more === false) { break; } } // fire featureout events for (i=0, len=out.length; i<len; ++i) { feature = out[i]; delete this.cache[feature.id]; more = this.triggerEvent("featureout", {feature: feature}); if (more === false) { break; } } }, /** * Method: triggerEvent * Determines where to trigger the event and triggers it. * * Parameters: * type - {String} The event type to trigger * evt - {Object} The listener argument * * Returns: * {Boolean} The last listener return. */ triggerEvent: function(type, evt) { var layer = evt.feature ? evt.feature.layer : evt.layer, object = this.target.object; if (object instanceof OpenLayers.Map || object === layer) { return this.target.triggerEvent(type, evt); } }, /** * Method: getFeatures * Get all features at the given screen location. * * Parameters: * evt - {Object} Event object. * * Returns: * {Array(<OpenLayers.Feature.Vector>)} List of features at the given point. */ getFeatures: function(evt) { var x = evt.clientX, y = evt.clientY, features = [], targets = [], layers = [], layer, target, feature, i, len; // go through all layers looking for targets for (i=this.map.layers.length-1; i>=0; --i) { layer = this.map.layers[i]; if (layer.div.style.display !== "none") { if (layer.renderer instanceof OpenLayers.Renderer.Elements) { if (layer instanceof OpenLayers.Layer.Vector) { target = document.elementFromPoint(x, y); while (target && target._featureId) { feature = layer.getFeatureById(target._featureId); if (feature) { features.push(feature); target.style.display = "none"; targets.push(target); target = document.elementFromPoint(x, y); } else { // sketch, all bets off target = false; } } } layers.push(layer); layer.div.style.display = "none"; } else if (layer.renderer instanceof OpenLayers.Renderer.Canvas) { feature = layer.renderer.getFeatureIdFromEvent(evt); if (feature) { features.push(feature); layers.push(layer); } } } } // restore feature visibility for (i=0, len=targets.length; i<len; ++i) { targets[i].style.display = ""; } // restore layer visibility for (i=layers.length-1; i>=0; --i) { layers[i].div.style.display = "block"; } return features; }, /** * APIMethod: destroy * Clean up. */ destroy: function() { for (var i=this.provides.length-1; i>=0; --i) { delete this.target.extensions[this.provides[i]]; } this.map.events.un({ mousemove: this.onMousemove, mousedown: this.start, mouseup: this.onClick, touchstart: this.start, touchmove: this.cancel, touchend: this.onClick, scope: this }); delete this.cache; delete this.map; delete this.target; } }); /** * Class: OpenLayers.Events.nofeatureclick * * Extension event type for handling click events that do not hit a feature. * * Event types provided by this extension: * - nofeatureclick */ OpenLayers.Events.nofeatureclick = OpenLayers.Events.featureclick; /** * Class: OpenLayers.Events.featureover * * Extension event type for handling hovering over a feature. * * Event types provided by this extension: * - featureover */ OpenLayers.Events.featureover = OpenLayers.Events.featureclick; /** * Class: OpenLayers.Events.featureout * * Extension event type for handling leaving a feature. * * Event types provided by this extension: * - featureout */ OpenLayers.Events.featureout = OpenLayers.Events.featureclick; /* ====================================================================== OpenLayers/Format/ArcXML.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/Geometry/Polygon.js * @requires OpenLayers/Geometry/Point.js * @requires OpenLayers/Geometry/MultiPolygon.js * @requires OpenLayers/Geometry/LinearRing.js */ /** * Class: OpenLayers.Format.ArcXML * Read/Write ArcXML. Create a new instance with the <OpenLayers.Format.ArcXML> * constructor. * * Inherits from: * - <OpenLayers.Format.XML> */ OpenLayers.Format.ArcXML = OpenLayers.Class(OpenLayers.Format.XML, { /** * Property: fontStyleKeys * {Array} List of keys used in font styling. */ fontStyleKeys: [ 'antialiasing', 'blockout', 'font', 'fontcolor','fontsize', 'fontstyle', 'glowing', 'interval', 'outline', 'printmode', 'shadow', 'transparency' ], /** * Property: request * A get_image request destined for an ArcIMS server. */ request: null, /** * Property: response * A parsed response from an ArcIMS server. */ response: null, /** * Constructor: OpenLayers.Format.ArcXML * Create a new parser/writer for ArcXML. Create an instance of this class * to begin authoring a request to an ArcIMS service. This is used * primarily by the ArcIMS layer, but could be used to do other wild * stuff, like geocoding. * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ initialize: function(options) { this.request = new OpenLayers.Format.ArcXML.Request(); this.response = new OpenLayers.Format.ArcXML.Response(); if (options) { if (options.requesttype == "feature") { this.request.get_image = null; var qry = this.request.get_feature.query; this.addCoordSys(qry.featurecoordsys, options.featureCoordSys); this.addCoordSys(qry.filtercoordsys, options.filterCoordSys); if (options.polygon) { qry.isspatial = true; qry.spatialfilter.polygon = options.polygon; } else if (options.envelope) { qry.isspatial = true; qry.spatialfilter.envelope = {minx:0, miny:0, maxx:0, maxy:0}; this.parseEnvelope(qry.spatialfilter.envelope, options.envelope); } } else if (options.requesttype == "image") { this.request.get_feature = null; var props = this.request.get_image.properties; this.parseEnvelope(props.envelope, options.envelope); this.addLayers(props.layerlist, options.layers); this.addImageSize(props.imagesize, options.tileSize); this.addCoordSys(props.featurecoordsys, options.featureCoordSys); this.addCoordSys(props.filtercoordsys, options.filterCoordSys); } else { // if an arcxml object is being created with no request type, it is // probably going to consume a response, so do not throw an error if // the requesttype is not defined this.request = null; } } OpenLayers.Format.XML.prototype.initialize.apply(this, [options]); }, /** * Method: parseEnvelope * Parse an array of coordinates into an ArcXML envelope structure. * * Parameters: * env - {Object} An envelope object that will contain the parsed coordinates. * arr - {Array(double)} An array of coordinates in the order: [ minx, miny, maxx, maxy ] */ parseEnvelope: function(env, arr) { if (arr && arr.length == 4) { env.minx = arr[0]; env.miny = arr[1]; env.maxx = arr[2]; env.maxy = arr[3]; } }, /** * Method: addLayers * Add a collection of layers to another collection of layers. Each layer in the list is tuple of * { id, visible }. These layer collections represent the * /ARCXML/REQUEST/get_image/PROPERTIES/LAYERLIST/LAYERDEF items in ArcXML * * TODO: Add support for dynamic layer rendering. * * Parameters: * ll - {Array({id,visible})} A list of layer definitions. * lyrs - {Array({id,visible})} A list of layer definitions. */ addLayers: function(ll, lyrs) { for(var lind = 0, len=lyrs.length; lind < len; lind++) { ll.push(lyrs[lind]); } }, /** * Method: addImageSize * Set the size of the requested image. * * Parameters: * imsize - {Object} An ArcXML imagesize object. * olsize - {<OpenLayers.Size>} The image size to set. */ addImageSize: function(imsize, olsize) { if (olsize !== null) { imsize.width = olsize.w; imsize.height = olsize.h; imsize.printwidth = olsize.w; imsize.printheight = olsize.h; } }, /** * Method: addCoordSys * Add the coordinate system information to an object. The object may be * * Parameters: * featOrFilt - {Object} A featurecoordsys or filtercoordsys ArcXML structure. * fsys - {String} or {<OpenLayers.Projection>} or {filtercoordsys} or * {featurecoordsys} A projection representation. If it's a {String}, * the value is assumed to be the SRID. If it's a {OpenLayers.Projection} * AND Proj4js is available, the projection number and name are extracted * from there. If it's a filter or feature ArcXML structure, it is copied. */ addCoordSys: function(featOrFilt, fsys) { if (typeof fsys == "string") { featOrFilt.id = parseInt(fsys); featOrFilt.string = fsys; } // is this a proj4js instance? else if (typeof fsys == "object" && fsys.proj !== null){ featOrFilt.id = fsys.proj.srsProjNumber; featOrFilt.string = fsys.proj.srsCode; } else { featOrFilt = fsys; } }, /** * APIMethod: iserror * Check to see if the response from the server was an error. * * Parameters: * data - {String} or {DOMElement} data to read/parse. If nothing is supplied, * the current response is examined. * * Returns: * {Boolean} true if the response was an error. */ iserror: function(data) { var ret = null; if (!data) { ret = (this.response.error !== ''); } else { data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); var errorNodes = data.documentElement.getElementsByTagName("ERROR"); ret = (errorNodes !== null && errorNodes.length > 0); } return ret; }, /** * APIMethod: read * Read data from a string, and return an response. * * Parameters: * data - {String} or {DOMElement} data to read/parse. * * Returns: * {<OpenLayers.Format.ArcXML.Response>} An ArcXML response. Note that this response * data may change in the future. */ read: function(data) { if(typeof data == "string") { data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); } var arcNode = null; if (data && data.documentElement) { if(data.documentElement.nodeName == "ARCXML") { arcNode = data.documentElement; } else { arcNode = data.documentElement.getElementsByTagName("ARCXML")[0]; } } // in Safari, arcNode will be there but will have a child named // parsererror if (!arcNode || arcNode.firstChild.nodeName === 'parsererror') { var error, source; try { error = data.firstChild.nodeValue; source = data.firstChild.childNodes[1].firstChild.nodeValue; } catch (err) { // pass } throw { message: "Error parsing the ArcXML request", error: error, source: source }; } var response = this.parseResponse(arcNode); return response; }, /** * APIMethod: write * Generate an ArcXml document string for sending to an ArcIMS server. * * Returns: * {String} A string representing the ArcXML document request. */ write: function(request) { if (!request) { request = this.request; } var root = this.createElementNS("", "ARCXML"); root.setAttribute("version","1.1"); var reqElem = this.createElementNS("", "REQUEST"); if (request.get_image != null) { var getElem = this.createElementNS("", "GET_IMAGE"); reqElem.appendChild(getElem); var propElem = this.createElementNS("", "PROPERTIES"); getElem.appendChild(propElem); var props = request.get_image.properties; if (props.featurecoordsys != null) { var feat = this.createElementNS("", "FEATURECOORDSYS"); propElem.appendChild(feat); if (props.featurecoordsys.id === 0) { feat.setAttribute("string", props.featurecoordsys['string']); } else { feat.setAttribute("id", props.featurecoordsys.id); } } if (props.filtercoordsys != null) { var filt = this.createElementNS("", "FILTERCOORDSYS"); propElem.appendChild(filt); if (props.filtercoordsys.id === 0) { filt.setAttribute("string", props.filtercoordsys.string); } else { filt.setAttribute("id", props.filtercoordsys.id); } } if (props.envelope != null) { var env = this.createElementNS("", "ENVELOPE"); propElem.appendChild(env); env.setAttribute("minx", props.envelope.minx); env.setAttribute("miny", props.envelope.miny); env.setAttribute("maxx", props.envelope.maxx); env.setAttribute("maxy", props.envelope.maxy); } var imagesz = this.createElementNS("", "IMAGESIZE"); propElem.appendChild(imagesz); imagesz.setAttribute("height", props.imagesize.height); imagesz.setAttribute("width", props.imagesize.width); if (props.imagesize.height != props.imagesize.printheight || props.imagesize.width != props.imagesize.printwidth) { imagesz.setAttribute("printheight", props.imagesize.printheight); imagesz.setArrtibute("printwidth", props.imagesize.printwidth); } if (props.background != null) { var backgrnd = this.createElementNS("", "BACKGROUND"); propElem.appendChild(backgrnd); backgrnd.setAttribute("color", props.background.color.r + "," + props.background.color.g + "," + props.background.color.b); if (props.background.transcolor !== null) { backgrnd.setAttribute("transcolor", props.background.transcolor.r + "," + props.background.transcolor.g + "," + props.background.transcolor.b); } } if (props.layerlist != null && props.layerlist.length > 0) { var layerlst = this.createElementNS("", "LAYERLIST"); propElem.appendChild(layerlst); for (var ld = 0; ld < props.layerlist.length; ld++) { var ldef = this.createElementNS("", "LAYERDEF"); layerlst.appendChild(ldef); ldef.setAttribute("id", props.layerlist[ld].id); ldef.setAttribute("visible", props.layerlist[ld].visible); if (typeof props.layerlist[ld].query == "object") { var query = props.layerlist[ld].query; if (query.where.length < 0) { continue; } var queryElem = null; if (typeof query.spatialfilter == "boolean" && query.spatialfilter) { // handle spatial filter madness queryElem = this.createElementNS("", "SPATIALQUERY"); } else { queryElem = this.createElementNS("", "QUERY"); } queryElem.setAttribute("where", query.where); if (typeof query.accuracy == "number" && query.accuracy > 0) { queryElem.setAttribute("accuracy", query.accuracy); } if (typeof query.featurelimit == "number" && query.featurelimit < 2000) { queryElem.setAttribute("featurelimit", query.featurelimit); } if (typeof query.subfields == "string" && query.subfields != "#ALL#") { queryElem.setAttribute("subfields", query.subfields); } if (typeof query.joinexpression == "string" && query.joinexpression.length > 0) { queryElem.setAttribute("joinexpression", query.joinexpression); } if (typeof query.jointables == "string" && query.jointables.length > 0) { queryElem.setAttribute("jointables", query.jointables); } ldef.appendChild(queryElem); } if (typeof props.layerlist[ld].renderer == "object") { this.addRenderer(ldef, props.layerlist[ld].renderer); } } } } else if (request.get_feature != null) { var getElem = this.createElementNS("", "GET_FEATURES"); getElem.setAttribute("outputmode", "newxml"); getElem.setAttribute("checkesc", "true"); if (request.get_feature.geometry) { getElem.setAttribute("geometry", request.get_feature.geometry); } else { getElem.setAttribute("geometry", "false"); } if (request.get_feature.compact) { getElem.setAttribute("compact", request.get_feature.compact); } if (request.get_feature.featurelimit == "number") { getElem.setAttribute("featurelimit", request.get_feature.featurelimit); } getElem.setAttribute("globalenvelope", "true"); reqElem.appendChild(getElem); if (request.get_feature.layer != null && request.get_feature.layer.length > 0) { var lyrElem = this.createElementNS("", "LAYER"); lyrElem.setAttribute("id", request.get_feature.layer); getElem.appendChild(lyrElem); } var fquery = request.get_feature.query; if (fquery != null) { var qElem = null; if (fquery.isspatial) { qElem = this.createElementNS("", "SPATIALQUERY"); } else { qElem = this.createElementNS("", "QUERY"); } getElem.appendChild(qElem); if (typeof fquery.accuracy == "number") { qElem.setAttribute("accuracy", fquery.accuracy); } //qElem.setAttribute("featurelimit", "5"); if (fquery.featurecoordsys != null) { var fcsElem1 = this.createElementNS("", "FEATURECOORDSYS"); if (fquery.featurecoordsys.id == 0) { fcsElem1.setAttribute("string", fquery.featurecoordsys.string); } else { fcsElem1.setAttribute("id", fquery.featurecoordsys.id); } qElem.appendChild(fcsElem1); } if (fquery.filtercoordsys != null) { var fcsElem2 = this.createElementNS("", "FILTERCOORDSYS"); if (fquery.filtercoordsys.id === 0) { fcsElem2.setAttribute("string", fquery.filtercoordsys.string); } else { fcsElem2.setAttribute("id", fquery.filtercoordsys.id); } qElem.appendChild(fcsElem2); } if (fquery.buffer > 0) { var bufElem = this.createElementNS("", "BUFFER"); bufElem.setAttribute("distance", fquery.buffer); qElem.appendChild(bufElem); } if (fquery.isspatial) { var spfElem = this.createElementNS("", "SPATIALFILTER"); spfElem.setAttribute("relation", fquery.spatialfilter.relation); qElem.appendChild(spfElem); if (fquery.spatialfilter.envelope) { var envElem = this.createElementNS("", "ENVELOPE"); envElem.setAttribute("minx", fquery.spatialfilter.envelope.minx); envElem.setAttribute("miny", fquery.spatialfilter.envelope.miny); envElem.setAttribute("maxx", fquery.spatialfilter.envelope.maxx); envElem.setAttribute("maxy", fquery.spatialfilter.envelope.maxy); spfElem.appendChild(envElem); } else if(typeof fquery.spatialfilter.polygon == "object") { spfElem.appendChild(this.writePolygonGeometry(fquery.spatialfilter.polygon)); } } if (fquery.where != null && fquery.where.length > 0) { qElem.setAttribute("where", fquery.where); } } } root.appendChild(reqElem); return OpenLayers.Format.XML.prototype.write.apply(this, [root]); }, addGroupRenderer: function(ldef, toprenderer) { var topRelem = this.createElementNS("", "GROUPRENDERER"); ldef.appendChild(topRelem); for (var rind = 0; rind < toprenderer.length; rind++) { var renderer = toprenderer[rind]; this.addRenderer(topRelem, renderer); } }, addRenderer: function(topRelem, renderer) { if (OpenLayers.Util.isArray(renderer)) { this.addGroupRenderer(topRelem, renderer); } else { var renderElem = this.createElementNS("", renderer.type.toUpperCase() + "RENDERER"); topRelem.appendChild(renderElem); if (renderElem.tagName == "VALUEMAPRENDERER") { this.addValueMapRenderer(renderElem, renderer); } else if (renderElem.tagName == "VALUEMAPLABELRENDERER") { this.addValueMapLabelRenderer(renderElem, renderer); } else if (renderElem.tagName == "SIMPLELABELRENDERER") { this.addSimpleLabelRenderer(renderElem, renderer); } else if (renderElem.tagName == "SCALEDEPENDENTRENDERER") { this.addScaleDependentRenderer(renderElem, renderer); } } }, addScaleDependentRenderer: function(renderElem, renderer) { if (typeof renderer.lower == "string" || typeof renderer.lower == "number") { renderElem.setAttribute("lower", renderer.lower); } if (typeof renderer.upper == "string" || typeof renderer.upper == "number") { renderElem.setAttribute("upper", renderer.upper); } this.addRenderer(renderElem, renderer.renderer); }, addValueMapLabelRenderer: function(renderElem, renderer) { renderElem.setAttribute("lookupfield", renderer.lookupfield); renderElem.setAttribute("labelfield", renderer.labelfield); if (typeof renderer.exacts == "object") { for (var ext=0, extlen=renderer.exacts.length; ext<extlen; ext++) { var exact = renderer.exacts[ext]; var eelem = this.createElementNS("", "EXACT"); if (typeof exact.value == "string") { eelem.setAttribute("value", exact.value); } if (typeof exact.label == "string") { eelem.setAttribute("label", exact.label); } if (typeof exact.method == "string") { eelem.setAttribute("method", exact.method); } renderElem.appendChild(eelem); if (typeof exact.symbol == "object") { var selem = null; if (exact.symbol.type == "text") { selem = this.createElementNS("", "TEXTSYMBOL"); } if (selem != null) { var keys = this.fontStyleKeys; for (var i = 0, len = keys.length; i < len; i++) { var key = keys[i]; if (exact.symbol[key]) { selem.setAttribute(key, exact.symbol[key]); } } eelem.appendChild(selem); } } } // for each exact } }, addValueMapRenderer: function(renderElem, renderer) { renderElem.setAttribute("lookupfield", renderer.lookupfield); if (typeof renderer.ranges == "object") { for(var rng=0, rnglen=renderer.ranges.length; rng<rnglen; rng++) { var range = renderer.ranges[rng]; var relem = this.createElementNS("", "RANGE"); relem.setAttribute("lower", range.lower); relem.setAttribute("upper", range.upper); renderElem.appendChild(relem); if (typeof range.symbol == "object") { var selem = null; if (range.symbol.type == "simplepolygon") { selem = this.createElementNS("", "SIMPLEPOLYGONSYMBOL"); } if (selem != null) { if (typeof range.symbol.boundarycolor == "string") { selem.setAttribute("boundarycolor", range.symbol.boundarycolor); } if (typeof range.symbol.fillcolor == "string") { selem.setAttribute("fillcolor", range.symbol.fillcolor); } if (typeof range.symbol.filltransparency == "number") { selem.setAttribute("filltransparency", range.symbol.filltransparency); } relem.appendChild(selem); } } } // for each range } else if (typeof renderer.exacts == "object") { for (var ext=0, extlen=renderer.exacts.length; ext<extlen; ext++) { var exact = renderer.exacts[ext]; var eelem = this.createElementNS("", "EXACT"); if (typeof exact.value == "string") { eelem.setAttribute("value", exact.value); } if (typeof exact.label == "string") { eelem.setAttribute("label", exact.label); } if (typeof exact.method == "string") { eelem.setAttribute("method", exact.method); } renderElem.appendChild(eelem); if (typeof exact.symbol == "object") { var selem = null; if (exact.symbol.type == "simplemarker") { selem = this.createElementNS("", "SIMPLEMARKERSYMBOL"); } if (selem != null) { if (typeof exact.symbol.antialiasing == "string") { selem.setAttribute("antialiasing", exact.symbol.antialiasing); } if (typeof exact.symbol.color == "string") { selem.setAttribute("color", exact.symbol.color); } if (typeof exact.symbol.outline == "string") { selem.setAttribute("outline", exact.symbol.outline); } if (typeof exact.symbol.overlap == "string") { selem.setAttribute("overlap", exact.symbol.overlap); } if (typeof exact.symbol.shadow == "string") { selem.setAttribute("shadow", exact.symbol.shadow); } if (typeof exact.symbol.transparency == "number") { selem.setAttribute("transparency", exact.symbol.transparency); } //if (typeof exact.symbol.type == "string") // selem.setAttribute("type", exact.symbol.type); if (typeof exact.symbol.usecentroid == "string") { selem.setAttribute("usecentroid", exact.symbol.usecentroid); } if (typeof exact.symbol.width == "number") { selem.setAttribute("width", exact.symbol.width); } eelem.appendChild(selem); } } } // for each exact } }, addSimpleLabelRenderer: function(renderElem, renderer) { renderElem.setAttribute("field", renderer.field); var keys = ['featureweight', 'howmanylabels', 'labelbufferratio', 'labelpriorities', 'labelweight', 'linelabelposition', 'rotationalangles']; for (var i=0, len=keys.length; i<len; i++) { var key = keys[i]; if (renderer[key]) { renderElem.setAttribute(key, renderer[key]); } } if (renderer.symbol.type == "text") { var symbol = renderer.symbol; var selem = this.createElementNS("", "TEXTSYMBOL"); renderElem.appendChild(selem); var keys = this.fontStyleKeys; for (var i=0, len=keys.length; i<len; i++) { var key = keys[i]; if (symbol[key]) { selem.setAttribute(key, renderer[key]); } } } }, writePolygonGeometry: function(polygon) { if (!(polygon instanceof OpenLayers.Geometry.Polygon)) { throw { message:'Cannot write polygon geometry to ArcXML with an ' + polygon.CLASS_NAME + ' object.', geometry: polygon }; } var polyElem = this.createElementNS("", "POLYGON"); for (var ln=0, lnlen=polygon.components.length; ln<lnlen; ln++) { var ring = polygon.components[ln]; var ringElem = this.createElementNS("", "RING"); for (var rn=0, rnlen=ring.components.length; rn<rnlen; rn++) { var point = ring.components[rn]; var pointElem = this.createElementNS("", "POINT"); pointElem.setAttribute("x", point.x); pointElem.setAttribute("y", point.y); ringElem.appendChild(pointElem); } polyElem.appendChild(ringElem); } return polyElem; }, /** * Method: parseResponse * Take an ArcXML response, and parse in into this object's internal properties. * * Parameters: * data - {String} or {DOMElement} The ArcXML response, as either a string or the * top level DOMElement of the response. */ parseResponse: function(data) { if(typeof data == "string") { var newData = new OpenLayers.Format.XML(); data = newData.read(data); } var response = new OpenLayers.Format.ArcXML.Response(); var errorNode = data.getElementsByTagName("ERROR"); if (errorNode != null && errorNode.length > 0) { response.error = this.getChildValue(errorNode, "Unknown error."); } else { var responseNode = data.getElementsByTagName("RESPONSE"); if (responseNode == null || responseNode.length == 0) { response.error = "No RESPONSE tag found in ArcXML response."; return response; } var rtype = responseNode[0].firstChild.nodeName; if (rtype == "#text") { rtype = responseNode[0].firstChild.nextSibling.nodeName; } if (rtype == "IMAGE") { var envelopeNode = data.getElementsByTagName("ENVELOPE"); var outputNode = data.getElementsByTagName("OUTPUT"); if (envelopeNode == null || envelopeNode.length == 0) { response.error = "No ENVELOPE tag found in ArcXML response."; } else if (outputNode == null || outputNode.length == 0) { response.error = "No OUTPUT tag found in ArcXML response."; } else { var envAttr = this.parseAttributes(envelopeNode[0]); var outputAttr = this.parseAttributes(outputNode[0]); if (typeof outputAttr.type == "string") { response.image = { envelope: envAttr, output: { type: outputAttr.type, data: this.getChildValue(outputNode[0]) } }; } else { response.image = { envelope: envAttr, output: outputAttr }; } } } else if (rtype == "FEATURES") { var features = responseNode[0].getElementsByTagName("FEATURES"); // get the feature count var featureCount = features[0].getElementsByTagName("FEATURECOUNT"); response.features.featurecount = featureCount[0].getAttribute("count"); if (response.features.featurecount > 0) { // get the feature envelope var envelope = features[0].getElementsByTagName("ENVELOPE"); response.features.envelope = this.parseAttributes(envelope[0], typeof(0)); // get the field values per feature var featureList = features[0].getElementsByTagName("FEATURE"); for (var fn = 0; fn < featureList.length; fn++) { var feature = new OpenLayers.Feature.Vector(); var fields = featureList[fn].getElementsByTagName("FIELD"); for (var fdn = 0; fdn < fields.length; fdn++) { var fieldName = fields[fdn].getAttribute("name"); var fieldValue = fields[fdn].getAttribute("value"); feature.attributes[ fieldName ] = fieldValue; } var geom = featureList[fn].getElementsByTagName("POLYGON"); if (geom.length > 0) { // if there is a polygon, create an openlayers polygon, and assign // it to the .geometry property of the feature var ring = geom[0].getElementsByTagName("RING"); var polys = []; for (var rn = 0; rn < ring.length; rn++) { var linearRings = []; linearRings.push(this.parsePointGeometry(ring[rn])); var holes = ring[rn].getElementsByTagName("HOLE"); for (var hn = 0; hn < holes.length; hn++) { linearRings.push(this.parsePointGeometry(holes[hn])); } holes = null; polys.push(new OpenLayers.Geometry.Polygon(linearRings)); linearRings = null; } ring = null; if (polys.length == 1) { feature.geometry = polys[0]; } else { feature.geometry = new OpenLayers.Geometry.MultiPolygon(polys); } } response.features.feature.push(feature); } } } else { response.error = "Unidentified response type."; } } return response; }, /** * Method: parseAttributes * * Parameters: * node - {<DOMElement>} An element to parse attributes from. * * Returns: * {Object} An attributes object, with properties set to attribute values. */ parseAttributes: function(node,type) { var attributes = {}; for(var attr = 0; attr < node.attributes.length; attr++) { if (type == "number") { attributes[node.attributes[attr].nodeName] = parseFloat(node.attributes[attr].nodeValue); } else { attributes[node.attributes[attr].nodeName] = node.attributes[attr].nodeValue; } } return attributes; }, /** * Method: parsePointGeometry * * Parameters: * node - {<DOMElement>} An element to parse <COORDS> or <POINT> arcxml data from. * * Returns: * {<OpenLayers.Geometry.LinearRing>} A linear ring represented by the node's points. */ parsePointGeometry: function(node) { var ringPoints = []; var coords = node.getElementsByTagName("COORDS"); if (coords.length > 0) { // if coords is present, it's the only coords item var coordArr = this.getChildValue(coords[0]); coordArr = coordArr.split(/;/); for (var cn = 0; cn < coordArr.length; cn++) { var coordItems = coordArr[cn].split(/ /); ringPoints.push(new OpenLayers.Geometry.Point(coordItems[0], coordItems[1])); } coords = null; } else { var point = node.getElementsByTagName("POINT"); if (point.length > 0) { for (var pn = 0; pn < point.length; pn++) { ringPoints.push( new OpenLayers.Geometry.Point( parseFloat(point[pn].getAttribute("x")), parseFloat(point[pn].getAttribute("y")) ) ); } } point = null; } return new OpenLayers.Geometry.LinearRing(ringPoints); }, CLASS_NAME: "OpenLayers.Format.ArcXML" }); OpenLayers.Format.ArcXML.Request = OpenLayers.Class({ initialize: function(params) { var defaults = { get_image: { properties: { background: null, /*{ color: { r:255, g:255, b:255 }, transcolor: null },*/ draw: true, envelope: { minx: 0, miny: 0, maxx: 0, maxy: 0 }, featurecoordsys: { id:0, string:"", datumtransformid:0, datumtransformstring:"" }, filtercoordsys:{ id:0, string:"", datumtransformid:0, datumtransformstring:"" }, imagesize:{ height:0, width:0, dpi:96, printheight:0, printwidth:0, scalesymbols:false }, layerlist:[], /* no support for legends */ output:{ baseurl:"", legendbaseurl:"", legendname:"", legendpath:"", legendurl:"", name:"", path:"", type:"jpg", url:"" } } }, get_feature: { layer: "", query: { isspatial: false, featurecoordsys: { id:0, string:"", datumtransformid:0, datumtransformstring:"" }, filtercoordsys: { id:0, string:"", datumtransformid:0, datumtransformstring:"" }, buffer:0, where:"", spatialfilter: { relation: "envelope_intersection", envelope: null } } }, environment: { separators: { cs:" ", ts:";" } }, layer: [], workspaces: [] }; return OpenLayers.Util.extend(this, defaults); }, CLASS_NAME: "OpenLayers.Format.ArcXML.Request" }); OpenLayers.Format.ArcXML.Response = OpenLayers.Class({ initialize: function(params) { var defaults = { image: { envelope:null, output:'' }, features: { featurecount: 0, envelope: null, feature: [] }, error:'' }; return OpenLayers.Util.extend(this, defaults); }, CLASS_NAME: "OpenLayers.Format.ArcXML.Response" }); /* ====================================================================== OpenLayers/Format/CSWGetRecords.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.CSWGetRecords * Default version is 2.0.2. * * Returns: * {<OpenLayers.Format>} A CSWGetRecords format of the given version. */ OpenLayers.Format.CSWGetRecords = function(options) { options = OpenLayers.Util.applyDefaults( options, OpenLayers.Format.CSWGetRecords.DEFAULTS ); var cls = OpenLayers.Format.CSWGetRecords["v"+options.version.replace(/\./g, "_")]; if(!cls) { throw "Unsupported CSWGetRecords version: " + options.version; } return new cls(options); }; /** * Constant: DEFAULTS * {Object} Default properties for the CSWGetRecords format. */ OpenLayers.Format.CSWGetRecords.DEFAULTS = { "version": "2.0.2" }; /* ====================================================================== OpenLayers/Format/WMSDescribeLayer.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 */ /** * Class: OpenLayers.Format.WMSDescribeLayer * Read SLD WMS DescribeLayer response * DescribeLayer is meant to couple WMS to WFS and WCS * * Inherits from: * - <OpenLayers.Format.XML.VersionedOGC> */ OpenLayers.Format.WMSDescribeLayer = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, { /** * APIProperty: defaultVersion * {String} Version number to assume if none found. Default is "1.1.1". */ defaultVersion: "1.1.1", /** * Constructor: OpenLayers.Format.WMSDescribeLayer * Create a new parser for WMS DescribeLayer responses. * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ /** * APIMethod: read * Read DescribeLayer data from a string, and return the response. * The OGC currently defines 2 formats which are allowed for output, * so we need to parse these 2 types * * Parameters: * data - {String} or {DOMElement} data to read/parse. * * Returns: * {Array} Array of {<LayerDescription>} objects which have: * - {String} owsType: WFS/WCS * - {String} owsURL: the online resource * - {String} typeName: the name of the typename on the service */ CLASS_NAME: "OpenLayers.Format.WMSDescribeLayer" }); /* ====================================================================== OpenLayers/Format/GPX.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/LineString.js * @requires OpenLayers/Projection.js */ /** * Class: OpenLayers.Format.GPX * Read/write GPX parser. Create a new instance with the * <OpenLayers.Format.GPX> constructor. * * Inherits from: * - <OpenLayers.Format.XML> */ OpenLayers.Format.GPX = OpenLayers.Class(OpenLayers.Format.XML, { /** * APIProperty: defaultDesc * {String} Default description for the waypoints/tracks in the case * where the feature has no "description" attribute. * Default is "No description available". */ defaultDesc: "No description available", /** * APIProperty: extractWaypoints * {Boolean} Extract waypoints from GPX. (default: true) */ extractWaypoints: true, /** * APIProperty: extractTracks * {Boolean} Extract tracks from GPX. (default: true) */ extractTracks: true, /** * APIProperty: extractRoutes * {Boolean} Extract routes from GPX. (default: true) */ extractRoutes: true, /** * APIProperty: extractAttributes * {Boolean} Extract feature attributes from GPX. (default: true) * NOTE: Attributes as part of extensions to the GPX standard may not * be extracted. */ extractAttributes: true, /** * Property: namespaces * {Object} Mapping of namespace aliases to namespace URIs. */ namespaces: { gpx: "http://www.topografix.com/GPX/1/1", xsi: "http://www.w3.org/2001/XMLSchema-instance" }, /** * Property: schemaLocation * {String} Schema location. Defaults to * "http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd" */ schemaLocation: "http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd", /** * APIProperty: creator * {String} The creator attribute to be added to the written GPX files. * Defaults to "OpenLayers" */ creator: "OpenLayers", /** * Constructor: OpenLayers.Format.GPX * Create a new parser for GPX. * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ initialize: function(options) { // GPX coordinates are always in longlat WGS84 this.externalProjection = new OpenLayers.Projection("EPSG:4326"); OpenLayers.Format.XML.prototype.initialize.apply(this, [options]); }, /** * APIMethod: read * Return a list of features from a GPX doc * * Parameters: * doc - {Element} * * Returns: * Array({<OpenLayers.Feature.Vector>}) */ read: function(doc) { if (typeof doc == "string") { doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc]); } var features = []; if(this.extractTracks) { var tracks = doc.getElementsByTagName("trk"); for (var i=0, len=tracks.length; i<len; i++) { // Attributes are only in trk nodes, not trkseg nodes var attrs = {}; if(this.extractAttributes) { attrs = this.parseAttributes(tracks[i]); } var segs = this.getElementsByTagNameNS(tracks[i], tracks[i].namespaceURI, "trkseg"); for (var j = 0, seglen = segs.length; j < seglen; j++) { // We don't yet support extraction of trkpt attributes // All trksegs of a trk get that trk's attributes var track = this.extractSegment(segs[j], "trkpt"); features.push(new OpenLayers.Feature.Vector(track, attrs)); } } } if(this.extractRoutes) { var routes = doc.getElementsByTagName("rte"); for (var k=0, klen=routes.length; k<klen; k++) { var attrs = {}; if(this.extractAttributes) { attrs = this.parseAttributes(routes[k]); } var route = this.extractSegment(routes[k], "rtept"); features.push(new OpenLayers.Feature.Vector(route, attrs)); } } if(this.extractWaypoints) { var waypoints = doc.getElementsByTagName("wpt"); for (var l = 0, len = waypoints.length; l < len; l++) { var attrs = {}; if(this.extractAttributes) { attrs = this.parseAttributes(waypoints[l]); } var wpt = new OpenLayers.Geometry.Point(waypoints[l].getAttribute("lon"), waypoints[l].getAttribute("lat")); features.push(new OpenLayers.Feature.Vector(wpt, attrs)); } } if (this.internalProjection && this.externalProjection) { for (var g = 0, featLength = features.length; g < featLength; g++) { features[g].geometry.transform(this.externalProjection, this.internalProjection); } } return features; }, /** * Method: extractSegment * * Parameters: * segment - {DOMElement} a trkseg or rte node to parse * segmentType - {String} nodeName of waypoints that form the line * * Returns: * {<OpenLayers.Geometry.LineString>} A linestring geometry */ extractSegment: function(segment, segmentType) { var points = this.getElementsByTagNameNS(segment, segment.namespaceURI, segmentType); var point_features = []; for (var i = 0, len = points.length; i < len; i++) { point_features.push(new OpenLayers.Geometry.Point(points[i].getAttribute("lon"), points[i].getAttribute("lat"))); } return new OpenLayers.Geometry.LineString(point_features); }, /** * Method: parseAttributes * * Parameters: * node - {<DOMElement>} * * Returns: * {Object} An attributes object. */ parseAttributes: function(node) { // node is either a wpt, trk or rte // attributes are children of the form <attr>value</attr> var attributes = {}; var attrNode = node.firstChild, value, name; while(attrNode) { if(attrNode.nodeType == 1 && attrNode.firstChild) { value = attrNode.firstChild; if(value.nodeType == 3 || value.nodeType == 4) { name = (attrNode.prefix) ? attrNode.nodeName.split(":")[1] : attrNode.nodeName; if(name != "trkseg" && name != "rtept") { attributes[name] = value.nodeValue; } } } attrNode = attrNode.nextSibling; } return attributes; }, /** * APIMethod: write * Accepts Feature Collection, and returns a string. * * Parameters: * features - {Array(<OpenLayers.Feature.Vector>)} List of features to serialize into a string. * metadata - {Object} A key/value pairs object to build a metadata node to * add to the gpx. Supported keys are 'name', 'desc', 'author'. */ write: function(features, metadata) { features = OpenLayers.Util.isArray(features) ? features : [features]; var gpx = this.createElementNS(this.namespaces.gpx, "gpx"); gpx.setAttribute("version", "1.1"); gpx.setAttribute("creator", this.creator); this.setAttributes(gpx, { "xsi:schemaLocation": this.schemaLocation }); if (metadata && typeof metadata == 'object') { gpx.appendChild(this.buildMetadataNode(metadata)); } for(var i=0, len=features.length; i<len; i++) { gpx.appendChild(this.buildFeatureNode(features[i])); } return OpenLayers.Format.XML.prototype.write.apply(this, [gpx]); }, /** * Method: buildMetadataNode * Creates a "metadata" node. * * Returns: * {DOMElement} */ buildMetadataNode: function(metadata) { var types = ['name', 'desc', 'author'], node = this.createElementNS(this.namespaces.gpx, 'metadata'); for (var i=0; i < types.length; i++) { var type = types[i]; if (metadata[type]) { var n = this.createElementNS(this.namespaces.gpx, type); n.appendChild(this.createTextNode(metadata[type])); node.appendChild(n); } } return node; }, /** * Method: buildFeatureNode * Accepts an <OpenLayers.Feature.Vector>, and builds a node for it. * * Parameters: * feature - {<OpenLayers.Feature.Vector>} * * Returns: * {DOMElement} - The created node, either a 'wpt' or a 'trk'. */ buildFeatureNode: function(feature) { var geometry = feature.geometry; geometry = geometry.clone(); if (this.internalProjection && this.externalProjection) { geometry.transform(this.internalProjection, this.externalProjection); } if (geometry.CLASS_NAME == "OpenLayers.Geometry.Point") { var wpt = this.buildWptNode(geometry); this.appendAttributesNode(wpt, feature); return wpt; } else { var trkNode = this.createElementNS(this.namespaces.gpx, "trk"); this.appendAttributesNode(trkNode, feature); var trkSegNodes = this.buildTrkSegNode(geometry); trkSegNodes = OpenLayers.Util.isArray(trkSegNodes) ? trkSegNodes : [trkSegNodes]; for (var i = 0, len = trkSegNodes.length; i < len; i++) { trkNode.appendChild(trkSegNodes[i]); } return trkNode; } }, /** * Method: buildTrkSegNode * Builds trkseg node(s) given a geometry * * Parameters: * trknode * geometry - {<OpenLayers.Geometry>} */ buildTrkSegNode: function(geometry) { var node, i, len, point, nodes; if (geometry.CLASS_NAME == "OpenLayers.Geometry.LineString" || geometry.CLASS_NAME == "OpenLayers.Geometry.LinearRing") { node = this.createElementNS(this.namespaces.gpx, "trkseg"); for (i = 0, len=geometry.components.length; i < len; i++) { point = geometry.components[i]; node.appendChild(this.buildTrkPtNode(point)); } return node; } else { nodes = []; for (i = 0, len = geometry.components.length; i < len; i++) { nodes.push(this.buildTrkSegNode(geometry.components[i])); } return nodes; } }, /** * Method: buildTrkPtNode * Builds a trkpt node given a point * * Parameters: * point - {<OpenLayers.Geometry.Point>} * * Returns: * {DOMElement} A trkpt node */ buildTrkPtNode: function(point) { var node = this.createElementNS(this.namespaces.gpx, "trkpt"); node.setAttribute("lon", point.x); node.setAttribute("lat", point.y); return node; }, /** * Method: buildWptNode * Builds a wpt node given a point * * Parameters: * geometry - {<OpenLayers.Geometry.Point>} * * Returns: * {DOMElement} A wpt node */ buildWptNode: function(geometry) { var node = this.createElementNS(this.namespaces.gpx, "wpt"); node.setAttribute("lon", geometry.x); node.setAttribute("lat", geometry.y); return node; }, /** * Method: appendAttributesNode * Adds some attributes node. * * Parameters: * node - {DOMElement} the node to append the attribute nodes to. * feature - {<OpenLayers.Feature.Vector>} */ appendAttributesNode: function(node, feature) { var name = this.createElementNS(this.namespaces.gpx, 'name'); name.appendChild(this.createTextNode( feature.attributes.name || feature.id)); node.appendChild(name); var desc = this.createElementNS(this.namespaces.gpx, 'desc'); desc.appendChild(this.createTextNode( feature.attributes.description || this.defaultDesc)); node.appendChild(desc); // TBD - deal with remaining (non name/description) attributes. }, CLASS_NAME: "OpenLayers.Format.GPX" }); /* ====================================================================== OpenLayers/Format/WFSCapabilities.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 */ /** * Class: OpenLayers.Format.WFSCapabilities * Read WFS Capabilities. * * Inherits from: * - <OpenLayers.Format.XML.VersionedOGC> */ OpenLayers.Format.WFSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, { /** * APIProperty: defaultVersion * {String} Version number to assume if none found. Default is "1.1.0". */ defaultVersion: "1.1.0", /** * Constructor: OpenLayers.Format.WFSCapabilities * Create a new parser for WFS capabilities. * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ /** * APIMethod: read * Read capabilities data from a string, and return a list of layers. * * Parameters: * data - {String} or {DOMElement} data to read/parse. * * Returns: * {Array} List of named layers. */ CLASS_NAME: "OpenLayers.Format.WFSCapabilities" }); /* ====================================================================== OpenLayers/Format/WMSGetFeatureInfo.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.WMSGetFeatureInfo * Class to read GetFeatureInfo responses from Web Mapping Services * * Inherits from: * - <OpenLayers.Format.XML> */ OpenLayers.Format.WMSGetFeatureInfo = OpenLayers.Class(OpenLayers.Format.XML, { /** * APIProperty: layerIdentifier * {String} All xml nodes containing this search criteria will populate an * internal array of layer nodes. */ layerIdentifier: '_layer', /** * APIProperty: featureIdentifier * {String} All xml nodes containing this search criteria will populate an * internal array of feature nodes for each layer node found. */ featureIdentifier: '_feature', /** * Property: regExes * Compiled regular expressions for manipulating strings. */ regExes: { trimSpace: (/^\s*|\s*$/g), removeSpace: (/\s*/g), splitSpace: (/\s+/), trimComma: (/\s*,\s*/g) }, /** * Property: gmlFormat * {<OpenLayers.Format.GML>} internal GML format for parsing geometries * in msGMLOutput */ gmlFormat: null, /** * Constructor: OpenLayers.Format.WMSGetFeatureInfo * Create a new parser for WMS GetFeatureInfo responses * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ /** * APIMethod: read * Read WMS GetFeatureInfo data from a string, and return an array of features * * Parameters: * data - {String} or {DOMElement} data to read/parse. * * Returns: * {Array(<OpenLayers.Feature.Vector>)} An array of features. */ read: function(data) { var result; if(typeof data == "string") { data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); } var root = data.documentElement; if(root) { var scope = this; var read = this["read_" + root.nodeName]; if(read) { result = read.call(this, root); } else { // fall-back to GML since this is a common output format for WMS // GetFeatureInfo responses result = new OpenLayers.Format.GML((this.options ? this.options : {})).read(data); } } else { result = data; } return result; }, /** * Method: read_msGMLOutput * Parse msGMLOutput nodes. * * Parameters: * data - {DOMElement} * * Returns: * {Array} */ read_msGMLOutput: function(data) { var response = []; var layerNodes = this.getSiblingNodesByTagCriteria(data, this.layerIdentifier); if (layerNodes) { for (var i=0, len=layerNodes.length; i<len; ++i) { var node = layerNodes[i]; var layerName = node.nodeName; if (node.prefix) { layerName = layerName.split(':')[1]; } var layerName = layerName.replace(this.layerIdentifier, ''); var featureNodes = this.getSiblingNodesByTagCriteria(node, this.featureIdentifier); if (featureNodes) { for (var j = 0; j < featureNodes.length; j++) { var featureNode = featureNodes[j]; var geomInfo = this.parseGeometry(featureNode); var attributes = this.parseAttributes(featureNode); var feature = new OpenLayers.Feature.Vector(geomInfo.geometry, attributes, null); feature.bounds = geomInfo.bounds; feature.type = layerName; response.push(feature); } } } } return response; }, /** * Method: read_FeatureInfoResponse * Parse FeatureInfoResponse nodes. * * Parameters: * data - {DOMElement} * * Returns: * {Array} */ read_FeatureInfoResponse: function(data) { var response = []; var featureNodes = this.getElementsByTagNameNS(data, '*', 'FIELDS'); for(var i=0, len=featureNodes.length;i<len;i++) { var featureNode = featureNodes[i]; var geom = null; // attributes can be actual attributes on the FIELDS tag, // or FIELD children var attributes = {}; var j; var jlen = featureNode.attributes.length; if (jlen > 0) { for(j=0; j<jlen; j++) { var attribute = featureNode.attributes[j]; attributes[attribute.nodeName] = attribute.nodeValue; } } else { var nodes = featureNode.childNodes; for (j=0, jlen=nodes.length; j<jlen; ++j) { var node = nodes[j]; if (node.nodeType != 3) { attributes[node.getAttribute("name")] = node.getAttribute("value"); } } } response.push( new OpenLayers.Feature.Vector(geom, attributes, null) ); } return response; }, /** * Method: getSiblingNodesByTagCriteria * Recursively searches passed xml node and all it's descendant levels for * nodes whose tagName contains the passed search string. This returns an * array of all sibling nodes which match the criteria from the highest * hierarchial level from which a match is found. * * Parameters: * node - {DOMElement} An xml node * criteria - {String} Search string which will match some part of a tagName * * Returns: * Array({DOMElement}) An array of sibling xml nodes */ getSiblingNodesByTagCriteria: function(node, criteria){ var nodes = []; var children, tagName, n, matchNodes, child; if (node && node.hasChildNodes()) { children = node.childNodes; n = children.length; for(var k=0; k<n; k++){ child = children[k]; while (child && child.nodeType != 1) { child = child.nextSibling; k++; } tagName = (child ? child.nodeName : ''); if (tagName.length > 0 && tagName.indexOf(criteria) > -1) { nodes.push(child); } else { matchNodes = this.getSiblingNodesByTagCriteria( child, criteria); if(matchNodes.length > 0){ (nodes.length == 0) ? nodes = matchNodes : nodes.push(matchNodes); } } } } return nodes; }, /** * Method: parseAttributes * * Parameters: * node - {<DOMElement>} * * Returns: * {Object} An attributes object. * * Notes: * Assumes that attributes are direct child xml nodes of the passed node * and contain only a single text node. */ parseAttributes: function(node){ var attributes = {}; if (node.nodeType == 1) { var children = node.childNodes; var n = children.length; for (var i = 0; i < n; ++i) { var child = children[i]; if (child.nodeType == 1) { var grandchildren = child.childNodes; var name = (child.prefix) ? child.nodeName.split(":")[1] : child.nodeName; if (grandchildren.length == 0) { attributes[name] = null; } else if (grandchildren.length == 1) { var grandchild = grandchildren[0]; if (grandchild.nodeType == 3 || grandchild.nodeType == 4) { var value = grandchild.nodeValue.replace( this.regExes.trimSpace, ""); attributes[name] = value; } } } } } return attributes; }, /** * Method: parseGeometry * Parse the geometry and the feature bounds out of the node using * Format.GML * * Parameters: * node - {<DOMElement>} * * Returns: * {Object} An object containing the geometry and the feature bounds */ parseGeometry: function(node) { // we need to use the old Format.GML parser since we do not know the // geometry name if (!this.gmlFormat) { this.gmlFormat = new OpenLayers.Format.GML(); } var feature = this.gmlFormat.parseFeature(node); var geometry, bounds = null; if (feature) { geometry = feature.geometry && feature.geometry.clone(); bounds = feature.bounds && feature.bounds.clone(); feature.destroy(); } return {geometry: geometry, bounds: bounds}; }, CLASS_NAME: "OpenLayers.Format.WMSGetFeatureInfo" }); /* ====================================================================== OpenLayers/Format/WMTSCapabilities.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 */ /** * Class: OpenLayers.Format.WMTSCapabilities * Read WMTS Capabilities. * * Inherits from: * - <OpenLayers.Format.XML.VersionedOGC> */ OpenLayers.Format.WMTSCapabilities = 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", /** * APIProperty: yx * {Object} Members in the yx object are used to determine if a CRS URN * corresponds to a CRS with y,x axis order. Member names are CRS URNs * and values are boolean. By default, the following CRS URN are * assumed to correspond to a CRS with y,x axis order: * * * urn:ogc:def:crs:EPSG::4326 */ yx: { "urn:ogc:def:crs:EPSG::4326": true }, /** * Constructor: OpenLayers.Format.WMTSCapabilities * Create a new parser for WMTS capabilities. * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ /** * APIMethod: read * Read capabilities data from a string, and return information about * the service (offering and observedProperty mostly). * * Parameters: * data - {String} or {DOMElement} data to read/parse. * * Returns: * {Object} Info about the WMTS Capabilities */ /** * APIMethod: createLayer * Create a WMTS layer given a capabilities object. * * Parameters: * capabilities - {Object} The object returned from a <read> call to this * format. * config - {Object} Configuration properties for the layer. Defaults for * the layer will apply if not provided. * * Required config properties: * layer - {String} The layer identifier. * * Optional config properties: * matrixSet - {String} The matrix set identifier, required if there is * more than one matrix set in the layer capabilities. * style - {String} The name of the style * format - {String} Image format for the layer. Default is the first * format returned in the GetCapabilities response. * param - {Object} The dimensions values eg: {"Year": "2012"} * * Returns: * {<OpenLayers.Layer.WMTS>} A properly configured WMTS layer. Throws an * error if an incomplete config is provided. Returns undefined if no * layer could be created with the provided config. */ createLayer: function(capabilities, config) { var layer; // confirm required properties are supplied in config if (!('layer' in config)) { throw new Error("Missing property 'layer' in configuration."); } var contents = capabilities.contents; // find the layer definition with the given identifier var layers = contents.layers; var layerDef; for (var i=0, ii=contents.layers.length; i<ii; ++i) { if (contents.layers[i].identifier === config.layer) { layerDef = contents.layers[i]; break; } } if (!layerDef) { throw new Error("Layer not found"); } var format = config.format; if (!format && layerDef.formats && layerDef.formats.length) { format = layerDef.formats[0]; } // find the matrixSet definition var matrixSet; if (config.matrixSet) { matrixSet = contents.tileMatrixSets[config.matrixSet]; } else if (layerDef.tileMatrixSetLinks.length >= 1) { matrixSet = contents.tileMatrixSets[ layerDef.tileMatrixSetLinks[0].tileMatrixSet]; } if (!matrixSet) { throw new Error("matrixSet not found"); } // get the default style for the layer var style; for (var i=0, ii=layerDef.styles.length; i<ii; ++i) { style = layerDef.styles[i]; if (style.isDefault) { break; } } var requestEncoding = config.requestEncoding; if (!requestEncoding) { requestEncoding = "KVP"; if (capabilities.operationsMetadata.GetTile.dcp.http) { var http = capabilities.operationsMetadata.GetTile.dcp.http; // Get first get method if (http.get[0].constraints) { var constraints = http.get[0].constraints; var allowedValues = constraints.GetEncoding.allowedValues; // The OGC documentation is not clear if we should use // REST or RESTful, ArcGis use RESTful, // and OpenLayers use REST. if (!allowedValues.KVP && (allowedValues.REST || allowedValues.RESTful)) { requestEncoding = "REST"; } } } } var dimensions = []; var params = config.params || {}; // to don't overwrite the changes in the applyDefaults delete config.params; for (var id = 0, ld = layerDef.dimensions.length ; id < ld ; id++) { var dimension = layerDef.dimensions[id]; dimensions.push(dimension.identifier); if (!params.hasOwnProperty(dimension.identifier)) { params[dimension.identifier] = dimension['default']; } } var projection = config.projection || matrixSet.supportedCRS.replace( /urn:ogc:def:crs:(\w+):(.*:)?(\w+)$/, "$1:$3"); var units = config.units || (projection === "EPSG:4326" ? "degrees" : "m"); var resolutions = []; for (var mid in matrixSet.matrixIds) { if (matrixSet.matrixIds.hasOwnProperty(mid)) { resolutions.push( matrixSet.matrixIds[mid].scaleDenominator * 0.28E-3 / OpenLayers.METERS_PER_INCH / OpenLayers.INCHES_PER_UNIT[units]); } } var url; if (requestEncoding === "REST" && layerDef.resourceUrls) { url = []; var resourceUrls = layerDef.resourceUrls, resourceUrl; for (var t = 0, tt = layerDef.resourceUrls.length; t < tt; ++t) { resourceUrl = layerDef.resourceUrls[t]; if (resourceUrl.format === format && resourceUrl.resourceType === "tile") { url.push(resourceUrl.template); } } } else { var httpGet = capabilities.operationsMetadata.GetTile.dcp.http.get; url = []; var constraint; for (var i = 0, ii = httpGet.length; i < ii; i++) { constraint = httpGet[i].constraints; if (!constraint || (constraint && constraint. GetEncoding.allowedValues[requestEncoding])) { url.push(httpGet[i].url); } } } return new OpenLayers.Layer.WMTS( OpenLayers.Util.applyDefaults(config, { url: url, requestEncoding: requestEncoding, name: layerDef.title, style: style.identifier, format: format, matrixIds: matrixSet.matrixIds, matrixSet: matrixSet.identifier, projection: projection, units: units, resolutions: config.isBaseLayer === false ? undefined : resolutions, serverResolutions: resolutions, tileFullExtent: matrixSet.bounds, dimensions: dimensions, params: params }) ); }, CLASS_NAME: "OpenLayers.Format.WMTSCapabilities" }); /* ====================================================================== OpenLayers/Format/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/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/Projection.js */ /** * Class: OpenLayers.Format.OSM * OSM parser. Create a new instance with the * <OpenLayers.Format.OSM> constructor. * * Inherits from: * - <OpenLayers.Format.XML> */ OpenLayers.Format.OSM = OpenLayers.Class(OpenLayers.Format.XML, { /** * APIProperty: checkTags * {Boolean} Should tags be checked to determine whether something * should be treated as a seperate node. Will slow down parsing. * Default is false. */ checkTags: false, /** * Property: interestingTagsExclude * {Array} List of tags to exclude from 'interesting' checks on nodes. * Must be set when creating the format. Will only be used if checkTags * is set. */ interestingTagsExclude: null, /** * APIProperty: areaTags * {Array} List of tags indicating that something is an area. * Must be set when creating the format. Will only be used if * checkTags is true. */ areaTags: null, /** * Constructor: OpenLayers.Format.OSM * Create a new parser for OSM. * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ initialize: function(options) { var layer_defaults = { 'interestingTagsExclude': ['source', 'source_ref', 'source:ref', 'history', 'attribution', 'created_by'], 'areaTags': ['area', 'building', 'leisure', 'tourism', 'ruins', 'historic', 'landuse', 'military', 'natural', 'sport'] }; layer_defaults = OpenLayers.Util.extend(layer_defaults, options); var interesting = {}; for (var i = 0; i < layer_defaults.interestingTagsExclude.length; i++) { interesting[layer_defaults.interestingTagsExclude[i]] = true; } layer_defaults.interestingTagsExclude = interesting; var area = {}; for (var i = 0; i < layer_defaults.areaTags.length; i++) { area[layer_defaults.areaTags[i]] = true; } layer_defaults.areaTags = area; // OSM coordinates are always in longlat WGS84 this.externalProjection = new OpenLayers.Projection("EPSG:4326"); OpenLayers.Format.XML.prototype.initialize.apply(this, [layer_defaults]); }, /** * APIMethod: read * Return a list of features from a OSM doc * Parameters: * doc - {Element} * * Returns: * Array({<OpenLayers.Feature.Vector>}) */ read: function(doc) { if (typeof doc == "string") { doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc]); } var nodes = this.getNodes(doc); var ways = this.getWays(doc); // Geoms will contain at least ways.length entries. var feat_list = new Array(ways.length); for (var i = 0; i < ways.length; i++) { // We know the minimal of this one ahead of time. (Could be -1 // due to areas/polygons) var point_list = new Array(ways[i].nodes.length); var poly = this.isWayArea(ways[i]) ? 1 : 0; for (var j = 0; j < ways[i].nodes.length; j++) { var node = nodes[ways[i].nodes[j]]; var point = new OpenLayers.Geometry.Point(node.lon, node.lat); // Since OSM is topological, we stash the node ID internally. point.osm_id = parseInt(ways[i].nodes[j]); point_list[j] = point; // We don't display nodes if they're used inside other // elements. node.used = true; } var geometry = null; if (poly) { geometry = new OpenLayers.Geometry.Polygon( new OpenLayers.Geometry.LinearRing(point_list)); } else { geometry = new OpenLayers.Geometry.LineString(point_list); } if (this.internalProjection && this.externalProjection) { geometry.transform(this.externalProjection, this.internalProjection); } var feat = new OpenLayers.Feature.Vector(geometry, ways[i].tags); feat.osm_id = parseInt(ways[i].id); feat.fid = "way." + feat.osm_id; feat_list[i] = feat; } for (var node_id in nodes) { var node = nodes[node_id]; if (!node.used || this.checkTags) { var tags = null; if (this.checkTags) { var result = this.getTags(node.node, true); if (node.used && !result[1]) { continue; } tags = result[0]; } else { tags = this.getTags(node.node); } var feat = new OpenLayers.Feature.Vector( new OpenLayers.Geometry.Point(node['lon'], node['lat']), tags); if (this.internalProjection && this.externalProjection) { feat.geometry.transform(this.externalProjection, this.internalProjection); } feat.osm_id = parseInt(node_id); feat.fid = "node." + feat.osm_id; feat_list.push(feat); } // Memory cleanup node.node = null; } return feat_list; }, /** * Method: getNodes * Return the node items from a doc. * * Parameters: * doc - {DOMElement} node to parse tags from */ getNodes: function(doc) { var node_list = doc.getElementsByTagName("node"); var nodes = {}; for (var i = 0; i < node_list.length; i++) { var node = node_list[i]; var id = node.getAttribute("id"); nodes[id] = { 'lat': node.getAttribute("lat"), 'lon': node.getAttribute("lon"), 'node': node }; } return nodes; }, /** * Method: getWays * Return the way items from a doc. * * Parameters: * doc - {DOMElement} node to parse tags from */ getWays: function(doc) { var way_list = doc.getElementsByTagName("way"); var return_ways = []; for (var i = 0; i < way_list.length; i++) { var way = way_list[i]; var way_object = { id: way.getAttribute("id") }; way_object.tags = this.getTags(way); var node_list = way.getElementsByTagName("nd"); way_object.nodes = new Array(node_list.length); for (var j = 0; j < node_list.length; j++) { way_object.nodes[j] = node_list[j].getAttribute("ref"); } return_ways.push(way_object); } return return_ways; }, /** * Method: getTags * Return the tags list attached to a specific DOM element. * * Parameters: * dom_node - {DOMElement} node to parse tags from * interesting_tags - {Boolean} whether the return from this function should * return a boolean indicating that it has 'interesting tags' -- * tags like attribution and source are ignored. (To change the list * of tags, see interestingTagsExclude) * * Returns: * tags - {Object} hash of tags * interesting - {Boolean} if interesting_tags is passed, returns * whether there are any interesting tags on this element. */ getTags: function(dom_node, interesting_tags) { var tag_list = dom_node.getElementsByTagName("tag"); var tags = {}; var interesting = false; for (var j = 0; j < tag_list.length; j++) { var key = tag_list[j].getAttribute("k"); tags[key] = tag_list[j].getAttribute("v"); if (interesting_tags) { if (!this.interestingTagsExclude[key]) { interesting = true; } } } return interesting_tags ? [tags, interesting] : tags; }, /** * Method: isWayArea * Given a way object from getWays, check whether the tags and geometry * indicate something is an area. * * Returns: * {Boolean} */ isWayArea: function(way) { var poly_shaped = false; var poly_tags = false; if (way.nodes[0] == way.nodes[way.nodes.length - 1]) { poly_shaped = true; } if (this.checkTags) { for(var key in way.tags) { if (this.areaTags[key]) { poly_tags = true; break; } } } return poly_shaped && (this.checkTags ? poly_tags : true); }, /** * APIMethod: write * Takes a list of features, returns a serialized OSM format file for use * in tools like JOSM. * * Parameters: * features - {Array(<OpenLayers.Feature.Vector>)} */ write: function(features) { if (!(OpenLayers.Util.isArray(features))) { features = [features]; } this.osm_id = 1; this.created_nodes = {}; var root_node = this.createElementNS(null, "osm"); root_node.setAttribute("version", "0.5"); root_node.setAttribute("generator", "OpenLayers "+ OpenLayers.VERSION_NUMBER); // Loop backwards, because the deserializer puts nodes last, and // we want them first if possible for(var i = features.length - 1; i >= 0; i--) { var nodes = this.createFeatureNodes(features[i]); for (var j = 0; j < nodes.length; j++) { root_node.appendChild(nodes[j]); } } return OpenLayers.Format.XML.prototype.write.apply(this, [root_node]); }, /** * Method: createFeatureNodes * Takes a feature, returns a list of nodes from size 0->n. * Will include all pieces of the serialization that are required which * have not already been created. Calls out to createXML based on geometry * type. * * Parameters: * feature - {<OpenLayers.Feature.Vector>} */ createFeatureNodes: function(feature) { var nodes = []; var className = feature.geometry.CLASS_NAME; var type = className.substring(className.lastIndexOf(".") + 1); type = type.toLowerCase(); var builder = this.createXML[type]; if (builder) { nodes = builder.apply(this, [feature]); } return nodes; }, /** * Method: createXML * Takes a feature, returns a list of nodes from size 0->n. * Will include all pieces of the serialization that are required which * have not already been created. * * Parameters: * feature - {<OpenLayers.Feature.Vector>} */ createXML: { 'point': function(point) { var id = null; var geometry = point.geometry ? point.geometry : point; if (this.internalProjection && this.externalProjection) { geometry = geometry.clone(); geometry.transform(this.internalProjection, this.externalProjection); } var already_exists = false; // We don't return anything if the node // has already been created if (point.osm_id) { id = point.osm_id; if (this.created_nodes[id]) { already_exists = true; } } else { id = -this.osm_id; this.osm_id++; } if (already_exists) { node = this.created_nodes[id]; } else { var node = this.createElementNS(null, "node"); } this.created_nodes[id] = node; node.setAttribute("id", id); node.setAttribute("lon", geometry.x); node.setAttribute("lat", geometry.y); if (point.attributes) { this.serializeTags(point, node); } this.setState(point, node); return already_exists ? [] : [node]; }, linestring: function(feature) { var id; var nodes = []; var geometry = feature.geometry; if (feature.osm_id) { id = feature.osm_id; } else { id = -this.osm_id; this.osm_id++; } var way = this.createElementNS(null, "way"); way.setAttribute("id", id); for (var i = 0; i < geometry.components.length; i++) { var node = this.createXML['point'].apply(this, [geometry.components[i]]); if (node.length) { node = node[0]; var node_ref = node.getAttribute("id"); nodes.push(node); } else { node_ref = geometry.components[i].osm_id; node = this.created_nodes[node_ref]; } this.setState(feature, node); var nd_dom = this.createElementNS(null, "nd"); nd_dom.setAttribute("ref", node_ref); way.appendChild(nd_dom); } this.serializeTags(feature, way); nodes.push(way); return nodes; }, polygon: function(feature) { var attrs = OpenLayers.Util.extend({'area':'yes'}, feature.attributes); var feat = new OpenLayers.Feature.Vector(feature.geometry.components[0], attrs); feat.osm_id = feature.osm_id; return this.createXML['linestring'].apply(this, [feat]); } }, /** * Method: serializeTags * Given a feature, serialize the attributes onto the given node. * * Parameters: * feature - {<OpenLayers.Feature.Vector>} * node - {DOMNode} */ serializeTags: function(feature, node) { for (var key in feature.attributes) { var tag = this.createElementNS(null, "tag"); tag.setAttribute("k", key); tag.setAttribute("v", feature.attributes[key]); node.appendChild(tag); } }, /** * Method: setState * OpenStreetMap has a convention that 'state' is stored for modification or deletion. * This allows the file to be uploaded via JOSM or the bulk uploader tool. * * Parameters: * feature - {<OpenLayers.Feature.Vector>} * node - {DOMNode} */ setState: function(feature, node) { if (feature.state) { var state = null; switch(feature.state) { case OpenLayers.State.UPDATE: state = "modify"; case OpenLayers.State.DELETE: state = "delete"; } if (state) { node.setAttribute("action", state); } } }, CLASS_NAME: "OpenLayers.Format.OSM" }); /* ====================================================================== OpenLayers/Format/WCSCapabilities.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 */ /** * Class: OpenLayers.Format.WCSCapabilities * Read WCS Capabilities. * * Inherits from: * - <OpenLayers.Format.XML.VersionedOGC> */ OpenLayers.Format.WCSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, { /** * APIProperty: defaultVersion * {String} Version number to assume if none found. Default is "1.1.0". */ defaultVersion: "1.1.0", /** * Constructor: OpenLayers.Format.WCSCapabilities * Create a new parser for WCS capabilities. * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ /** * APIMethod: read * Read capabilities data from a string, and return a list of coverages. * * Parameters: * data - {String} or {DOMElement} data to read/parse. * * Returns: * {Array} List of named coverages. */ CLASS_NAME: "OpenLayers.Format.WCSCapabilities" }); /* ====================================================================== OpenLayers/Format/Context.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 */ /** * Class: OpenLayers.Format.Context * Base class for both Format.WMC and Format.OWSContext * * Inherits from: * - <OpenLayers.Format.XML.VersionedOGC> */ OpenLayers.Format.Context = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, { /** * Property: layerOptions * {Object} Default options for layers created by the parser. These * options are overridden by the options which are read from the * capabilities document. */ layerOptions: null, /** * Property: layerParams * {Object} Default parameters for layers created by the parser. This * can be used e.g. to override DEFAULT_PARAMS for * OpenLayers.Layer.WMS. */ layerParams: null, /** * Constructor: OpenLayers.Format.Context * Create a new parser for Context documents. * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ /** * APIMethod: read * Read Context data from a string, and return an object with map * properties and a list of layers. * * Parameters: * data - {String} or {DOMElement} data to read/parse. * options - {Object} The options object must contain a map property. If * the map property is a string, it must be the id of a dom element * where the new map will be placed. If the map property is an * <OpenLayers.Map>, the layers from the context document will be added * to the map. * * Returns: * {<OpenLayers.Map>} A map based on the context. */ read: function(data, options) { var context = OpenLayers.Format.XML.VersionedOGC.prototype.read.apply(this, arguments); var map; if(options && options.map) { this.context = context; if(options.map instanceof OpenLayers.Map) { map = this.mergeContextToMap(context, options.map); } else { var mapOptions = options.map; if(OpenLayers.Util.isElement(mapOptions) || typeof mapOptions == "string") { // we assume mapOptions references a div // element mapOptions = {div: mapOptions}; } map = this.contextToMap(context, mapOptions); } } else { // not documented as part of the API, provided as a non-API option map = context; } return map; }, /** * Method: getLayerFromContext * Create a WMS layer from a layerContext object. * * Parameters: * layerContext - {Object} An object representing a WMS layer. * * Returns: * {<OpenLayers.Layer.WMS>} A WMS layer. */ getLayerFromContext: function(layerContext) { var i, len; // fill initial options object from layerContext var options = { queryable: layerContext.queryable, //keep queryable for api compatibility visibility: layerContext.visibility, maxExtent: layerContext.maxExtent, metadata: OpenLayers.Util.applyDefaults(layerContext.metadata, {styles: layerContext.styles, formats: layerContext.formats, "abstract": layerContext["abstract"], dataURL: layerContext.dataURL }), numZoomLevels: layerContext.numZoomLevels, units: layerContext.units, isBaseLayer: layerContext.isBaseLayer, opacity: layerContext.opacity, displayInLayerSwitcher: layerContext.displayInLayerSwitcher, singleTile: layerContext.singleTile, tileSize: (layerContext.tileSize) ? new OpenLayers.Size( layerContext.tileSize.width, layerContext.tileSize.height ) : undefined, minScale: layerContext.minScale || layerContext.maxScaleDenominator, maxScale: layerContext.maxScale || layerContext.minScaleDenominator, srs: layerContext.srs, dimensions: layerContext.dimensions, metadataURL: layerContext.metadataURL }; if (this.layerOptions) { OpenLayers.Util.applyDefaults(options, this.layerOptions); } var params = { layers: layerContext.name, transparent: layerContext.transparent, version: layerContext.version }; if (layerContext.formats && layerContext.formats.length>0) { // set default value for params if current attribute is not positionned params.format = layerContext.formats[0].value; for (i=0, len=layerContext.formats.length; i<len; i++) { var format = layerContext.formats[i]; if (format.current == true) { params.format = format.value; break; } } } if (layerContext.styles && layerContext.styles.length>0) { for (i=0, len=layerContext.styles.length; i<len; i++) { var style = layerContext.styles[i]; if (style.current == true) { // three style types to consider // 1) linked SLD // 2) inline SLD // 3) named style if(style.href) { params.sld = style.href; } else if(style.body) { params.sld_body = style.body; } else { params.styles = style.name; } break; } } } if (this.layerParams) { OpenLayers.Util.applyDefaults(params, this.layerParams); } var layer = null; var service = layerContext.service; if (service == OpenLayers.Format.Context.serviceTypes.WFS) { options.strategies = [new OpenLayers.Strategy.BBOX()]; options.protocol = new OpenLayers.Protocol.WFS({ url: layerContext.url, // since we do not know featureNS, let the protocol // determine it automagically using featurePrefix featurePrefix: layerContext.name.split(":")[0], featureType: layerContext.name.split(":").pop() }); layer = new OpenLayers.Layer.Vector( layerContext.title || layerContext.name, options ); } else if (service == OpenLayers.Format.Context.serviceTypes.KML) { // use a vector layer with an HTTP Protcol and a Fixed strategy options.strategies = [new OpenLayers.Strategy.Fixed()]; options.protocol = new OpenLayers.Protocol.HTTP({ url: layerContext.url, format: new OpenLayers.Format.KML() }); layer = new OpenLayers.Layer.Vector( layerContext.title || layerContext.name, options ); } else if (service == OpenLayers.Format.Context.serviceTypes.GML) { // use a vector layer with a HTTP Protocol and a Fixed strategy options.strategies = [new OpenLayers.Strategy.Fixed()]; options.protocol = new OpenLayers.Protocol.HTTP({ url: layerContext.url, format: new OpenLayers.Format.GML() }); layer = new OpenLayers.Layer.Vector( layerContext.title || layerContext.name, options ); } else if (layerContext.features) { // inline GML or KML features layer = new OpenLayers.Layer.Vector( layerContext.title || layerContext.name, options ); layer.addFeatures(layerContext.features); } else if (layerContext.categoryLayer !== true) { layer = new OpenLayers.Layer.WMS( layerContext.title || layerContext.name, layerContext.url, params, options ); } return layer; }, /** * Method: getLayersFromContext * Create an array of layers from an array of layerContext objects. * * Parameters: * layersContext - {Array(Object)} An array of objects representing layers. * * Returns: * {Array(<OpenLayers.Layer>)} An array of layers. */ getLayersFromContext: function(layersContext) { var layers = []; for (var i=0, len=layersContext.length; i<len; i++) { var layer = this.getLayerFromContext(layersContext[i]); if (layer !== null) { layers.push(layer); } } return layers; }, /** * Method: contextToMap * Create a map given a context object. * * Parameters: * context - {Object} The context object. * options - {Object} Default map options. * * Returns: * {<OpenLayers.Map>} A map based on the context object. */ contextToMap: function(context, options) { options = OpenLayers.Util.applyDefaults({ maxExtent: context.maxExtent, projection: context.projection, units: context.units }, options); if (options.maxExtent) { options.maxResolution = options.maxExtent.getWidth() / OpenLayers.Map.TILE_WIDTH; } var metadata = { contactInformation: context.contactInformation, "abstract": context["abstract"], keywords: context.keywords, logo: context.logo, descriptionURL: context.descriptionURL }; options.metadata = metadata; var map = new OpenLayers.Map(options); map.addLayers(this.getLayersFromContext(context.layersContext)); map.setCenter( context.bounds.getCenterLonLat(), map.getZoomForExtent(context.bounds, true) ); return map; }, /** * Method: mergeContextToMap * Add layers from a context object to a map. * * Parameters: * context - {Object} The context object. * map - {<OpenLayers.Map>} The map. * * Returns: * {<OpenLayers.Map>} The same map with layers added. */ mergeContextToMap: function(context, map) { map.addLayers(this.getLayersFromContext(context.layersContext)); return map; }, /** * APIMethod: write * Write a context document given a map. * * Parameters: * obj - {<OpenLayers.Map> | Object} A map or context object. * options - {Object} Optional configuration object. * * Returns: * {String} A context document string. */ write: function(obj, options) { obj = this.toContext(obj); return OpenLayers.Format.XML.VersionedOGC.prototype.write.apply(this, arguments); }, CLASS_NAME: "OpenLayers.Format.Context" }); /** * Constant: OpenLayers.Format.Context.serviceTypes * Enumeration for service types */ OpenLayers.Format.Context.serviceTypes = { "WMS": "urn:ogc:serviceType:WMS", "WFS": "urn:ogc:serviceType:WFS", "WCS": "urn:ogc:serviceType:WCS", "GML": "urn:ogc:serviceType:GML", "SLD": "urn:ogc:serviceType:SLD", "FES": "urn:ogc:serviceType:FES", "KML": "urn:ogc:serviceType:KML" }; /* ====================================================================== OpenLayers/Format/WMC.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/Context.js */ /** * Class: OpenLayers.Format.WMC * Read and write Web Map Context documents. * * Inherits from: * - <OpenLayers.Format.Context> */ OpenLayers.Format.WMC = OpenLayers.Class(OpenLayers.Format.Context, { /** * APIProperty: defaultVersion * {String} Version number to assume if none found. Default is "1.1.0". */ defaultVersion: "1.1.0", /** * Constructor: OpenLayers.Format.WMC * Create a new parser for Web Map Context documents. * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ /** * Method: layerToContext * Create a layer context object given a wms layer object. * * Parameters: * layer - {<OpenLayers.Layer.WMS>} The layer. * * Returns: * {Object} A layer context object. */ layerToContext: function(layer) { var parser = this.getParser(); var layerContext = { queryable: layer.queryable, visibility: layer.visibility, name: layer.params["LAYERS"], title: layer.name, "abstract": layer.metadata["abstract"], dataURL: layer.metadata.dataURL, metadataURL: layer.metadataURL, server: { version: layer.params["VERSION"], url: layer.url }, maxExtent: layer.maxExtent, transparent: layer.params["TRANSPARENT"], numZoomLevels: layer.numZoomLevels, units: layer.units, isBaseLayer: layer.isBaseLayer, opacity: layer.opacity == 1 ? undefined : layer.opacity, displayInLayerSwitcher: layer.displayInLayerSwitcher, singleTile: layer.singleTile, tileSize: (layer.singleTile || !layer.tileSize) ? undefined : {width: layer.tileSize.w, height: layer.tileSize.h}, minScale : (layer.options.resolutions || layer.options.scales || layer.options.maxResolution || layer.options.minScale) ? layer.minScale : undefined, maxScale : (layer.options.resolutions || layer.options.scales || layer.options.minResolution || layer.options.maxScale) ? layer.maxScale : undefined, formats: [], styles: [], srs: layer.srs, dimensions: layer.dimensions }; if (layer.metadata.servertitle) { layerContext.server.title = layer.metadata.servertitle; } if (layer.metadata.formats && layer.metadata.formats.length > 0) { for (var i=0, len=layer.metadata.formats.length; i<len; i++) { var format = layer.metadata.formats[i]; layerContext.formats.push({ value: format.value, current: (format.value == layer.params["FORMAT"]) }); } } else { layerContext.formats.push({ value: layer.params["FORMAT"], current: true }); } if (layer.metadata.styles && layer.metadata.styles.length > 0) { for (var i=0, len=layer.metadata.styles.length; i<len; i++) { var style = layer.metadata.styles[i]; if ((style.href == layer.params["SLD"]) || (style.body == layer.params["SLD_BODY"]) || (style.name == layer.params["STYLES"])) { style.current = true; } else { style.current = false; } layerContext.styles.push(style); } } else { layerContext.styles.push({ href: layer.params["SLD"], body: layer.params["SLD_BODY"], name: layer.params["STYLES"] || parser.defaultStyleName, title: parser.defaultStyleTitle, current: true }); } return layerContext; }, /** * Method: toContext * Create a context object free from layer given a map or a * context object. * * Parameters: * obj - {<OpenLayers.Map> | Object} The map or context. * * Returns: * {Object} A context object. */ toContext: function(obj) { var context = {}; var layers = obj.layers; if (obj.CLASS_NAME == "OpenLayers.Map") { var metadata = obj.metadata || {}; context.size = obj.getSize(); context.bounds = obj.getExtent(); context.projection = obj.projection; context.title = obj.title; context.keywords = metadata.keywords; context["abstract"] = metadata["abstract"]; context.logo = metadata.logo; context.descriptionURL = metadata.descriptionURL; context.contactInformation = metadata.contactInformation; context.maxExtent = obj.maxExtent; } else { // copy all obj properties except the "layers" property OpenLayers.Util.applyDefaults(context, obj); if (context.layers != undefined) { delete(context.layers); } } if (context.layersContext == undefined) { context.layersContext = []; } // let's convert layers into layersContext object (if any) if (layers != undefined && OpenLayers.Util.isArray(layers)) { for (var i=0, len=layers.length; i<len; i++) { var layer = layers[i]; if (layer instanceof OpenLayers.Layer.WMS) { context.layersContext.push(this.layerToContext(layer)); } } } return context; }, CLASS_NAME: "OpenLayers.Format.WMC" }); /* ====================================================================== OpenLayers/Format/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/Format/GML.js * @requires OpenLayers/Console.js * @requires OpenLayers/Lang.js */ /** * Class: OpenLayers.Format.WFS * Read/Write WFS. * * Inherits from: * - <OpenLayers.Format.GML> */ OpenLayers.Format.WFS = OpenLayers.Class(OpenLayers.Format.GML, { /** * Property: layer * {<OpenLayers.Layer>} */ layer: null, /** * APIProperty: wfsns * {String} */ wfsns: "http://www.opengis.net/wfs", /** * Property: ogcns * {String} */ ogcns: "http://www.opengis.net/ogc", /** * Constructor: OpenLayers.Format.WFS * Create a WFS-T formatter. This requires a layer: that layer should * have two properties: geometry_column and typename. The parser * for this format is subclassed entirely from GML: There is a writer * only, which uses most of the code from the GML layer, and wraps * it in transactional elements. * * Parameters: * options - {Object} * layer - {<OpenLayers.Layer>} */ initialize: function(options, layer) { OpenLayers.Format.GML.prototype.initialize.apply(this, [options]); this.layer = layer; if (this.layer.featureNS) { this.featureNS = this.layer.featureNS; } if (this.layer.options.geometry_column) { this.geometryName = this.layer.options.geometry_column; } if (this.layer.options.typename) { this.featureName = this.layer.options.typename; } }, /** * Method: write * Takes a feature list, and generates a WFS-T Transaction * * Parameters: * features - {Array(<OpenLayers.Feature.Vector>)} */ write: function(features) { var transaction = this.createElementNS(this.wfsns, 'wfs:Transaction'); transaction.setAttribute("version","1.0.0"); transaction.setAttribute("service","WFS"); for (var i=0; i < features.length; i++) { switch (features[i].state) { case OpenLayers.State.INSERT: transaction.appendChild(this.insert(features[i])); break; case OpenLayers.State.UPDATE: transaction.appendChild(this.update(features[i])); break; case OpenLayers.State.DELETE: transaction.appendChild(this.remove(features[i])); break; } } return OpenLayers.Format.XML.prototype.write.apply(this,[transaction]); }, /** * Method: createFeatureXML * * Parameters: * feature - {<OpenLayers.Feature.Vector>} */ createFeatureXML: function(feature) { var geometryNode = this.buildGeometryNode(feature.geometry); var geomContainer = this.createElementNS(this.featureNS, "feature:" + this.geometryName); geomContainer.appendChild(geometryNode); var featureContainer = this.createElementNS(this.featureNS, "feature:" + this.featureName); featureContainer.appendChild(geomContainer); for(var attr in feature.attributes) { var attrText = this.createTextNode(feature.attributes[attr]); var nodename = attr; if (attr.search(":") != -1) { nodename = attr.split(":")[1]; } var attrContainer = this.createElementNS(this.featureNS, "feature:" + nodename); attrContainer.appendChild(attrText); featureContainer.appendChild(attrContainer); } return featureContainer; }, /** * Method: insert * Takes a feature, and generates a WFS-T Transaction "Insert" * * Parameters: * feature - {<OpenLayers.Feature.Vector>} */ insert: function(feature) { var insertNode = this.createElementNS(this.wfsns, 'wfs:Insert'); insertNode.appendChild(this.createFeatureXML(feature)); return insertNode; }, /** * Method: update * Takes a feature, and generates a WFS-T Transaction "Update" * * Parameters: * feature - {<OpenLayers.Feature.Vector>} */ update: function(feature) { if (!feature.fid) { OpenLayers.Console.userError(OpenLayers.i18n("noFID")); } var updateNode = this.createElementNS(this.wfsns, 'wfs:Update'); updateNode.setAttribute("typeName", this.featurePrefix + ':' + this.featureName); updateNode.setAttribute("xmlns:" + this.featurePrefix, this.featureNS); var propertyNode = this.createElementNS(this.wfsns, 'wfs:Property'); var nameNode = this.createElementNS(this.wfsns, 'wfs:Name'); var txtNode = this.createTextNode(this.geometryName); nameNode.appendChild(txtNode); propertyNode.appendChild(nameNode); var valueNode = this.createElementNS(this.wfsns, 'wfs:Value'); var geometryNode = this.buildGeometryNode(feature.geometry); if(feature.layer){ geometryNode.setAttribute( "srsName", feature.layer.projection.getCode() ); } valueNode.appendChild(geometryNode); propertyNode.appendChild(valueNode); updateNode.appendChild(propertyNode); // add in attributes for(var propName in feature.attributes) { propertyNode = this.createElementNS(this.wfsns, 'wfs:Property'); nameNode = this.createElementNS(this.wfsns, 'wfs:Name'); nameNode.appendChild(this.createTextNode(propName)); propertyNode.appendChild(nameNode); valueNode = this.createElementNS(this.wfsns, 'wfs:Value'); valueNode.appendChild(this.createTextNode(feature.attributes[propName])); propertyNode.appendChild(valueNode); updateNode.appendChild(propertyNode); } var filterNode = this.createElementNS(this.ogcns, 'ogc:Filter'); var filterIdNode = this.createElementNS(this.ogcns, 'ogc:FeatureId'); filterIdNode.setAttribute("fid", feature.fid); filterNode.appendChild(filterIdNode); updateNode.appendChild(filterNode); return updateNode; }, /** * Method: remove * Takes a feature, and generates a WFS-T Transaction "Delete" * * Parameters: * feature - {<OpenLayers.Feature.Vector>} */ remove: function(feature) { if (!feature.fid) { OpenLayers.Console.userError(OpenLayers.i18n("noFID")); return false; } var deleteNode = this.createElementNS(this.wfsns, 'wfs:Delete'); deleteNode.setAttribute("typeName", this.featurePrefix + ':' + this.featureName); deleteNode.setAttribute("xmlns:" + this.featurePrefix, this.featureNS); var filterNode = this.createElementNS(this.ogcns, 'ogc:Filter'); var filterIdNode = this.createElementNS(this.ogcns, 'ogc:FeatureId'); filterIdNode.setAttribute("fid", feature.fid); filterNode.appendChild(filterIdNode); deleteNode.appendChild(filterNode); return deleteNode; }, /** * APIMethod: destroy * Remove ciruclar ref to layer */ destroy: function() { this.layer = null; }, CLASS_NAME: "OpenLayers.Format.WFS" }); /* ====================================================================== OpenLayers/Format/CSWGetDomain.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.CSWGetDomain * Default version is 2.0.2. * * Returns: * {<OpenLayers.Format>} A CSWGetDomain format of the given version. */ OpenLayers.Format.CSWGetDomain = function(options) { options = OpenLayers.Util.applyDefaults( options, OpenLayers.Format.CSWGetDomain.DEFAULTS ); var cls = OpenLayers.Format.CSWGetDomain["v"+options.version.replace(/\./g, "_")]; if(!cls) { throw "Unsupported CSWGetDomain version: " + options.version; } return new cls(options); }; /** * Constant: DEFAULTS * {Object} Default properties for the CSWGetDomain format. */ OpenLayers.Format.CSWGetDomain.DEFAULTS = { "version": "2.0.2" }; /* ====================================================================== OpenLayers/Format/CQL.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/WKT.js * @requires OpenLayers/Filter/Comparison.js * @requires OpenLayers/Filter/Logical.js * @requires OpenLayers/Filter/Spatial.js */ /** * Class: OpenLayers.Format.CQL * Read CQL strings to get <OpenLayers.Filter> objects. Write * <OpenLayers.Filter> objects to get CQL strings. Create a new parser with * the <OpenLayers.Format.CQL> constructor. * * Inherits from: * - <OpenLayers.Format> */ OpenLayers.Format.CQL = (function() { var tokens = [ "PROPERTY", "COMPARISON", "VALUE", "LOGICAL" ], patterns = { PROPERTY: /^[_a-zA-Z]\w*/, COMPARISON: /^(=|<>|<=|<|>=|>|LIKE)/i, IS_NULL: /^IS NULL/i, COMMA: /^,/, LOGICAL: /^(AND|OR)/i, VALUE: /^('([^']|'')*'|\d+(\.\d*)?|\.\d+)/, LPAREN: /^\(/, RPAREN: /^\)/, SPATIAL: /^(BBOX|INTERSECTS|DWITHIN|WITHIN|CONTAINS)/i, NOT: /^NOT/i, BETWEEN: /^BETWEEN/i, GEOMETRY: function(text) { var type = /^(POINT|LINESTRING|POLYGON|MULTIPOINT|MULTILINESTRING|MULTIPOLYGON|GEOMETRYCOLLECTION)/.exec(text); if (type) { var len = text.length; var idx = text.indexOf("(", type[0].length); if (idx > -1) { var depth = 1; while (idx < len && depth > 0) { idx++; switch(text.charAt(idx)) { case '(': depth++; break; case ')': depth--; break; default: // in default case, do nothing } } } return [text.substr(0, idx+1)]; } }, END: /^$/ }, follows = { LPAREN: ['GEOMETRY', 'SPATIAL', 'PROPERTY', 'VALUE', 'LPAREN'], RPAREN: ['NOT', 'LOGICAL', 'END', 'RPAREN'], PROPERTY: ['COMPARISON', 'BETWEEN', 'COMMA', 'IS_NULL'], BETWEEN: ['VALUE'], IS_NULL: ['END'], COMPARISON: ['VALUE'], COMMA: ['GEOMETRY', 'VALUE', 'PROPERTY'], VALUE: ['LOGICAL', 'COMMA', 'RPAREN', 'END'], SPATIAL: ['LPAREN'], LOGICAL: ['NOT', 'VALUE', 'SPATIAL', 'PROPERTY', 'LPAREN'], NOT: ['PROPERTY', 'LPAREN'], GEOMETRY: ['COMMA', 'RPAREN'] }, operators = { '=': OpenLayers.Filter.Comparison.EQUAL_TO, '<>': OpenLayers.Filter.Comparison.NOT_EQUAL_TO, '<': OpenLayers.Filter.Comparison.LESS_THAN, '<=': OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO, '>': OpenLayers.Filter.Comparison.GREATER_THAN, '>=': OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO, 'LIKE': OpenLayers.Filter.Comparison.LIKE, 'BETWEEN': OpenLayers.Filter.Comparison.BETWEEN, 'IS NULL': OpenLayers.Filter.Comparison.IS_NULL }, operatorReverse = {}, logicals = { 'AND': OpenLayers.Filter.Logical.AND, 'OR': OpenLayers.Filter.Logical.OR }, logicalReverse = {}, precedence = { 'RPAREN': 3, 'LOGICAL': 2, 'COMPARISON': 1 }; var i; for (i in operators) { if (operators.hasOwnProperty(i)) { operatorReverse[operators[i]] = i; } } for (i in logicals) { if (logicals.hasOwnProperty(i)) { logicalReverse[logicals[i]] = i; } } function tryToken(text, pattern) { if (pattern instanceof RegExp) { return pattern.exec(text); } else { return pattern(text); } } function nextToken(text, tokens) { var i, token, len = tokens.length; for (i=0; i<len; i++) { token = tokens[i]; var pat = patterns[token]; var matches = tryToken(text, pat); if (matches) { var match = matches[0]; var remainder = text.substr(match.length).replace(/^\s*/, ""); return { type: token, text: match, remainder: remainder }; } } var msg = "ERROR: In parsing: [" + text + "], expected one of: "; for (i=0; i<len; i++) { token = tokens[i]; msg += "\n " + token + ": " + patterns[token]; } throw new Error(msg); } function tokenize(text) { var results = []; var token, expect = ["NOT", "GEOMETRY", "SPATIAL", "PROPERTY", "LPAREN"]; do { token = nextToken(text, expect); text = token.remainder; expect = follows[token.type]; if (token.type != "END" && !expect) { throw new Error("No follows list for " + token.type); } results.push(token); } while (token.type != "END"); return results; } function buildAst(tokens) { var operatorStack = [], postfix = []; while (tokens.length) { var tok = tokens.shift(); switch (tok.type) { case "PROPERTY": case "GEOMETRY": case "VALUE": postfix.push(tok); break; case "COMPARISON": case "BETWEEN": case "IS_NULL": case "LOGICAL": var p = precedence[tok.type]; while (operatorStack.length > 0 && (precedence[operatorStack[operatorStack.length - 1].type] <= p) ) { postfix.push(operatorStack.pop()); } operatorStack.push(tok); break; case "SPATIAL": case "NOT": case "LPAREN": operatorStack.push(tok); break; case "RPAREN": while (operatorStack.length > 0 && (operatorStack[operatorStack.length - 1].type != "LPAREN") ) { postfix.push(operatorStack.pop()); } operatorStack.pop(); // toss out the LPAREN if (operatorStack.length > 0 && operatorStack[operatorStack.length-1].type == "SPATIAL") { postfix.push(operatorStack.pop()); } case "COMMA": case "END": break; default: throw new Error("Unknown token type " + tok.type); } } while (operatorStack.length > 0) { postfix.push(operatorStack.pop()); } function buildTree() { var tok = postfix.pop(); switch (tok.type) { case "LOGICAL": var rhs = buildTree(), lhs = buildTree(); return new OpenLayers.Filter.Logical({ filters: [lhs, rhs], type: logicals[tok.text.toUpperCase()] }); case "NOT": var operand = buildTree(); return new OpenLayers.Filter.Logical({ filters: [operand], type: OpenLayers.Filter.Logical.NOT }); case "BETWEEN": var min, max, property; postfix.pop(); // unneeded AND token here max = buildTree(); min = buildTree(); property = buildTree(); return new OpenLayers.Filter.Comparison({ property: property, lowerBoundary: min, upperBoundary: max, type: OpenLayers.Filter.Comparison.BETWEEN }); case "COMPARISON": var value = buildTree(), property = buildTree(); return new OpenLayers.Filter.Comparison({ property: property, value: value, type: operators[tok.text.toUpperCase()] }); case "IS_NULL": var property = buildTree(); return new OpenLayers.Filter.Comparison({ property: property, type: operators[tok.text.toUpperCase()] }); case "VALUE": var match = tok.text.match(/^'(.*)'$/); if (match) { return match[1].replace(/''/g, "'"); } else { return Number(tok.text); } case "SPATIAL": switch(tok.text.toUpperCase()) { case "BBOX": var maxy = buildTree(), maxx = buildTree(), miny = buildTree(), minx = buildTree(), prop = buildTree(); return new OpenLayers.Filter.Spatial({ type: OpenLayers.Filter.Spatial.BBOX, property: prop, value: OpenLayers.Bounds.fromArray( [minx, miny, maxx, maxy] ) }); case "INTERSECTS": var value = buildTree(), property = buildTree(); return new OpenLayers.Filter.Spatial({ type: OpenLayers.Filter.Spatial.INTERSECTS, property: property, value: value }); case "WITHIN": var value = buildTree(), property = buildTree(); return new OpenLayers.Filter.Spatial({ type: OpenLayers.Filter.Spatial.WITHIN, property: property, value: value }); case "CONTAINS": var value = buildTree(), property = buildTree(); return new OpenLayers.Filter.Spatial({ type: OpenLayers.Filter.Spatial.CONTAINS, property: property, value: value }); case "DWITHIN": var distance = buildTree(), value = buildTree(), property = buildTree(); return new OpenLayers.Filter.Spatial({ type: OpenLayers.Filter.Spatial.DWITHIN, value: value, property: property, distance: Number(distance) }); } case "GEOMETRY": return OpenLayers.Geometry.fromWKT(tok.text); default: return tok.text; } } var result = buildTree(); if (postfix.length > 0) { var msg = "Remaining tokens after building AST: \n"; for (var i = postfix.length - 1; i >= 0; i--) { msg += postfix[i].type + ": " + postfix[i].text + "\n"; } throw new Error(msg); } return result; } return OpenLayers.Class(OpenLayers.Format, { /** * APIMethod: read * Generate a filter from a CQL string. * Parameters: * text - {String} The CQL text. * * Returns: * {<OpenLayers.Filter>} A filter based on the CQL text. */ read: function(text) { var result = buildAst(tokenize(text)); if (this.keepData) { this.data = result; } return result; }, /** * APIMethod: write * Convert a filter into a CQL string. * Parameters: * filter - {<OpenLayers.Filter>} The filter. * * Returns: * {String} A CQL string based on the filter. */ write: function(filter) { if (filter instanceof OpenLayers.Geometry) { return filter.toString(); } switch (filter.CLASS_NAME) { case "OpenLayers.Filter.Spatial": switch(filter.type) { case OpenLayers.Filter.Spatial.BBOX: return "BBOX(" + filter.property + "," + filter.value.toBBOX() + ")"; case OpenLayers.Filter.Spatial.DWITHIN: return "DWITHIN(" + filter.property + ", " + this.write(filter.value) + ", " + filter.distance + ")"; case OpenLayers.Filter.Spatial.WITHIN: return "WITHIN(" + filter.property + ", " + this.write(filter.value) + ")"; case OpenLayers.Filter.Spatial.INTERSECTS: return "INTERSECTS(" + filter.property + ", " + this.write(filter.value) + ")"; case OpenLayers.Filter.Spatial.CONTAINS: return "CONTAINS(" + filter.property + ", " + this.write(filter.value) + ")"; default: throw new Error("Unknown spatial filter type: " + filter.type); } case "OpenLayers.Filter.Logical": if (filter.type == OpenLayers.Filter.Logical.NOT) { // TODO: deal with precedence of logical operators to // avoid extra parentheses (not urgent) return "NOT (" + this.write(filter.filters[0]) + ")"; } else { var res = "("; var first = true; for (var i = 0; i < filter.filters.length; i++) { if (first) { first = false; } else { res += ") " + logicalReverse[filter.type] + " ("; } res += this.write(filter.filters[i]); } return res + ")"; } case "OpenLayers.Filter.Comparison": if (filter.type == OpenLayers.Filter.Comparison.BETWEEN) { return filter.property + " BETWEEN " + this.write(filter.lowerBoundary) + " AND " + this.write(filter.upperBoundary); } else { return (filter.value !== null) ? filter.property + " " + operatorReverse[filter.type] + " " + this.write(filter.value) : filter.property + " " + operatorReverse[filter.type]; } case undefined: if (typeof filter === "string") { return "'" + filter.replace(/'/g, "''") + "'"; } else if (typeof filter === "number") { return String(filter); } default: throw new Error("Can't encode: " + filter.CLASS_NAME + " " + filter); } }, CLASS_NAME: "OpenLayers.Format.CQL" }); })(); /* ====================================================================== OpenLayers/Format/Text.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/Feature/Vector.js * @requires OpenLayers/Geometry/Point.js */ /** * Class: OpenLayers.Format.Text * Read Text format. Create a new instance with the <OpenLayers.Format.Text> * constructor. This reads text which is formatted like CSV text, using * tabs as the seperator by default. It provides parsing of data originally * used in the MapViewerService, described on the wiki. This Format is used * by the <OpenLayers.Layer.Text> class. * * Inherits from: * - <OpenLayers.Format> */ OpenLayers.Format.Text = OpenLayers.Class(OpenLayers.Format, { /** * APIProperty: defaultStyle * defaultStyle allows one to control the default styling of the features. * It should be a symbolizer hash. By default, this is set to match the * Layer.Text behavior, which is to use the default OpenLayers Icon. */ defaultStyle: null, /** * APIProperty: extractStyles * set to true to extract styles from the TSV files, using information * from the image or icon, iconSize and iconOffset fields. This will result * in features with a symbolizer (style) property set, using the * default symbolizer specified in <defaultStyle>. Set to false if you * wish to use a styleMap or OpenLayers.Style options to style your * layer instead. */ extractStyles: true, /** * Constructor: OpenLayers.Format.Text * Create a new parser for TSV Text. * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ initialize: function(options) { options = options || {}; if(options.extractStyles !== false) { options.defaultStyle = { 'externalGraphic': OpenLayers.Util.getImageLocation("marker.png"), 'graphicWidth': 21, 'graphicHeight': 25, 'graphicXOffset': -10.5, 'graphicYOffset': -12.5 }; } OpenLayers.Format.prototype.initialize.apply(this, [options]); }, /** * APIMethod: read * Return a list of features from a Tab Seperated Values text string. * * Parameters: * text - {String} * * Returns: * Array({<OpenLayers.Feature.Vector>}) */ read: function(text) { var lines = text.split('\n'); var columns; var features = []; // length - 1 to allow for trailing new line for (var lcv = 0; lcv < (lines.length - 1); lcv++) { var currLine = lines[lcv].replace(/^\s*/,'').replace(/\s*$/,''); if (currLine.charAt(0) != '#') { /* not a comment */ if (!columns) { //First line is columns columns = currLine.split('\t'); } else { var vals = currLine.split('\t'); var geometry = new OpenLayers.Geometry.Point(0,0); var attributes = {}; var style = this.defaultStyle ? OpenLayers.Util.applyDefaults({}, this.defaultStyle) : null; var icon, iconSize, iconOffset, overflow; var set = false; for (var valIndex = 0; valIndex < vals.length; valIndex++) { if (vals[valIndex]) { if (columns[valIndex] == 'point') { var coords = vals[valIndex].split(','); geometry.y = parseFloat(coords[0]); geometry.x = parseFloat(coords[1]); set = true; } else if (columns[valIndex] == 'lat') { geometry.y = parseFloat(vals[valIndex]); set = true; } else if (columns[valIndex] == 'lon') { geometry.x = parseFloat(vals[valIndex]); set = true; } else if (columns[valIndex] == 'title') attributes['title'] = vals[valIndex]; else if (columns[valIndex] == 'image' || columns[valIndex] == 'icon' && style) { style['externalGraphic'] = vals[valIndex]; } else if (columns[valIndex] == 'iconSize' && style) { var size = vals[valIndex].split(','); style['graphicWidth'] = parseFloat(size[0]); style['graphicHeight'] = parseFloat(size[1]); } else if (columns[valIndex] == 'iconOffset' && style) { var offset = vals[valIndex].split(','); style['graphicXOffset'] = parseFloat(offset[0]); style['graphicYOffset'] = parseFloat(offset[1]); } else if (columns[valIndex] == 'description') { attributes['description'] = vals[valIndex]; } else if (columns[valIndex] == 'overflow') { attributes['overflow'] = vals[valIndex]; } else { // For StyleMap filtering, allow additional // columns to be stored as attributes. attributes[columns[valIndex]] = vals[valIndex]; } } } if (set) { if (this.internalProjection && this.externalProjection) { geometry.transform(this.externalProjection, this.internalProjection); } var feature = new OpenLayers.Feature.Vector(geometry, attributes, style); features.push(feature); } } } } return features; }, CLASS_NAME: "OpenLayers.Format.Text" }); /* ====================================================================== OpenLayers/Format/Atom.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/v3.js * @requires OpenLayers/Feature/Vector.js */ /** * Class: OpenLayers.Format.Atom * Read/write Atom feeds. Create a new instance with the * <OpenLayers.Format.AtomFeed> constructor. * * Inherits from: * - <OpenLayers.Format.XML> */ OpenLayers.Format.Atom = OpenLayers.Class(OpenLayers.Format.XML, { /** * 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: { atom: "http://www.w3.org/2005/Atom", georss: "http://www.georss.org/georss" }, /** * APIProperty: feedTitle * {String} Atom feed elements require a title. Default is "untitled". */ feedTitle: "untitled", /** * APIProperty: defaultEntryTitle * {String} Atom entry elements require a title. In cases where one is * not provided in the feature attributes, this will be used. Default * is "untitled". */ defaultEntryTitle: "untitled", /** * Property: gmlParse * {Object} GML Format object for parsing features * Non-API and only created if necessary */ gmlParser: null, /** * APIProperty: xy * {Boolean} Order of the GML coordinate: true:(x,y) or false:(y,x) * For GeoRSS the default is (y,x), therefore: false */ xy: false, /** * Constructor: OpenLayers.Format.AtomEntry * Create a new parser for Atom. * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ /** * APIMethod: read * Return a list of features from an Atom feed or entry document. * Parameters: * doc - {Element} or {String} * * Returns: * Array({<OpenLayers.Feature.Vector>}) */ read: function(doc) { if (typeof doc == "string") { doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc]); } return this.parseFeatures(doc); }, /** * APIMethod: write * Serialize or more feature nodes to Atom documents. * * Parameters: * features - {<OpenLayers.Feature.Vector>} or Array({<OpenLayers.Feature.Vector>}) * * Returns: * {String} an Atom entry document if passed one feature node, or a feed * document if passed an array of feature nodes. */ write: function(features) { var doc; if (OpenLayers.Util.isArray(features)) { doc = this.createElementNSPlus("atom:feed"); doc.appendChild( this.createElementNSPlus("atom:title", { value: this.feedTitle }) ); for (var i=0, ii=features.length; i<ii; i++) { doc.appendChild(this.buildEntryNode(features[i])); } } else { doc = this.buildEntryNode(features); } return OpenLayers.Format.XML.prototype.write.apply(this, [doc]); }, /** * Method: buildContentNode * * Parameters: * content - {Object} * * Returns: * {DOMElement} an Atom content node. * * TODO: types other than text. */ buildContentNode: function(content) { var node = this.createElementNSPlus("atom:content", { attributes: { type: content.type || null } }); if (content.src) { node.setAttribute("src", content.src); } else { if (content.type == "text" || content.type == null) { node.appendChild( this.createTextNode(content.value) ); } else if (content.type == "html") { if (typeof content.value != "string") { throw "HTML content must be in form of an escaped string"; } node.appendChild( this.createTextNode(content.value) ); } else if (content.type == "xhtml") { node.appendChild(content.value); } else if (content.type == "xhtml" || content.type.match(/(\+|\/)xml$/)) { node.appendChild(content.value); } else { // MUST be a valid Base64 encoding node.appendChild( this.createTextNode(content.value) ); } } return node; }, /** * Method: buildEntryNode * Build an Atom entry node from a feature object. * * Parameters: * feature - {<OpenLayers.Feature.Vector>} * * Returns: * {DOMElement} an Atom entry node. * * These entries are geared for publication using AtomPub. * * TODO: support extension elements */ buildEntryNode: function(feature) { var attrib = feature.attributes; var atomAttrib = attrib.atom || {}; var entryNode = this.createElementNSPlus("atom:entry"); // atom:author if (atomAttrib.authors) { var authors = OpenLayers.Util.isArray(atomAttrib.authors) ? atomAttrib.authors : [atomAttrib.authors]; for (var i=0, ii=authors.length; i<ii; i++) { entryNode.appendChild( this.buildPersonConstructNode( "author", authors[i] ) ); } } // atom:category if (atomAttrib.categories) { var categories = OpenLayers.Util.isArray(atomAttrib.categories) ? atomAttrib.categories : [atomAttrib.categories]; var category; for (var i=0, ii=categories.length; i<ii; i++) { category = categories[i]; entryNode.appendChild( this.createElementNSPlus("atom:category", { attributes: { term: category.term, scheme: category.scheme || null, label: category.label || null } }) ); } } // atom:content if (atomAttrib.content) { entryNode.appendChild(this.buildContentNode(atomAttrib.content)); } // atom:contributor if (atomAttrib.contributors) { var contributors = OpenLayers.Util.isArray(atomAttrib.contributors) ? atomAttrib.contributors : [atomAttrib.contributors]; for (var i=0, ii=contributors.length; i<ii; i++) { entryNode.appendChild( this.buildPersonConstructNode( "contributor", contributors[i] ) ); } } // atom:id if (feature.fid) { entryNode.appendChild( this.createElementNSPlus("atom:id", { value: feature.fid }) ); } // atom:link if (atomAttrib.links) { var links = OpenLayers.Util.isArray(atomAttrib.links) ? atomAttrib.links : [atomAttrib.links]; var link; for (var i=0, ii=links.length; i<ii; i++) { link = links[i]; entryNode.appendChild( this.createElementNSPlus("atom:link", { attributes: { href: link.href, rel: link.rel || null, type: link.type || null, hreflang: link.hreflang || null, title: link.title || null, length: link.length || null } }) ); } } // atom:published if (atomAttrib.published) { entryNode.appendChild( this.createElementNSPlus("atom:published", { value: atomAttrib.published }) ); } // atom:rights if (atomAttrib.rights) { entryNode.appendChild( this.createElementNSPlus("atom:rights", { value: atomAttrib.rights }) ); } // atom:source not implemented // atom:summary if (atomAttrib.summary || attrib.description) { entryNode.appendChild( this.createElementNSPlus("atom:summary", { value: atomAttrib.summary || attrib.description }) ); } // atom:title entryNode.appendChild( this.createElementNSPlus("atom:title", { value: atomAttrib.title || attrib.title || this.defaultEntryTitle }) ); // atom:updated if (atomAttrib.updated) { entryNode.appendChild( this.createElementNSPlus("atom:updated", { value: atomAttrib.updated }) ); } // georss:where if (feature.geometry) { var whereNode = this.createElementNSPlus("georss:where"); whereNode.appendChild( this.buildGeometryNode(feature.geometry) ); entryNode.appendChild(whereNode); } return entryNode; }, /** * Method: initGmlParser * Creates a GML parser. */ initGmlParser: function() { this.gmlParser = new OpenLayers.Format.GML.v3({ xy: this.xy, featureNS: "http://example.com#feature", internalProjection: this.internalProjection, externalProjection: this.externalProjection }); }, /** * Method: buildGeometryNode * builds a GeoRSS node with a given geometry * * Parameters: * geometry - {<OpenLayers.Geometry>} * * Returns: * {DOMElement} A gml node. */ buildGeometryNode: function(geometry) { if (!this.gmlParser) { this.initGmlParser(); } var node = this.gmlParser.writeNode("feature:_geometry", geometry); return node.firstChild; }, /** * Method: buildPersonConstructNode * * Parameters: * name - {String} * value - {Object} * * Returns: * {DOMElement} an Atom person construct node. * * Example: * >>> buildPersonConstructNode("author", {name: "John Smith"}) * {<author><name>John Smith</name></author>} * * TODO: how to specify extension elements? Add to the oNames array? */ buildPersonConstructNode: function(name, value) { var oNames = ["uri", "email"]; var personNode = this.createElementNSPlus("atom:" + name); personNode.appendChild( this.createElementNSPlus("atom:name", { value: value.name }) ); for (var i=0, ii=oNames.length; i<ii; i++) { if (value[oNames[i]]) { personNode.appendChild( this.createElementNSPlus("atom:" + oNames[i], { value: value[oNames[i]] }) ); } } return personNode; }, /** * Method: getFirstChildValue * * Parameters: * node - {DOMElement} * nsuri - {String} Child node namespace uri ("*" for any). * name - {String} Child node name. * def - {String} Optional string default to return if no child found. * * Returns: * {String} The value of the first child with the given tag name. Returns * default value or empty string if none found. */ getFirstChildValue: function(node, nsuri, name, def) { var value; var nodes = this.getElementsByTagNameNS(node, nsuri, name); if (nodes && nodes.length > 0) { value = this.getChildValue(nodes[0], def); } else { value = def; } return value; }, /** * Method: parseFeature * Parse feature from an Atom entry node.. * * Parameters: * node - {DOMElement} An Atom entry or feed node. * * Returns: * {<OpenLayers.Feature.Vector>} */ parseFeature: function(node) { var atomAttrib = {}; var value = null; var nodes = null; var attval = null; var atomns = this.namespaces.atom; // atomAuthor* this.parsePersonConstructs(node, "author", atomAttrib); // atomCategory* nodes = this.getElementsByTagNameNS(node, atomns, "category"); if (nodes.length > 0) { atomAttrib.categories = []; } for (var i=0, ii=nodes.length; i<ii; i++) { value = {}; value.term = nodes[i].getAttribute("term"); attval = nodes[i].getAttribute("scheme"); if (attval) { value.scheme = attval; } attval = nodes[i].getAttribute("label"); if (attval) { value.label = attval; } atomAttrib.categories.push(value); } // atomContent? nodes = this.getElementsByTagNameNS(node, atomns, "content"); if (nodes.length > 0) { value = {}; attval = nodes[0].getAttribute("type"); if (attval) { value.type = attval; } attval = nodes[0].getAttribute("src"); if (attval) { value.src = attval; } else { if (value.type == "text" || value.type == "html" || value.type == null ) { value.value = this.getFirstChildValue( node, atomns, "content", null ); } else if (value.type == "xhtml" || value.type.match(/(\+|\/)xml$/)) { value.value = this.getChildEl(nodes[0]); } else { // MUST be base64 encoded value.value = this.getFirstChildValue( node, atomns, "content", null ); } atomAttrib.content = value; } } // atomContributor* this.parsePersonConstructs(node, "contributor", atomAttrib); // atomId atomAttrib.id = this.getFirstChildValue(node, atomns, "id", null); // atomLink* nodes = this.getElementsByTagNameNS(node, atomns, "link"); if (nodes.length > 0) { atomAttrib.links = new Array(nodes.length); } var oAtts = ["rel", "type", "hreflang", "title", "length"]; for (var i=0, ii=nodes.length; i<ii; i++) { value = {}; value.href = nodes[i].getAttribute("href"); for (var j=0, jj=oAtts.length; j<jj; j++) { attval = nodes[i].getAttribute(oAtts[j]); if (attval) { value[oAtts[j]] = attval; } } atomAttrib.links[i] = value; } // atomPublished? value = this.getFirstChildValue(node, atomns, "published", null); if (value) { atomAttrib.published = value; } // atomRights? value = this.getFirstChildValue(node, atomns, "rights", null); if (value) { atomAttrib.rights = value; } // atomSource? -- not implemented // atomSummary? value = this.getFirstChildValue(node, atomns, "summary", null); if (value) { atomAttrib.summary = value; } // atomTitle atomAttrib.title = this.getFirstChildValue( node, atomns, "title", null ); // atomUpdated atomAttrib.updated = this.getFirstChildValue( node, atomns, "updated", null ); var featureAttrib = { title: atomAttrib.title, description: atomAttrib.summary, atom: atomAttrib }; var geometry = this.parseLocations(node)[0]; var feature = new OpenLayers.Feature.Vector(geometry, featureAttrib); feature.fid = atomAttrib.id; return feature; }, /** * Method: parseFeatures * Return features from an Atom entry or feed. * * Parameters: * node - {DOMElement} An Atom entry or feed node. * * Returns: * Array({<OpenLayers.Feature.Vector>}) */ parseFeatures: function(node) { var features = []; var entries = this.getElementsByTagNameNS( node, this.namespaces.atom, "entry" ); if (entries.length == 0) { entries = [node]; } for (var i=0, ii=entries.length; i<ii; i++) { features.push(this.parseFeature(entries[i])); } return features; }, /** * Method: parseLocations * Parse the locations from an Atom entry or feed. * * Parameters: * node - {DOMElement} An Atom entry or feed node. * * Returns: * Array({<OpenLayers.Geometry>}) */ parseLocations: function(node) { var georssns = this.namespaces.georss; var locations = {components: []}; var where = this.getElementsByTagNameNS(node, georssns, "where"); if (where && where.length > 0) { if (!this.gmlParser) { this.initGmlParser(); } for (var i=0, ii=where.length; i<ii; i++) { this.gmlParser.readChildNodes(where[i], locations); } } var components = locations.components; var point = this.getElementsByTagNameNS(node, georssns, "point"); if (point && point.length > 0) { for (var i=0, ii=point.length; i<ii; i++) { var xy = OpenLayers.String.trim( point[i].firstChild.nodeValue ).split(/\s+/); if (xy.length !=2) { xy = OpenLayers.String.trim( point[i].firstChild.nodeValue ).split(/\s*,\s*/); } components.push(new OpenLayers.Geometry.Point(xy[1], xy[0])); } } var line = this.getElementsByTagNameNS(node, georssns, "line"); if (line && line.length > 0) { var coords; var p; var points; for (var i=0, ii=line.length; i<ii; i++) { coords = OpenLayers.String.trim( line[i].firstChild.nodeValue ).split(/\s+/); points = []; for (var j=0, jj=coords.length; j<jj; j+=2) { p = new OpenLayers.Geometry.Point(coords[j+1], coords[j]); points.push(p); } components.push( new OpenLayers.Geometry.LineString(points) ); } } var polygon = this.getElementsByTagNameNS(node, georssns, "polygon"); if (polygon && polygon.length > 0) { var coords; var p; var points; for (var i=0, ii=polygon.length; i<ii; i++) { coords = OpenLayers.String.trim( polygon[i].firstChild.nodeValue ).split(/\s+/); points = []; for (var j=0, jj=coords.length; j<jj; j+=2) { p = new OpenLayers.Geometry.Point(coords[j+1], coords[j]); points.push(p); } components.push( new OpenLayers.Geometry.Polygon( [new OpenLayers.Geometry.LinearRing(points)] ) ); } } if (this.internalProjection && this.externalProjection) { for (var i=0, ii=components.length; i<ii; i++) { if (components[i]) { components[i].transform( this.externalProjection, this.internalProjection ); } } } return components; }, /** * Method: parsePersonConstruct * Parse Atom person constructs from an Atom entry node. * * Parameters: * node - {DOMElement} An Atom entry or feed node. * name - {String} Construcy name ("author" or "contributor") * data = {Object} Object in which to put parsed persons. * * Returns: * An {Object}. */ parsePersonConstructs: function(node, name, data) { var persons = []; var atomns = this.namespaces.atom; var nodes = this.getElementsByTagNameNS(node, atomns, name); var oAtts = ["uri", "email"]; for (var i=0, ii=nodes.length; i<ii; i++) { var value = {}; value.name = this.getFirstChildValue( nodes[i], atomns, "name", null ); for (var j=0, jj=oAtts.length; j<jj; j++) { var attval = this.getFirstChildValue( nodes[i], atomns, oAtts[j], null); if (attval) { value[oAtts[j]] = attval; } } persons.push(value); } if (persons.length > 0) { data[name + "s"] = persons; } }, CLASS_NAME: "OpenLayers.Format.Atom" }); /* ====================================================================== OpenLayers/Format/SOSCapabilities.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 */ /** * Class: OpenLayers.Format.SOSCapabilities * Read SOS Capabilities. * * Inherits from: * - <OpenLayers.Format.XML.VersionedOGC> */ OpenLayers.Format.SOSCapabilities = 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", /** * Constructor: OpenLayers.Format.SOSCapabilities * Create a new parser for SOS Capabilities. * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ /** * APIMethod: read * Read capabilities data from a string, and return information about * the service (offering and observedProperty mostly). * * Parameters: * data - {String} or {DOMElement} data to read/parse. * * Returns: * {Object} Info about the SOS */ CLASS_NAME: "OpenLayers.Format.SOSCapabilities" }); /* ====================================================================== OpenLayers/Format/WMSCapabilities.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 */ /** * Class: OpenLayers.Format.WMSCapabilities * Read WMS Capabilities. * * Inherits from: * - <OpenLayers.Format.XML.VersionedOGC> */ OpenLayers.Format.WMSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, { /** * APIProperty: defaultVersion * {String} Version number to assume if none found. Default is "1.1.1". */ defaultVersion: "1.1.1", /** * APIProperty: profile * {String} If provided, use a custom profile. * * Currently supported profiles: * - WMSC - parses vendor specific capabilities for WMS-C. */ profile: null, /** * Constructor: OpenLayers.Format.WMSCapabilities * Create a new parser for WMS capabilities. * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ /** * APIMethod: read * Read capabilities data from a string, and return a list of layers. * * Parameters: * data - {String} or {DOMElement} data to read/parse. * * Returns: * {Array} List of named layers. */ CLASS_NAME: "OpenLayers.Format.WMSCapabilities" }); /* ====================================================================== OpenLayers/Format/GeoRSS.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/LineString.js * @requires OpenLayers/Geometry/Polygon.js */ /** * Class: OpenLayers.Format.GeoRSS * Read/write GeoRSS parser. Create a new instance with the * <OpenLayers.Format.GeoRSS> constructor. * * Inherits from: * - <OpenLayers.Format.XML> */ OpenLayers.Format.GeoRSS = OpenLayers.Class(OpenLayers.Format.XML, { /** * APIProperty: rssns * {String} RSS namespace to use. Defaults to * "http://backend.userland.com/rss2" */ rssns: "http://backend.userland.com/rss2", /** * APIProperty: featurens * {String} Feature Attributes namespace. Defaults to * "http://mapserver.gis.umn.edu/mapserver" */ featureNS: "http://mapserver.gis.umn.edu/mapserver", /** * APIProperty: georssns * {String} GeoRSS namespace to use. Defaults to * "http://www.georss.org/georss" */ georssns: "http://www.georss.org/georss", /** * APIProperty: geons * {String} W3C Geo namespace to use. Defaults to * "http://www.w3.org/2003/01/geo/wgs84_pos#" */ geons: "http://www.w3.org/2003/01/geo/wgs84_pos#", /** * APIProperty: featureTitle * {String} Default title for features. Defaults to "Untitled" */ featureTitle: "Untitled", /** * APIProperty: featureDescription * {String} Default description for features. Defaults to "No Description" */ featureDescription: "No Description", /** * Property: gmlParse * {Object} GML Format object for parsing features * Non-API and only created if necessary */ gmlParser: null, /** * APIProperty: xy * {Boolean} Order of the GML coordinate: true:(x,y) or false:(y,x) * For GeoRSS the default is (y,x), therefore: false */ xy: false, /** * Constructor: OpenLayers.Format.GeoRSS * Create a new parser for GeoRSS. * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ /** * Method: createGeometryFromItem * Return a geometry from a GeoRSS Item. * * Parameters: * item - {DOMElement} A GeoRSS item node. * * Returns: * {<OpenLayers.Geometry>} A geometry representing the node. */ createGeometryFromItem: function(item) { var point = this.getElementsByTagNameNS(item, this.georssns, "point"); var lat = this.getElementsByTagNameNS(item, this.geons, 'lat'); var lon = this.getElementsByTagNameNS(item, this.geons, 'long'); var line = this.getElementsByTagNameNS(item, this.georssns, "line"); var polygon = this.getElementsByTagNameNS(item, this.georssns, "polygon"); var where = this.getElementsByTagNameNS(item, this.georssns, "where"); var box = this.getElementsByTagNameNS(item, this.georssns, "box"); if (point.length > 0 || (lat.length > 0 && lon.length > 0)) { var location; if (point.length > 0) { location = OpenLayers.String.trim( point[0].firstChild.nodeValue).split(/\s+/); if (location.length !=2) { location = OpenLayers.String.trim( point[0].firstChild.nodeValue).split(/\s*,\s*/); } } else { location = [parseFloat(lat[0].firstChild.nodeValue), parseFloat(lon[0].firstChild.nodeValue)]; } var geometry = new OpenLayers.Geometry.Point(location[1], location[0]); } else if (line.length > 0) { var coords = OpenLayers.String.trim(this.getChildValue(line[0])).split(/\s+/); var components = []; var point; for (var i=0, len=coords.length; i<len; i+=2) { point = new OpenLayers.Geometry.Point(coords[i+1], coords[i]); components.push(point); } geometry = new OpenLayers.Geometry.LineString(components); } else if (polygon.length > 0) { var coords = OpenLayers.String.trim(this.getChildValue(polygon[0])).split(/\s+/); var components = []; var point; for (var i=0, len=coords.length; i<len; i+=2) { point = new OpenLayers.Geometry.Point(coords[i+1], coords[i]); components.push(point); } geometry = new OpenLayers.Geometry.Polygon([new OpenLayers.Geometry.LinearRing(components)]); } else if (where.length > 0) { if (!this.gmlParser) { this.gmlParser = new OpenLayers.Format.GML({'xy': this.xy}); } var feature = this.gmlParser.parseFeature(where[0]); geometry = feature.geometry; } else if (box.length > 0) { var coords = OpenLayers.String.trim(box[0].firstChild.nodeValue).split(/\s+/); var components = []; var point; if (coords.length > 3) { point = new OpenLayers.Geometry.Point(coords[1], coords[0]); components.push(point); point = new OpenLayers.Geometry.Point(coords[1], coords[2]); components.push(point); point = new OpenLayers.Geometry.Point(coords[3], coords[2]); components.push(point); point = new OpenLayers.Geometry.Point(coords[3], coords[0]); components.push(point); point = new OpenLayers.Geometry.Point(coords[1], coords[0]); components.push(point); } geometry = new OpenLayers.Geometry.Polygon([new OpenLayers.Geometry.LinearRing(components)]); } if (geometry && this.internalProjection && this.externalProjection) { geometry.transform(this.externalProjection, this.internalProjection); } return geometry; }, /** * Method: createFeatureFromItem * Return a feature from a GeoRSS Item. * * Parameters: * item - {DOMElement} A GeoRSS item node. * * Returns: * {<OpenLayers.Feature.Vector>} A feature representing the item. */ createFeatureFromItem: function(item) { var geometry = this.createGeometryFromItem(item); /* Provide defaults for title and description */ var title = this._getChildValue(item, "*", "title", this.featureTitle); /* First try RSS descriptions, then Atom summaries */ var description = this._getChildValue( item, "*", "description", this._getChildValue(item, "*", "content", this._getChildValue(item, "*", "summary", this.featureDescription))); /* If no link URL is found in the first child node, try the href attribute */ var link = this._getChildValue(item, "*", "link"); if(!link) { try { link = this.getElementsByTagNameNS(item, "*", "link")[0].getAttribute("href"); } catch(e) { link = null; } } var id = this._getChildValue(item, "*", "id", null); var data = { "title": title, "description": description, "link": link }; var feature = new OpenLayers.Feature.Vector(geometry, data); feature.fid = id; return feature; }, /** * Method: _getChildValue * * Parameters: * node - {DOMElement} * nsuri - {String} Child node namespace uri ("*" for any). * name - {String} Child node name. * def - {String} Optional string default to return if no child found. * * Returns: * {String} The value of the first child with the given tag name. Returns * default value or empty string if none found. */ _getChildValue: function(node, nsuri, name, def) { var value; var eles = this.getElementsByTagNameNS(node, nsuri, name); if(eles && eles[0] && eles[0].firstChild && eles[0].firstChild.nodeValue) { value = this.getChildValue(eles[0]); } else { value = (def == undefined) ? "" : def; } return value; }, /** * APIMethod: read * Return a list of features from a GeoRSS doc * * Parameters: * doc - {Element} * * Returns: * {Array(<OpenLayers.Feature.Vector>)} */ read: function(doc) { if (typeof doc == "string") { doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc]); } /* Try RSS items first, then Atom entries */ var itemlist = null; itemlist = this.getElementsByTagNameNS(doc, '*', 'item'); if (itemlist.length == 0) { itemlist = this.getElementsByTagNameNS(doc, '*', 'entry'); } var numItems = itemlist.length; var features = new Array(numItems); for(var i=0; i<numItems; i++) { features[i] = this.createFeatureFromItem(itemlist[i]); } return features; }, /** * APIMethod: write * Accept Feature Collection, and return a string. * * Parameters: * features - {Array(<OpenLayers.Feature.Vector>)} List of features to serialize into a string. */ write: function(features) { var georss; if(OpenLayers.Util.isArray(features)) { georss = this.createElementNS(this.rssns, "rss"); for(var i=0, len=features.length; i<len; i++) { georss.appendChild(this.createFeatureXML(features[i])); } } else { georss = this.createFeatureXML(features); } return OpenLayers.Format.XML.prototype.write.apply(this, [georss]); }, /** * Method: createFeatureXML * Accept an <OpenLayers.Feature.Vector>, and build a geometry for it. * * Parameters: * feature - {<OpenLayers.Feature.Vector>} * * Returns: * {DOMElement} */ createFeatureXML: function(feature) { var geometryNode = this.buildGeometryNode(feature.geometry); var featureNode = this.createElementNS(this.rssns, "item"); var titleNode = this.createElementNS(this.rssns, "title"); titleNode.appendChild(this.createTextNode(feature.attributes.title ? feature.attributes.title : "")); var descNode = this.createElementNS(this.rssns, "description"); descNode.appendChild(this.createTextNode(feature.attributes.description ? feature.attributes.description : "")); featureNode.appendChild(titleNode); featureNode.appendChild(descNode); if (feature.attributes.link) { var linkNode = this.createElementNS(this.rssns, "link"); linkNode.appendChild(this.createTextNode(feature.attributes.link)); featureNode.appendChild(linkNode); } for(var attr in feature.attributes) { if (attr == "link" || attr == "title" || attr == "description") { continue; } var attrText = this.createTextNode(feature.attributes[attr]); var nodename = attr; if (attr.search(":") != -1) { nodename = attr.split(":")[1]; } var attrContainer = this.createElementNS(this.featureNS, "feature:"+nodename); attrContainer.appendChild(attrText); featureNode.appendChild(attrContainer); } featureNode.appendChild(geometryNode); return featureNode; }, /** * Method: buildGeometryNode * builds a GeoRSS node with a given geometry * * Parameters: * geometry - {<OpenLayers.Geometry>} * * Returns: * {DOMElement} A gml node. */ buildGeometryNode: function(geometry) { if (this.internalProjection && this.externalProjection) { geometry = geometry.clone(); geometry.transform(this.internalProjection, this.externalProjection); } var node; // match Polygon if (geometry.CLASS_NAME == "OpenLayers.Geometry.Polygon") { node = this.createElementNS(this.georssns, 'georss:polygon'); node.appendChild(this.buildCoordinatesNode(geometry.components[0])); } // match LineString else if (geometry.CLASS_NAME == "OpenLayers.Geometry.LineString") { node = this.createElementNS(this.georssns, 'georss:line'); node.appendChild(this.buildCoordinatesNode(geometry)); } // match Point else if (geometry.CLASS_NAME == "OpenLayers.Geometry.Point") { node = this.createElementNS(this.georssns, 'georss:point'); node.appendChild(this.buildCoordinatesNode(geometry)); } else { throw "Couldn't parse " + geometry.CLASS_NAME; } return node; }, /** * Method: buildCoordinatesNode * * Parameters: * geometry - {<OpenLayers.Geometry>} */ buildCoordinatesNode: function(geometry) { var points = null; if (geometry.components) { points = geometry.components; } var path; if (points) { var numPoints = points.length; var parts = new Array(numPoints); for (var i = 0; i < numPoints; i++) { parts[i] = points[i].y + " " + points[i].x; } path = parts.join(" "); } else { path = geometry.y + " " + geometry.x; } return this.createTextNode(path); }, CLASS_NAME: "OpenLayers.Format.GeoRSS" }); /* ====================================================================== OpenLayers/Format/SLD.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/Style.js * @requires OpenLayers/Rule.js * @requires OpenLayers/Filter/FeatureId.js * @requires OpenLayers/Filter/Logical.js * @requires OpenLayers/Filter/Comparison.js * @requires OpenLayers/Filter/Spatial.js */ /** * Class: OpenLayers.Format.SLD * Read/Write SLD. Create a new instance with the <OpenLayers.Format.SLD> * constructor. * * Inherits from: * - <OpenLayers.Format.XML.VersionedOGC> */ OpenLayers.Format.SLD = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, { /** * APIProperty: profile * {String} If provided, use a custom profile. * * Currently supported profiles: * - GeoServer - parses GeoServer vendor specific capabilities for SLD. */ profile: null, /** * APIProperty: defaultVersion * {String} Version number to assume if none found. Default is "1.0.0". */ defaultVersion: "1.0.0", /** * APIProperty: stringifyOutput * {Boolean} If true, write will return a string otherwise a DOMElement. * Default is true. */ stringifyOutput: true, /** * APIProperty: namedLayersAsArray * {Boolean} Generate a namedLayers array. If false, the namedLayers * property value will be an object keyed by layer name. Default is * false. */ namedLayersAsArray: false, /** * APIMethod: write * Write a SLD document given a list of styles. * * Parameters: * sld - {Object} An object representing the SLD. * options - {Object} Optional configuration object. * * Returns: * {String} An SLD document string. */ /** * APIMethod: read * Read and SLD doc and return an object representing the SLD. * * Parameters: * data - {String | DOMElement} Data to read. * options - {Object} Options for the reader. * * Returns: * {Object} An object representing the SLD. */ CLASS_NAME: "OpenLayers.Format.SLD" }); /* ====================================================================== OpenLayers/Format/EncodedPolyline.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 * @requires OpenLayers/Feature/Vector.js */ /** * Class: OpenLayers.Format.EncodedPolyline * Class for reading and writing encoded polylines. Create a new instance * with the <OpenLayers.Format.EncodedPolyline> constructor. * * Inherits from: * - <OpenLayers.Format> */ OpenLayers.Format.EncodedPolyline = OpenLayers.Class(OpenLayers.Format, { /** * APIProperty: geometryType * {String} Geometry type to output. One of: linestring (default), * linearring, point, multipoint or polygon. If the geometryType is * point, only the first point of the string is returned. */ geometryType: "linestring", /** * Constructor: OpenLayers.Format.EncodedPolyline * Create a new parser for encoded polylines * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance * * Returns: * {<OpenLayers.Format.EncodedPolyline>} A new encoded polylines parser. */ initialize: function(options) { OpenLayers.Format.prototype.initialize.apply(this, [options]); }, /** * APIMethod: read * Deserialize an encoded polyline string and return a vector feature. * * Parameters: * encoded - {String} An encoded polyline string * * Returns: * {<OpenLayers.Feature.Vector>} A vector feature with a linestring. */ read: function(encoded) { var geomType; if (this.geometryType == "linestring") geomType = OpenLayers.Geometry.LineString; else if (this.geometryType == "linearring") geomType = OpenLayers.Geometry.LinearRing; else if (this.geometryType == "multipoint") geomType = OpenLayers.Geometry.MultiPoint; else if (this.geometryType != "point" && this.geometryType != "polygon") return null; var flatPoints = this.decodeDeltas(encoded, 2); var flatPointsLength = flatPoints.length; var pointGeometries = []; for (var i = 0; i + 1 < flatPointsLength;) { var y = flatPoints[i++], x = flatPoints[i++]; pointGeometries.push(new OpenLayers.Geometry.Point(x, y)); } if (this.geometryType == "point") return new OpenLayers.Feature.Vector( pointGeometries[0] ); if (this.geometryType == "polygon") return new OpenLayers.Feature.Vector( new OpenLayers.Geometry.Polygon([ new OpenLayers.Geometry.LinearRing(pointGeometries) ]) ); return new OpenLayers.Feature.Vector( new geomType(pointGeometries) ); }, /** * APIMethod: decode * Deserialize an encoded string and return an array of n-dimensional * points. * * Parameters: * encoded - {String} An encoded string * dims - {int} The dimension of the points that are returned * * Returns: * {Array(Array(int))} An array containing n-dimensional arrays of * coordinates. */ decode: function(encoded, dims, opt_factor) { var factor = opt_factor || 1e5; var flatPoints = this.decodeDeltas(encoded, dims, factor); var flatPointsLength = flatPoints.length; var points = []; for (var i = 0; i + (dims - 1) < flatPointsLength;) { var point = []; for (var dim = 0; dim < dims; ++dim) { point.push(flatPoints[i++]) } points.push(point); } return points; }, /** * APIMethod: write * Serialize a feature or array of features into a WKT string. * * Parameters: * features - {<OpenLayers.Feature.Vector>|Array} A feature or array of * features * * Returns: * {String} The WKT string representation of the input geometries */ write: function(features) { var feature; if (features.constructor == Array) feature = features[0]; else feature = features; var geometry = feature.geometry; var type = geometry.CLASS_NAME.split('.')[2].toLowerCase(); var pointGeometries; if (type == "point") pointGeometries = new Array(geometry); else if (type == "linestring" || type == "linearring" || type == "multipoint") pointGeometries = geometry.components; else if (type == "polygon") pointGeometries = geometry.components[0].components; else return null; var flatPoints = []; var pointGeometriesLength = pointGeometries.length; for (var i = 0; i < pointGeometriesLength; ++i) { var pointGeometry = pointGeometries[i]; flatPoints.push(pointGeometry.y); flatPoints.push(pointGeometry.x); } return this.encodeDeltas(flatPoints, 2); }, /** * APIMethod: encode * Serialize an array of n-dimensional points and return an encoded string * * Parameters: * points - {Array(Array(int))} An array containing n-dimensional * arrays of coordinates * dims - {int} The dimension of the points that should be read * * Returns: * {String} An encoded string */ encode: function (points, dims, opt_factor) { var factor = opt_factor || 1e5; var flatPoints = []; var pointsLength = points.length; for (var i = 0; i < pointsLength; ++i) { var point = points[i]; for (var dim = 0; dim < dims; ++dim) { flatPoints.push(point[dim]); } } return this.encodeDeltas(flatPoints, dims, factor); }, /** * APIMethod: encodeDeltas * Encode a list of n-dimensional points and return an encoded string * * Attention: This function will modify the passed array! * * Parameters: * numbers - {Array.<number>} A list of n-dimensional points. * dimension - {number} The dimension of the points in the list. * opt_factor - {number=} The factor by which the numbers will be * multiplied. The remaining decimal places will get rounded away. * * Returns: * {string} The encoded string. */ encodeDeltas: function(numbers, dimension, opt_factor) { var factor = opt_factor || 1e5; var d; var lastNumbers = new Array(dimension); for (d = 0; d < dimension; ++d) { lastNumbers[d] = 0; } var numbersLength = numbers.length; for (var i = 0; i < numbersLength;) { for (d = 0; d < dimension; ++d, ++i) { var num = numbers[i]; var delta = num - lastNumbers[d]; lastNumbers[d] = num; numbers[i] = delta; } } return this.encodeFloats(numbers, factor); }, /** * APIMethod: decodeDeltas * Decode a list of n-dimensional points from an encoded string * * Parameters: * encoded - {string} An encoded string. * dimension - {number} The dimension of the points in the encoded string. * opt_factor - {number=} The factor by which the resulting numbers will * be divided. * * Returns: * {Array.<number>} A list of n-dimensional points. */ decodeDeltas: function(encoded, dimension, opt_factor) { var factor = opt_factor || 1e5; var d; var lastNumbers = new Array(dimension); for (d = 0; d < dimension; ++d) { lastNumbers[d] = 0; } var numbers = this.decodeFloats(encoded, factor); var numbersLength = numbers.length; for (var i = 0; i < numbersLength;) { for (d = 0; d < dimension; ++d, ++i) { lastNumbers[d] += numbers[i]; numbers[i] = lastNumbers[d]; } } return numbers; }, /** * APIMethod: encodeFloats * Encode a list of floating point numbers and return an encoded string * * Attention: This function will modify the passed array! * * Parameters: * numbers - {Array.<number>} A list of floating point numbers. * opt_factor - {number=} The factor by which the numbers will be * multiplied. The remaining decimal places will get rounded away. * * Returns: * {string} The encoded string. */ encodeFloats: function(numbers, opt_factor) { var factor = opt_factor || 1e5; var numbersLength = numbers.length; for (var i = 0; i < numbersLength; ++i) { numbers[i] = Math.round(numbers[i] * factor); } return this.encodeSignedIntegers(numbers); }, /** * APIMethod: decodeFloats * Decode a list of floating point numbers from an encoded string * * Parameters: * encoded - {string} An encoded string. * opt_factor - {number=} The factor by which the result will be divided. * * Returns: * {Array.<number>} A list of floating point numbers. */ decodeFloats: function(encoded, opt_factor) { var factor = opt_factor || 1e5; var numbers = this.decodeSignedIntegers(encoded); var numbersLength = numbers.length; for (var i = 0; i < numbersLength; ++i) { numbers[i] /= factor; } return numbers; }, /** * APIMethod: encodeSignedIntegers * Encode a list of signed integers and return an encoded string * * Attention: This function will modify the passed array! * * Parameters: * numbers - {Array.<number>} A list of signed integers. * * Returns: * {string} The encoded string. */ encodeSignedIntegers: function(numbers) { var numbersLength = numbers.length; for (var i = 0; i < numbersLength; ++i) { var num = numbers[i]; var signedNum = num << 1; if (num < 0) { signedNum = ~(signedNum); } numbers[i] = signedNum; } return this.encodeUnsignedIntegers(numbers); }, /** * APIMethod: decodeSignedIntegers * Decode a list of signed integers from an encoded string * * Parameters: * encoded - {string} An encoded string. * * Returns: * {Array.<number>} A list of signed integers. */ decodeSignedIntegers: function(encoded) { var numbers = this.decodeUnsignedIntegers(encoded); var numbersLength = numbers.length; for (var i = 0; i < numbersLength; ++i) { var num = numbers[i]; numbers[i] = (num & 1) ? ~(num >> 1) : (num >> 1); } return numbers; }, /** * APIMethod: encodeUnsignedIntegers * Encode a list of unsigned integers and return an encoded string * * Parameters: * numbers - {Array.<number>} A list of unsigned integers. * * Returns: * {string} The encoded string. */ encodeUnsignedIntegers: function(numbers) { var encoded = ''; var numbersLength = numbers.length; for (var i = 0; i < numbersLength; ++i) { encoded += this.encodeUnsignedInteger(numbers[i]); } return encoded; }, /** * APIMethod: decodeUnsignedIntegers * Decode a list of unsigned integers from an encoded string * * Parameters: * encoded - {string} An encoded string. * * Returns: * {Array.<number>} A list of unsigned integers. */ decodeUnsignedIntegers: function(encoded) { var numbers = []; var current = 0; var shift = 0; var encodedLength = encoded.length; for (var i = 0; i < encodedLength; ++i) { var b = encoded.charCodeAt(i) - 63; current |= (b & 0x1f) << shift; if (b < 0x20) { numbers.push(current); current = 0; shift = 0; } else { shift += 5; } } return numbers; }, /** * Method: encodeFloat * Encode one single floating point number and return an encoded string * * Parameters: * num - {number} Floating point number that should be encoded. * opt_factor - {number=} The factor by which num will be multiplied. * The remaining decimal places will get rounded away. * * Returns: * {string} The encoded string. */ encodeFloat: function(num, opt_factor) { num = Math.round(num * (opt_factor || 1e5)); return this.encodeSignedInteger(num); }, /** * Method: decodeFloat * Decode one single floating point number from an encoded string * * Parameters: * encoded - {string} An encoded string. * opt_factor - {number=} The factor by which the result will be divided. * * Returns: * {number} The decoded floating point number. */ decodeFloat: function(encoded, opt_factor) { var result = this.decodeSignedInteger(encoded); return result / (opt_factor || 1e5); }, /** * Method: encodeSignedInteger * Encode one single signed integer and return an encoded string * * Parameters: * num - {number} Signed integer that should be encoded. * * Returns: * {string} The encoded string. */ encodeSignedInteger: function(num) { var signedNum = num << 1; if (num < 0) { signedNum = ~(signedNum); } return this.encodeUnsignedInteger(signedNum); }, /** * Method: decodeSignedInteger * Decode one single signed integer from an encoded string * * Parameters: * encoded - {string} An encoded string. * * Returns: * {number} The decoded signed integer. */ decodeSignedInteger: function(encoded) { var result = this.decodeUnsignedInteger(encoded); return ((result & 1) ? ~(result >> 1) : (result >> 1)); }, /** * Method: encodeUnsignedInteger * Encode one single unsigned integer and return an encoded string * * Parameters: * num - {number} Unsigned integer that should be encoded. * * Returns: * {string} The encoded string. */ encodeUnsignedInteger: function(num) { var value, encoded = ''; while (num >= 0x20) { value = (0x20 | (num & 0x1f)) + 63; encoded += (String.fromCharCode(value)); num >>= 5; } value = num + 63; encoded += (String.fromCharCode(value)); return encoded; }, /** * Method: decodeUnsignedInteger * Decode one single unsigned integer from an encoded string * * Parameters: * encoded - {string} An encoded string. * * Returns: * {number} The decoded unsigned integer. */ decodeUnsignedInteger: function(encoded) { var result = 0; var shift = 0; var encodedLength = encoded.length; for (var i = 0; i < encodedLength; ++i) { var b = encoded.charCodeAt(i) - 63; result |= (b & 0x1f) << shift; if (b < 0x20) break; shift += 5; } return result; }, CLASS_NAME: "OpenLayers.Format.EncodedPolyline" }); /* ====================================================================== OpenLayers/Format/XLS.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 */ /** * Class: OpenLayers.Format.XLS * Read/Write XLS (OpenLS). Create a new instance with the <OpenLayers.Format.XLS> * constructor. Currently only implemented for Location Utility Services, more * specifically only for Geocoding. No support for Reverse Geocoding as yet. * * Inherits from: * - <OpenLayers.Format.XML.VersionedOGC> */ OpenLayers.Format.XLS = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, { /** * APIProperty: defaultVersion * {String} Version number to assume if none found. Default is "1.1.0". */ defaultVersion: "1.1.0", /** * APIProperty: stringifyOutput * {Boolean} If true, write will return a string otherwise a DOMElement. * Default is true. */ stringifyOutput: true, /** * Constructor: OpenLayers.Format.XLS * Create a new parser for XLS. * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ /** * APIMethod: write * Write out an XLS request. * * Parameters: * request - {Object} An object representing the LUS request. * options - {Object} Optional configuration object. * * Returns: * {String} An XLS document string. */ /** * APIMethod: read * Read an XLS doc and return an object representing the result. * * Parameters: * data - {String | DOMElement} Data to read. * options - {Object} Options for the reader. * * Returns: * {Object} An object representing the GeocodeResponse. */ CLASS_NAME: "OpenLayers.Format.XLS" }); /* ====================================================================== OpenLayers/Format/OWSContext.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/Context.js */ /** * Class: OpenLayers.Format.OWSContext * Read and write OWS Context documents. OWS Context documents are a * preliminary OGC (Open Geospatial Consortium) standard for storing the * state of a web mapping application. In a way it is the successor to * Web Map Context (WMC), since it is more generic and more types of layers * can be stored. Also, nesting of layers is supported since version 0.3.1. * For more information see: http://www.ogcnetwork.net/context * * Inherits from: * - <OpenLayers.Format.Context> */ OpenLayers.Format.OWSContext = OpenLayers.Class(OpenLayers.Format.Context,{ /** * APIProperty: defaultVersion * {String} Version number to assume if none found. Default is "0.3.1". */ defaultVersion: "0.3.1", /** * Constructor: OpenLayers.Format.OWSContext * Create a new parser for OWS Context documents. * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ /** * 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 = OpenLayers.Format.XML.VersionedOGC.prototype.getVersion.apply( this, arguments); // 0.3.1 is backwards compatible with 0.3.0 if (version === "0.3.0") { version = this.defaultVersion; } return version; }, /** * Method: toContext * Create a context object free from layer given a map or a * context object. * * Parameters: * obj - {<OpenLayers.Map> | Object} The map or context. * * Returns: * {Object} A context object. */ toContext: function(obj) { var context = {}; if(obj.CLASS_NAME == "OpenLayers.Map") { context.bounds = obj.getExtent(); context.maxExtent = obj.maxExtent; context.projection = obj.projection; context.size = obj.getSize(); context.layers = obj.layers; } return context; }, CLASS_NAME: "OpenLayers.Format.OWSContext" }); /* ====================================================================== OpenLayers/Format/SOSGetFeatureOfInterest.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/v3.js */ /** * Class: OpenLayers.Format.SOSGetFeatureOfInterest * Read and write SOS GetFeatureOfInterest. This is used to get to * the location of the features (stations). The stations can have 1 or more * sensors. * * Inherits from: * - <OpenLayers.Format.XML> */ OpenLayers.Format.SOSGetFeatureOfInterest = OpenLayers.Class( OpenLayers.Format.XML, { /** * Constant: VERSION * {String} 1.0.0 */ VERSION: "1.0.0", /** * Property: namespaces * {Object} Mapping of namespace aliases to namespace URIs. */ namespaces: { sos: "http://www.opengis.net/sos/1.0", gml: "http://www.opengis.net/gml", sa: "http://www.opengis.net/sampling/1.0", xsi: "http://www.w3.org/2001/XMLSchema-instance" }, /** * Property: schemaLocation * {String} Schema location */ schemaLocation: "http://www.opengis.net/sos/1.0 http://schemas.opengis.net/sos/1.0.0/sosAll.xsd", /** * Property: defaultPrefix */ defaultPrefix: "sos", /** * Property: regExes * Compiled regular expressions for manipulating strings. */ regExes: { trimSpace: (/^\s*|\s*$/g), removeSpace: (/\s*/g), splitSpace: (/\s+/), trimComma: (/\s*,\s*/g) }, /** * Constructor: OpenLayers.Format.SOSGetFeatureOfInterest * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ /** * APIMethod: read * Parse a GetFeatureOfInterest response and return an array 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]); } if(data && data.nodeType == 9) { data = data.documentElement; } var info = {features: []}; this.readNode(data, info); var features = []; for (var i=0, len=info.features.length; i<len; i++) { var container = info.features[i]; // reproject features if needed if(this.internalProjection && this.externalProjection && container.components[0]) { container.components[0].transform( this.externalProjection, this.internalProjection ); } var feature = new OpenLayers.Feature.Vector( container.components[0], container.attributes); features.push(feature); } return 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: { "sa": { "SamplingPoint": function(node, obj) { // sampling point can also be without a featureMember if // there is only 1 if (!obj.attributes) { var feature = {attributes: {}}; obj.features.push(feature); obj = feature; } obj.attributes.id = this.getAttributeNS(node, this.namespaces.gml, "id"); this.readChildNodes(node, obj); }, "position": function (node, obj) { this.readChildNodes(node, obj); } }, "gml": OpenLayers.Util.applyDefaults({ "FeatureCollection": function(node, obj) { this.readChildNodes(node, obj); }, "featureMember": function(node, obj) { var feature = {attributes: {}}; obj.features.push(feature); this.readChildNodes(node, feature); }, "name": function(node, obj) { obj.attributes.name = this.getChildValue(node); }, "pos": function(node, obj) { // we need to parse the srsName to get to the // externalProjection, that's why we cannot use // GML v3 for this if (!this.externalProjection) { this.externalProjection = new OpenLayers.Projection( node.getAttribute("srsName")); } OpenLayers.Format.GML.v3.prototype.readers.gml.pos.apply( this, [node, obj]); } }, OpenLayers.Format.GML.v3.prototype.readers.gml) }, /** * 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: { "sos": { "GetFeatureOfInterest": function(options) { var node = this.createElementNSPlus("GetFeatureOfInterest", { attributes: { version: this.VERSION, service: 'SOS', "xsi:schemaLocation": this.schemaLocation } }); for (var i=0, len=options.fois.length; i<len; i++) { this.writeNode("FeatureOfInterestId", {foi: options.fois[i]}, node); } return node; }, "FeatureOfInterestId": function(options) { var node = this.createElementNSPlus("FeatureOfInterestId", {value: options.foi}); return node; } } }, CLASS_NAME: "OpenLayers.Format.SOSGetFeatureOfInterest" }); /* ====================================================================== OpenLayers/Format/WPSCapabilities.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 */ /** * Class: OpenLayers.Format.WPSCapabilities * Read WPS Capabilities. * * Inherits from: * - <OpenLayers.Format.XML.VersionedOGC> */ OpenLayers.Format.WPSCapabilities = 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", /** * Constructor: OpenLayers.Format.WPSCapabilities * Create a new parser for WPS Capabilities. * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ /** * APIMethod: read * Read capabilities data from a string, and return information about * the service. * * Parameters: * data - {String} or {DOMElement} data to read/parse. * * Returns: * {Object} Info about the WPS */ CLASS_NAME: "OpenLayers.Format.WPSCapabilities" }); /* ====================================================================== 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/Format/SOSGetObservation.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/SOSGetFeatureOfInterest.js */ /** * Class: OpenLayers.Format.SOSGetObservation * Read and write SOS GetObersation (to get the actual values from a sensor) * version 1.0.0 * * Inherits from: * - <OpenLayers.Format.XML> */ OpenLayers.Format.SOSGetObservation = OpenLayers.Class(OpenLayers.Format.XML, { /** * Property: namespaces * {Object} Mapping of namespace aliases to namespace URIs. */ namespaces: { ows: "http://www.opengis.net/ows", gml: "http://www.opengis.net/gml", sos: "http://www.opengis.net/sos/1.0", ogc: "http://www.opengis.net/ogc", om: "http://www.opengis.net/om/1.0", sa: "http://www.opengis.net/sampling/1.0", xlink: "http://www.w3.org/1999/xlink", xsi: "http://www.w3.org/2001/XMLSchema-instance" }, /** * Property: regExes * Compiled regular expressions for manipulating strings. */ regExes: { trimSpace: (/^\s*|\s*$/g), removeSpace: (/\s*/g), splitSpace: (/\s+/), trimComma: (/\s*,\s*/g) }, /** * Constant: VERSION * {String} 1.0.0 */ VERSION: "1.0.0", /** * Property: schemaLocation * {String} Schema location */ schemaLocation: "http://www.opengis.net/sos/1.0 http://schemas.opengis.net/sos/1.0.0/sosGetObservation.xsd", /** * Property: defaultPrefix */ defaultPrefix: "sos", /** * Constructor: OpenLayers.Format.SOSGetObservation * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ /** * Method: read * * Parameters: * data - {String} or {DOMElement} data to read/parse. * * Returns: * {Object} An object containing the measurements */ 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 info = {measurements: [], observations: []}; this.readNode(data, info); return info; }, /** * Method: write * * Parameters: * options - {Object} Optional object. * * Returns: * {String} An SOS GetObservation request XML string. */ write: function(options) { var node = this.writeNode("sos:GetObservation", options); node.setAttribute("xmlns:om", this.namespaces.om); node.setAttribute("xmlns:ogc", this.namespaces.ogc); this.setAttributeNS( node, this.namespaces.xsi, "xsi:schemaLocation", this.schemaLocation ); return OpenLayers.Format.XML.prototype.write.apply(this, [node]); }, /** * 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: { "om": { "ObservationCollection": function(node, obj) { obj.id = this.getAttributeNS(node, this.namespaces.gml, "id"); this.readChildNodes(node, obj); }, "member": function(node, observationCollection) { this.readChildNodes(node, observationCollection); }, "Measurement": function(node, observationCollection) { var measurement = {}; observationCollection.measurements.push(measurement); this.readChildNodes(node, measurement); }, "Observation": function(node, observationCollection) { var observation = {}; observationCollection.observations.push(observation); this.readChildNodes(node, observation); }, "samplingTime": function(node, measurement) { var samplingTime = {}; measurement.samplingTime = samplingTime; this.readChildNodes(node, samplingTime); }, "observedProperty": function(node, measurement) { measurement.observedProperty = this.getAttributeNS(node, this.namespaces.xlink, "href"); this.readChildNodes(node, measurement); }, "procedure": function(node, measurement) { measurement.procedure = this.getAttributeNS(node, this.namespaces.xlink, "href"); this.readChildNodes(node, measurement); }, "featureOfInterest": function(node, observation) { var foi = {features: []}; observation.fois = []; observation.fois.push(foi); this.readChildNodes(node, foi); // postprocessing to get actual features var features = []; for (var i=0, len=foi.features.length; i<len; i++) { var feature = foi.features[i]; features.push(new OpenLayers.Feature.Vector( feature.components[0], feature.attributes)); } foi.features = features; }, "result": function(node, measurement) { var result = {}; measurement.result = result; if (this.getChildValue(node) !== '') { result.value = this.getChildValue(node); result.uom = node.getAttribute("uom"); } else { this.readChildNodes(node, result); } } }, "sa": OpenLayers.Format.SOSGetFeatureOfInterest.prototype.readers.sa, "gml": OpenLayers.Util.applyDefaults({ "TimeInstant": function(node, samplingTime) { var timeInstant = {}; samplingTime.timeInstant = timeInstant; this.readChildNodes(node, timeInstant); }, "timePosition": function(node, timeInstant) { timeInstant.timePosition = this.getChildValue(node); } }, OpenLayers.Format.SOSGetFeatureOfInterest.prototype.readers.gml) }, /** * 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: { "sos": { "GetObservation": function(options) { var node = this.createElementNSPlus("GetObservation", { attributes: { version: this.VERSION, service: 'SOS' } }); this.writeNode("offering", options, node); if (options.eventTime) { this.writeNode("eventTime", options, node); } for (var procedure in options.procedures) { this.writeNode("procedure", options.procedures[procedure], node); } for (var observedProperty in options.observedProperties) { this.writeNode("observedProperty", options.observedProperties[observedProperty], node); } if (options.foi) { this.writeNode("featureOfInterest", options.foi, node); } this.writeNode("responseFormat", options, node); if (options.resultModel) { this.writeNode("resultModel", options, node); } if (options.responseMode) { this.writeNode("responseMode", options, node); } return node; }, "featureOfInterest": function(foi) { var node = this.createElementNSPlus("featureOfInterest"); this.writeNode("ObjectID", foi.objectId, node); return node; }, "ObjectID": function(options) { return this.createElementNSPlus("ObjectID", {value: options}); }, "responseFormat": function(options) { return this.createElementNSPlus("responseFormat", {value: options.responseFormat}); }, "procedure": function(procedure) { return this.createElementNSPlus("procedure", {value: procedure}); }, "offering": function(options) { return this.createElementNSPlus("offering", {value: options.offering}); }, "observedProperty": function(observedProperty) { return this.createElementNSPlus("observedProperty", {value: observedProperty}); }, "eventTime": function(options) { var node = this.createElementNSPlus("eventTime"); if (options.eventTime === 'latest') { this.writeNode("ogc:TM_Equals", options, node); } return node; }, "resultModel": function(options) { return this.createElementNSPlus("resultModel", {value: options.resultModel}); }, "responseMode": function(options) { return this.createElementNSPlus("responseMode", {value: options.responseMode}); } }, "ogc": { "TM_Equals": function(options) { var node = this.createElementNSPlus("ogc:TM_Equals"); this.writeNode("ogc:PropertyName", {property: "urn:ogc:data:time:iso8601"}, node); if (options.eventTime === 'latest') { this.writeNode("gml:TimeInstant", {value: 'latest'}, node); } return node; }, "PropertyName": function(options) { return this.createElementNSPlus("ogc:PropertyName", {value: options.property}); } }, "gml": { "TimeInstant": function(options) { var node = this.createElementNSPlus("gml:TimeInstant"); this.writeNode("gml:timePosition", options, node); return node; }, "timePosition": function(options) { var node = this.createElementNSPlus("gml:timePosition", {value: options.value}); return node; } } }, CLASS_NAME: "OpenLayers.Format.SOSGetObservation" }); /* ====================================================================== OpenLayers/Format/WFSDescribeFeatureType.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.WFSDescribeFeatureType * Read WFS DescribeFeatureType response * * Inherits from: * - <OpenLayers.Format.XML> */ OpenLayers.Format.WFSDescribeFeatureType = OpenLayers.Class( OpenLayers.Format.XML, { /** * Property: regExes * Compiled regular expressions for manipulating strings. */ regExes: { trimSpace: (/^\s*|\s*$/g) }, /** * Property: namespaces * {Object} Mapping of namespace aliases to namespace URIs. */ namespaces: { xsd: "http://www.w3.org/2001/XMLSchema" }, /** * Constructor: OpenLayers.Format.WFSDescribeFeatureType * Create a new parser for WFS DescribeFeatureType responses. * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ /** * 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: { "xsd": { "schema": function(node, obj) { var complexTypes = []; var customTypes = {}; var schema = { complexTypes: complexTypes, customTypes: customTypes }; var i, len; this.readChildNodes(node, schema); var attributes = node.attributes; var attr, name; for(i=0, len=attributes.length; i<len; ++i) { attr = attributes[i]; name = attr.name; if(name.indexOf("xmlns") === 0) { this.setNamespace(name.split(":")[1] || "", attr.value); } else { obj[name] = attr.value; } } obj.featureTypes = complexTypes; obj.targetPrefix = this.namespaceAlias[obj.targetNamespace]; // map complexTypes to names of customTypes var complexType, customType; for(i=0, len=complexTypes.length; i<len; ++i) { complexType = complexTypes[i]; customType = customTypes[complexType.typeName]; if(customTypes[complexType.typeName]) { complexType.typeName = customType.name; } } }, "complexType": function(node, obj) { var complexType = { // this is a temporary typeName, it will be overwritten by // the schema reader with the metadata found in the // customTypes hash "typeName": node.getAttribute("name") }; this.readChildNodes(node, complexType); obj.complexTypes.push(complexType); }, "complexContent": function(node, obj) { this.readChildNodes(node, obj); }, "extension": function(node, obj) { this.readChildNodes(node, obj); }, "sequence": function(node, obj) { var sequence = { elements: [] }; this.readChildNodes(node, sequence); obj.properties = sequence.elements; }, "element": function(node, obj) { var type; if(obj.elements) { var element = {}; var attributes = node.attributes; var attr; for(var i=0, len=attributes.length; i<len; ++i) { attr = attributes[i]; element[attr.name] = attr.value; } type = element.type; if(!type) { type = {}; this.readChildNodes(node, type); element.restriction = type; element.type = type.base; } var fullType = type.base || type; element.localType = fullType.split(":").pop(); obj.elements.push(element); this.readChildNodes(node, element); } if(obj.complexTypes) { type = node.getAttribute("type"); var localType = type.split(":").pop(); obj.customTypes[localType] = { "name": node.getAttribute("name"), "type": type }; } }, "annotation": function(node, obj) { obj.annotation = {}; this.readChildNodes(node, obj.annotation); }, "appinfo": function(node, obj) { if (!obj.appinfo) { obj.appinfo = []; } obj.appinfo.push(this.getChildValue(node)); }, "documentation": function(node, obj) { if (!obj.documentation) { obj.documentation = []; } var value = this.getChildValue(node); obj.documentation.push({ lang: node.getAttribute("xml:lang"), textContent: value.replace(this.regExes.trimSpace, "") }); }, "simpleType": function(node, obj) { this.readChildNodes(node, obj); }, "restriction": function(node, obj) { obj.base = node.getAttribute("base"); this.readRestriction(node, obj); } } }, /** * Method: readRestriction * Reads restriction defined in the child nodes of a restriction element * * Parameters: * node - {DOMElement} the node to parse * obj - {Object} the object that receives the read result */ readRestriction: function(node, obj) { var children = node.childNodes; var child, nodeName, value; for(var i=0, len=children.length; i<len; ++i) { child = children[i]; if(child.nodeType == 1) { nodeName = child.nodeName.split(":").pop(); value = child.getAttribute("value"); if(!obj[nodeName]) { obj[nodeName] = value; } else { if(typeof obj[nodeName] == "string") { obj[nodeName] = [obj[nodeName]]; } obj[nodeName].push(value); } } } }, /** * Method: read * * Parameters: * data - {DOMElement|String} A WFS DescribeFeatureType document. * * Returns: * {Object} An object representing the WFS DescribeFeatureType response. */ 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 schema = {}; if (data.nodeName.split(":").pop() === 'ExceptionReport') { // an exception must have occurred, so parse it var parser = new OpenLayers.Format.OGCExceptionReport(); schema.error = parser.read(data); } else { this.readNode(data, schema); } return schema; }, CLASS_NAME: "OpenLayers.Format.WFSDescribeFeatureType" }); /* ====================================================================== OpenLayers/Format/QueryStringFilter.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/Console.js * @requires OpenLayers/Format.js * @requires OpenLayers/Filter/Spatial.js * @requires OpenLayers/Filter/Comparison.js * @requires OpenLayers/Filter/Logical.js */ /** * Class: OpenLayers.Format.QueryStringFilter * Parser for reading a query string and creating a simple filter. * * Inherits from: * - <OpenLayers.Format> */ OpenLayers.Format.QueryStringFilter = (function() { /** * Map the OpenLayers.Filter.Comparison types to the operation strings of * the protocol. */ var cmpToStr = {}; cmpToStr[OpenLayers.Filter.Comparison.EQUAL_TO] = "eq"; cmpToStr[OpenLayers.Filter.Comparison.NOT_EQUAL_TO] = "ne"; cmpToStr[OpenLayers.Filter.Comparison.LESS_THAN] = "lt"; cmpToStr[OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO] = "lte"; cmpToStr[OpenLayers.Filter.Comparison.GREATER_THAN] = "gt"; cmpToStr[OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO] = "gte"; cmpToStr[OpenLayers.Filter.Comparison.LIKE] = "ilike"; /** * Function: regex2value * Convert the value from a regular expression string to a LIKE/ILIKE * string known to the web service. * * Parameters: * value - {String} The regex string. * * Returns: * {String} The converted string. */ function regex2value(value) { // highly sensitive!! Do not change this without running the // Protocol/HTTP.html unit tests // convert % to \% value = value.replace(/%/g, "\\%"); // convert \\. to \\_ (\\.* occurences converted later) value = value.replace(/\\\\\.(\*)?/g, function($0, $1) { return $1 ? $0 : "\\\\_"; }); // convert \\.* to \\% value = value.replace(/\\\\\.\*/g, "\\\\%"); // convert . to _ (\. and .* occurences converted later) value = value.replace(/(\\)?\.(\*)?/g, function($0, $1, $2) { return $1 || $2 ? $0 : "_"; }); // convert .* to % (\.* occurnces converted later) value = value.replace(/(\\)?\.\*/g, function($0, $1) { return $1 ? $0 : "%"; }); // convert \. to . value = value.replace(/\\\./g, "."); // replace \* with * (watching out for \\*) value = value.replace(/(\\)?\\\*/g, function($0, $1) { return $1 ? $0 : "*"; }); return value; } return OpenLayers.Class(OpenLayers.Format, { /** * 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, /** * APIMethod: write * Serialize an <OpenLayers.Filter> objects using the "simple" filter syntax for * query string parameters. This function must be called as a method of * a protocol instance. * * Parameters: * filter - {<OpenLayers.Filter>} filter to convert. * params - {Object} The parameters object. * * Returns: * {Object} The resulting parameters object. */ write: function(filter, params) { params = params || {}; var className = filter.CLASS_NAME; var filterType = className.substring(className.lastIndexOf(".") + 1); switch (filterType) { case "Spatial": switch (filter.type) { case OpenLayers.Filter.Spatial.BBOX: params.bbox = filter.value.toArray(); if (this.srsInBBOX && filter.projection) { params.bbox.push(filter.projection.getCode()); } break; case OpenLayers.Filter.Spatial.DWITHIN: params.tolerance = filter.distance; // no break here case OpenLayers.Filter.Spatial.WITHIN: params.lon = filter.value.x; params.lat = filter.value.y; break; default: OpenLayers.Console.warn( "Unknown spatial filter type " + filter.type); } break; case "Comparison": var op = cmpToStr[filter.type]; if (op !== undefined) { var value = filter.value; if (filter.type == OpenLayers.Filter.Comparison.LIKE) { value = regex2value(value); if (this.wildcarded) { value = "%" + value + "%"; } } params[filter.property + "__" + op] = value; params.queryable = params.queryable || []; params.queryable.push(filter.property); } else { OpenLayers.Console.warn( "Unknown comparison filter type " + filter.type); } break; case "Logical": if (filter.type === OpenLayers.Filter.Logical.AND) { for (var i=0,len=filter.filters.length; i<len; i++) { params = this.write(filter.filters[i], params); } } else { OpenLayers.Console.warn( "Unsupported logical filter type " + filter.type); } break; default: OpenLayers.Console.warn("Unknown filter type " + filterType); } return params; }, CLASS_NAME: "OpenLayers.Format.QueryStringFilter" }); })(); /* ====================================================================== OpenLayers/Format/WMSDescribeLayer/v1_1.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/WMSDescribeLayer.js * @requires OpenLayers/Format/OGCExceptionReport.js */ /** * Class: OpenLayers.Format.WMSDescribeLayer.v1_1_1 * Read SLD WMS DescribeLayer response for WMS 1.1.X * WMS 1.1.X is tightly coupled to SLD 1.0.0 * * Example DescribeLayer request: * http://demo.opengeo.org/geoserver/wms?request=DescribeLayer&version=1.1.1&layers=topp:states * * Inherits from: * - <OpenLayers.Format.WMSDescribeLayer> */ OpenLayers.Format.WMSDescribeLayer.v1_1_1 = OpenLayers.Class( OpenLayers.Format.WMSDescribeLayer, { /** * Constructor: OpenLayers.Format.WMSDescribeLayer * Create a new parser for WMS DescribeLayer responses. * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ initialize: function(options) { OpenLayers.Format.WMSDescribeLayer.prototype.initialize.apply(this, [options]); }, /** * APIMethod: read * Read DescribeLayer data from a string, and return the response. * The OGC defines 2 formats which are allowed for output, * so we need to parse these 2 types for version 1.1.X * * Parameters: * data - {String} or {DOMElement} data to read/parse. * * Returns: * {Object} Object with a layerDescriptions property, which holds an Array * of {<LayerDescription>} objects which have: * - {String} owsType: WFS/WCS * - {String} owsURL: the online resource * - {String} typeName: the name of the typename on the owsType service * - {String} layerName: the name of the WMS layer we did a lookup for */ read: function(data) { if(typeof data == "string") { data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); } var root = data.documentElement; var children = root.childNodes; var describelayer = {layerDescriptions: []}; var childNode, nodeName; for(var i=0; i<children.length; ++i) { childNode = children[i]; nodeName = childNode.nodeName; if (nodeName == 'LayerDescription') { var layerName = childNode.getAttribute('name'); var owsType = ''; var owsURL = ''; var typeName = ''; // check for owsType and owsURL attributes if (childNode.getAttribute('owsType')) { owsType = childNode.getAttribute('owsType'); owsURL = childNode.getAttribute('owsURL'); } else { // look for wfs or wcs attribute if (childNode.getAttribute('wfs') != '') { owsType = 'WFS'; owsURL = childNode.getAttribute('wfs'); } else if (childNode.getAttribute('wcs') != '') { owsType = 'WCS'; owsURL = childNode.getAttribute('wcs'); } } // look for Query child var query = childNode.getElementsByTagName('Query'); if(query.length > 0) { typeName = query[0].getAttribute('typeName'); if (!typeName) { // because of Ionic bug typeName = query[0].getAttribute('typename'); } } var layerDescription = { layerName: layerName, owsType: owsType, owsURL: owsURL, typeName: typeName }; describelayer.layerDescriptions.push(layerDescription); //TODO do this in deprecated.js instead: // array style index for backwards compatibility describelayer.length = describelayer.layerDescriptions.length; describelayer[describelayer.length - 1] = layerDescription; } else if (nodeName == 'ServiceException') { // an exception must have occurred, so parse it var parser = new OpenLayers.Format.OGCExceptionReport(); return { error: parser.read(data) }; } } return describelayer; }, CLASS_NAME: "OpenLayers.Format.WMSDescribeLayer.v1_1_1" }); // Version alias - workaround for http://trac.osgeo.org/mapserver/ticket/2257 OpenLayers.Format.WMSDescribeLayer.v1_1_0 = OpenLayers.Format.WMSDescribeLayer.v1_1_1; /* ====================================================================== OpenLayers/Format/SOSCapabilities/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/SOSCapabilities.js * @requires OpenLayers/Format/OWSCommon/v1_1_0.js * @requires OpenLayers/Format/GML/v3.js */ /** * Class: OpenLayers.Format.SOSCapabilities.v1_0_0 * Read SOS Capabilities version 1.0.0. * * Inherits from: * - <OpenLayers.Format.SOSCapabilities> */ OpenLayers.Format.SOSCapabilities.v1_0_0 = OpenLayers.Class( OpenLayers.Format.SOSCapabilities, { /** * Property: namespaces * {Object} Mapping of namespace aliases to namespace URIs. */ namespaces: { ows: "http://www.opengis.net/ows/1.1", sos: "http://www.opengis.net/sos/1.0", gml: "http://www.opengis.net/gml", xlink: "http://www.w3.org/1999/xlink" }, /** * Property: regExes * Compiled regular expressions for manipulating strings. */ regExes: { trimSpace: (/^\s*|\s*$/g), removeSpace: (/\s*/g), splitSpace: (/\s+/), trimComma: (/\s*,\s*/g) }, /** * Constructor: OpenLayers.Format.SOSCapabilities.v1_0_0 * Create a new parser for SOS capabilities version 1.0.0. * * 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]); this.options = options; }, /** * APIMethod: read * Read capabilities data from a string, and return info about the SOS. * * Parameters: * data - {String} or {DOMElement} data to read/parse. * * Returns: * {Object} Information about the SOS service. */ 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 capabilities = {}; this.readNode(data, capabilities); return capabilities; }, /** * 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({ "name": function(node, obj) { obj.name = this.getChildValue(node); }, "TimePeriod": function(node, obj) { obj.timePeriod = {}; this.readChildNodes(node, obj.timePeriod); }, "beginPosition": function(node, timePeriod) { timePeriod.beginPosition = this.getChildValue(node); }, "endPosition": function(node, timePeriod) { timePeriod.endPosition = this.getChildValue(node); } }, OpenLayers.Format.GML.v3.prototype.readers["gml"]), "sos": { "Capabilities": function(node, obj) { this.readChildNodes(node, obj); }, "Contents": function(node, obj) { obj.contents = {}; this.readChildNodes(node, obj.contents); }, "ObservationOfferingList": function(node, contents) { contents.offeringList = {}; this.readChildNodes(node, contents.offeringList); }, "ObservationOffering": function(node, offeringList) { var id = this.getAttributeNS(node, this.namespaces.gml, "id"); offeringList[id] = { procedures: [], observedProperties: [], featureOfInterestIds: [], responseFormats: [], resultModels: [], responseModes: [] }; this.readChildNodes(node, offeringList[id]); }, "time": function(node, offering) { offering.time = {}; this.readChildNodes(node, offering.time); }, "procedure": function(node, offering) { offering.procedures.push(this.getAttributeNS(node, this.namespaces.xlink, "href")); }, "observedProperty": function(node, offering) { offering.observedProperties.push(this.getAttributeNS(node, this.namespaces.xlink, "href")); }, "featureOfInterest": function(node, offering) { offering.featureOfInterestIds.push(this.getAttributeNS(node, this.namespaces.xlink, "href")); }, "responseFormat": function(node, offering) { offering.responseFormats.push(this.getChildValue(node)); }, "resultModel": function(node, offering) { offering.resultModels.push(this.getChildValue(node)); }, "responseMode": function(node, offering) { offering.responseModes.push(this.getChildValue(node)); } }, "ows": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers["ows"] }, CLASS_NAME: "OpenLayers.Format.SOSCapabilities.v1_0_0" }); /* ====================================================================== OpenLayers/Format/WMC/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/WMC.js * @requires OpenLayers/Format/XML.js */ /** * Class: OpenLayers.Format.WMC.v1 * Superclass for WMC version 1 parsers. * * Inherits from: * - <OpenLayers.Format.XML> */ OpenLayers.Format.WMC.v1 = OpenLayers.Class(OpenLayers.Format.XML, { /** * Property: namespaces * {Object} Mapping of namespace aliases to namespace URIs. */ namespaces: { ol: "http://openlayers.org/context", wmc: "http://www.opengis.net/context", sld: "http://www.opengis.net/sld", xlink: "http://www.w3.org/1999/xlink", xsi: "http://www.w3.org/2001/XMLSchema-instance" }, /** * Property: schemaLocation * {String} Schema location for a particular minor version. */ schemaLocation: "", /** * Method: getNamespacePrefix * Get the namespace prefix for a given uri from the <namespaces> object. * * Returns: * {String} A namespace prefix or null if none found. */ getNamespacePrefix: function(uri) { var prefix = null; if(uri == null) { prefix = this.namespaces[this.defaultPrefix]; } else { for(prefix in this.namespaces) { if(this.namespaces[prefix] == uri) { break; } } } return prefix; }, /** * Property: defaultPrefix */ defaultPrefix: "wmc", /** * Property: rootPrefix * {String} Prefix on the root node that maps to the context namespace URI. */ rootPrefix: null, /** * Property: defaultStyleName * {String} Style name used if layer has no style param. Default is "". */ defaultStyleName: "", /** * Property: defaultStyleTitle * {String} Default style title. Default is "Default". */ defaultStyleTitle: "Default", /** * Constructor: OpenLayers.Format.WMC.v1 * Instances of this class are not created directly. Use the * <OpenLayers.Format.WMC> constructor instead. * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ initialize: function(options) { OpenLayers.Format.XML.prototype.initialize.apply(this, [options]); }, /** * Method: read * Read capabilities data from a string, and return a list of layers. * * Parameters: * data - {String} or {DOMElement} data to read/parse. * * Returns: * {Array} List of named layers. */ read: function(data) { if(typeof data == "string") { data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); } var root = data.documentElement; this.rootPrefix = root.prefix; var context = { version: root.getAttribute("version") }; this.runChildNodes(context, root); return context; }, /** * Method: runChildNodes */ runChildNodes: function(obj, node) { var children = node.childNodes; var childNode, processor, prefix, local; for(var i=0, len=children.length; i<len; ++i) { childNode = children[i]; if(childNode.nodeType == 1) { prefix = this.getNamespacePrefix(childNode.namespaceURI); local = childNode.nodeName.split(":").pop(); processor = this["read_" + prefix + "_" + local]; if(processor) { processor.apply(this, [obj, childNode]); } } } }, /** * Method: read_wmc_General */ read_wmc_General: function(context, node) { this.runChildNodes(context, node); }, /** * Method: read_wmc_BoundingBox */ read_wmc_BoundingBox: function(context, node) { context.projection = node.getAttribute("SRS"); context.bounds = new OpenLayers.Bounds( node.getAttribute("minx"), node.getAttribute("miny"), node.getAttribute("maxx"), node.getAttribute("maxy") ); }, /** * Method: read_wmc_LayerList */ read_wmc_LayerList: function(context, node) { // layersContext is an array containing info for each layer context.layersContext = []; this.runChildNodes(context, node); }, /** * Method: read_wmc_Layer */ read_wmc_Layer: function(context, node) { var layerContext = { visibility: (node.getAttribute("hidden") != "1"), queryable: (node.getAttribute("queryable") == "1"), formats: [], styles: [], metadata: {} }; this.runChildNodes(layerContext, node); // set properties common to multiple objects on layer options/params context.layersContext.push(layerContext); }, /** * Method: read_wmc_Extension */ read_wmc_Extension: function(obj, node) { this.runChildNodes(obj, node); }, /** * Method: read_ol_units */ read_ol_units: function(layerContext, node) { layerContext.units = this.getChildValue(node); }, /** * Method: read_ol_maxExtent */ read_ol_maxExtent: function(obj, node) { var bounds = new OpenLayers.Bounds( node.getAttribute("minx"), node.getAttribute("miny"), node.getAttribute("maxx"), node.getAttribute("maxy") ); obj.maxExtent = bounds; }, /** * Method: read_ol_transparent */ read_ol_transparent: function(layerContext, node) { layerContext.transparent = this.getChildValue(node); }, /** * Method: read_ol_numZoomLevels */ read_ol_numZoomLevels: function(layerContext, node) { layerContext.numZoomLevels = parseInt(this.getChildValue(node)); }, /** * Method: read_ol_opacity */ read_ol_opacity: function(layerContext, node) { layerContext.opacity = parseFloat(this.getChildValue(node)); }, /** * Method: read_ol_singleTile */ read_ol_singleTile: function(layerContext, node) { layerContext.singleTile = (this.getChildValue(node) == "true"); }, /** * Method: read_ol_tileSize */ read_ol_tileSize: function(layerContext, node) { var obj = {"width": node.getAttribute("width"), "height": node.getAttribute("height")}; layerContext.tileSize = obj; }, /** * Method: read_ol_isBaseLayer */ read_ol_isBaseLayer: function(layerContext, node) { layerContext.isBaseLayer = (this.getChildValue(node) == "true"); }, /** * Method: read_ol_displayInLayerSwitcher */ read_ol_displayInLayerSwitcher: function(layerContext, node) { layerContext.displayInLayerSwitcher = (this.getChildValue(node) == "true"); }, /** * Method: read_wmc_Server */ read_wmc_Server: function(layerContext, node) { layerContext.version = node.getAttribute("version"); layerContext.url = this.getOnlineResource_href(node); layerContext.metadata.servertitle = node.getAttribute("title"); }, /** * Method: read_wmc_FormatList */ read_wmc_FormatList: function(layerContext, node) { this.runChildNodes(layerContext, node); }, /** * Method: read_wmc_Format */ read_wmc_Format: function(layerContext, node) { var format = { value: this.getChildValue(node) }; if(node.getAttribute("current") == "1") { format.current = true; } layerContext.formats.push(format); }, /** * Method: read_wmc_StyleList */ read_wmc_StyleList: function(layerContext, node) { this.runChildNodes(layerContext, node); }, /** * Method: read_wmc_Style */ read_wmc_Style: function(layerContext, node) { var style = {}; this.runChildNodes(style, node); if(node.getAttribute("current") == "1") { style.current = true; } layerContext.styles.push(style); }, /** * Method: read_wmc_SLD */ read_wmc_SLD: function(style, node) { this.runChildNodes(style, node); // style either comes back with an href or a body property }, /** * Method: read_sld_StyledLayerDescriptor */ read_sld_StyledLayerDescriptor: function(sld, node) { var xml = OpenLayers.Format.XML.prototype.write.apply(this, [node]); sld.body = xml; }, /** * Method: read_sld_FeatureTypeStyle */ read_sld_FeatureTypeStyle: function(sld, node) { var xml = OpenLayers.Format.XML.prototype.write.apply(this, [node]); sld.body = xml; }, /** * Method: read_wmc_OnlineResource */ read_wmc_OnlineResource: function(obj, node) { obj.href = this.getAttributeNS( node, this.namespaces.xlink, "href" ); }, /** * Method: read_wmc_Name */ read_wmc_Name: function(obj, node) { var name = this.getChildValue(node); if(name) { obj.name = name; } }, /** * Method: read_wmc_Title */ read_wmc_Title: function(obj, node) { var title = this.getChildValue(node); if(title) { obj.title = title; } }, /** * Method: read_wmc_MetadataURL */ read_wmc_MetadataURL: function(layerContext, node) { layerContext.metadataURL = this.getOnlineResource_href(node); }, /** * Method: read_wmc_KeywordList */ read_wmc_KeywordList: function(context, node) { context.keywords = []; this.runChildNodes(context.keywords, node); }, /** * Method: read_wmc_Keyword */ read_wmc_Keyword: function(keywords, node) { keywords.push(this.getChildValue(node)); }, /** * Method: read_wmc_Abstract */ read_wmc_Abstract: function(obj, node) { var abst = this.getChildValue(node); if(abst) { obj["abstract"] = abst; } }, /** * Method: read_wmc_LogoURL */ read_wmc_LogoURL: function(context, node) { context.logo = { width: node.getAttribute("width"), height: node.getAttribute("height"), format: node.getAttribute("format"), href: this.getOnlineResource_href(node) }; }, /** * Method: read_wmc_DescriptionURL */ read_wmc_DescriptionURL: function(context, node) { context.descriptionURL = this.getOnlineResource_href(node); }, /** * Method: read_wmc_ContactInformation */ read_wmc_ContactInformation: function(obj, node) { var contact = {}; this.runChildNodes(contact, node); obj.contactInformation = contact; }, /** * Method: read_wmc_ContactPersonPrimary */ read_wmc_ContactPersonPrimary: function(contact, node) { var personPrimary = {}; this.runChildNodes(personPrimary, node); contact.personPrimary = personPrimary; }, /** * Method: read_wmc_ContactPerson */ read_wmc_ContactPerson: function(primaryPerson, node) { var person = this.getChildValue(node); if (person) { primaryPerson.person = person; } }, /** * Method: read_wmc_ContactOrganization */ read_wmc_ContactOrganization: function(primaryPerson, node) { var organization = this.getChildValue(node); if (organization) { primaryPerson.organization = organization; } }, /** * Method: read_wmc_ContactPosition */ read_wmc_ContactPosition: function(contact, node) { var position = this.getChildValue(node); if (position) { contact.position = position; } }, /** * Method: read_wmc_ContactAddress */ read_wmc_ContactAddress: function(contact, node) { var contactAddress = {}; this.runChildNodes(contactAddress, node); contact.contactAddress = contactAddress; }, /** * Method: read_wmc_AddressType */ read_wmc_AddressType: function(contactAddress, node) { var type = this.getChildValue(node); if (type) { contactAddress.type = type; } }, /** * Method: read_wmc_Address */ read_wmc_Address: function(contactAddress, node) { var address = this.getChildValue(node); if (address) { contactAddress.address = address; } }, /** * Method: read_wmc_City */ read_wmc_City: function(contactAddress, node) { var city = this.getChildValue(node); if (city) { contactAddress.city = city; } }, /** * Method: read_wmc_StateOrProvince */ read_wmc_StateOrProvince: function(contactAddress, node) { var stateOrProvince = this.getChildValue(node); if (stateOrProvince) { contactAddress.stateOrProvince = stateOrProvince; } }, /** * Method: read_wmc_PostCode */ read_wmc_PostCode: function(contactAddress, node) { var postcode = this.getChildValue(node); if (postcode) { contactAddress.postcode = postcode; } }, /** * Method: read_wmc_Country */ read_wmc_Country: function(contactAddress, node) { var country = this.getChildValue(node); if (country) { contactAddress.country = country; } }, /** * Method: read_wmc_ContactVoiceTelephone */ read_wmc_ContactVoiceTelephone: function(contact, node) { var phone = this.getChildValue(node); if (phone) { contact.phone = phone; } }, /** * Method: read_wmc_ContactFacsimileTelephone */ read_wmc_ContactFacsimileTelephone: function(contact, node) { var fax = this.getChildValue(node); if (fax) { contact.fax = fax; } }, /** * Method: read_wmc_ContactElectronicMailAddress */ read_wmc_ContactElectronicMailAddress: function(contact, node) { var email = this.getChildValue(node); if (email) { contact.email = email; } }, /** * Method: read_wmc_DataURL */ read_wmc_DataURL: function(layerContext, node) { layerContext.dataURL = this.getOnlineResource_href(node); }, /** * Method: read_wmc_LegendURL */ read_wmc_LegendURL: function(style, node) { var legend = { width: node.getAttribute('width'), height: node.getAttribute('height'), format: node.getAttribute('format'), href: this.getOnlineResource_href(node) }; style.legend = legend; }, /** * Method: read_wmc_DimensionList */ read_wmc_DimensionList: function(layerContext, node) { layerContext.dimensions = {}; this.runChildNodes(layerContext.dimensions, node); }, /** * Method: read_wmc_Dimension */ read_wmc_Dimension: function(dimensions, node) { var name = node.getAttribute("name").toLowerCase(); var dim = { name: name, units: node.getAttribute("units") || "", unitSymbol: node.getAttribute("unitSymbol") || "", userValue: node.getAttribute("userValue") || "", nearestValue: node.getAttribute("nearestValue") === "1", multipleValues: node.getAttribute("multipleValues") === "1", current: node.getAttribute("current") === "1", "default": node.getAttribute("default") || "" }; var values = this.getChildValue(node); dim.values = values.split(","); dimensions[dim.name] = dim; }, /** * Method: write * * Parameters: * context - {Object} An object representing the map context. * options - {Object} Optional object. * * Returns: * {String} A WMC document string. */ write: function(context, options) { var root = this.createElementDefaultNS("ViewContext"); this.setAttributes(root, { version: this.VERSION, id: (options && typeof options.id == "string") ? options.id : OpenLayers.Util.createUniqueID("OpenLayers_Context_") }); // add schemaLocation attribute this.setAttributeNS( root, this.namespaces.xsi, "xsi:schemaLocation", this.schemaLocation ); // required General element root.appendChild(this.write_wmc_General(context)); // required LayerList element root.appendChild(this.write_wmc_LayerList(context)); return OpenLayers.Format.XML.prototype.write.apply(this, [root]); }, /** * Method: createElementDefaultNS * Shorthand for createElementNS with namespace from <defaultPrefix>. * Can optionally be used to set attributes and a text child value. * * Parameters: * name - {String} The qualified node name. * childValue - {String} Optional value for text child node. * attributes - {Object} Optional object representing attributes. * * Returns: * {Element} An element node. */ createElementDefaultNS: function(name, childValue, attributes) { var node = this.createElementNS( this.namespaces[this.defaultPrefix], name ); if(childValue) { node.appendChild(this.createTextNode(childValue)); } if(attributes) { this.setAttributes(node, attributes); } return node; }, /** * Method: setAttributes * Set multiple attributes given key value pairs from an object. * * Parameters: * node - {Element} An element node. * obj - {Object} An object whose properties represent attribute names and * values represent attribute values. */ setAttributes: function(node, obj) { var value; for(var name in obj) { value = obj[name].toString(); if(value.match(/[A-Z]/)) { // safari lowercases attributes with setAttribute this.setAttributeNS(node, null, name, value); } else { node.setAttribute(name, value); } } }, /** * Method: write_wmc_General * Create a General node given an context object. * * Parameters: * context - {Object} Context object. * * Returns: * {Element} A WMC General element node. */ write_wmc_General: function(context) { var node = this.createElementDefaultNS("General"); // optional Window element if(context.size) { node.appendChild(this.createElementDefaultNS( "Window", null, { width: context.size.w, height: context.size.h } )); } // required BoundingBox element var bounds = context.bounds; node.appendChild(this.createElementDefaultNS( "BoundingBox", null, { minx: bounds.left.toPrecision(18), miny: bounds.bottom.toPrecision(18), maxx: bounds.right.toPrecision(18), maxy: bounds.top.toPrecision(18), SRS: context.projection } )); // required Title element node.appendChild(this.createElementDefaultNS( "Title", context.title )); // optional KeywordList element if (context.keywords) { node.appendChild(this.write_wmc_KeywordList(context.keywords)); } // optional Abstract element if (context["abstract"]) { node.appendChild(this.createElementDefaultNS( "Abstract", context["abstract"] )); } // Optional LogoURL element if (context.logo) { node.appendChild(this.write_wmc_URLType("LogoURL", context.logo.href, context.logo)); } // Optional DescriptionURL element if (context.descriptionURL) { node.appendChild(this.write_wmc_URLType("DescriptionURL", context.descriptionURL)); } // Optional ContactInformation element if (context.contactInformation) { node.appendChild(this.write_wmc_ContactInformation(context.contactInformation)); } // OpenLayers specific map properties node.appendChild(this.write_ol_MapExtension(context)); return node; }, /** * Method: write_wmc_KeywordList */ write_wmc_KeywordList: function(keywords) { var node = this.createElementDefaultNS("KeywordList"); for (var i=0, len=keywords.length; i<len; i++) { node.appendChild(this.createElementDefaultNS( "Keyword", keywords[i] )); } return node; }, /** * Method: write_wmc_ContactInformation */ write_wmc_ContactInformation: function(contact) { var node = this.createElementDefaultNS("ContactInformation"); if (contact.personPrimary) { node.appendChild(this.write_wmc_ContactPersonPrimary(contact.personPrimary)); } if (contact.position) { node.appendChild(this.createElementDefaultNS( "ContactPosition", contact.position )); } if (contact.contactAddress) { node.appendChild(this.write_wmc_ContactAddress(contact.contactAddress)); } if (contact.phone) { node.appendChild(this.createElementDefaultNS( "ContactVoiceTelephone", contact.phone )); } if (contact.fax) { node.appendChild(this.createElementDefaultNS( "ContactFacsimileTelephone", contact.fax )); } if (contact.email) { node.appendChild(this.createElementDefaultNS( "ContactElectronicMailAddress", contact.email )); } return node; }, /** * Method: write_wmc_ContactPersonPrimary */ write_wmc_ContactPersonPrimary: function(personPrimary) { var node = this.createElementDefaultNS("ContactPersonPrimary"); if (personPrimary.person) { node.appendChild(this.createElementDefaultNS( "ContactPerson", personPrimary.person )); } if (personPrimary.organization) { node.appendChild(this.createElementDefaultNS( "ContactOrganization", personPrimary.organization )); } return node; }, /** * Method: write_wmc_ContactAddress */ write_wmc_ContactAddress: function(contactAddress) { var node = this.createElementDefaultNS("ContactAddress"); if (contactAddress.type) { node.appendChild(this.createElementDefaultNS( "AddressType", contactAddress.type )); } if (contactAddress.address) { node.appendChild(this.createElementDefaultNS( "Address", contactAddress.address )); } if (contactAddress.city) { node.appendChild(this.createElementDefaultNS( "City", contactAddress.city )); } if (contactAddress.stateOrProvince) { node.appendChild(this.createElementDefaultNS( "StateOrProvince", contactAddress.stateOrProvince )); } if (contactAddress.postcode) { node.appendChild(this.createElementDefaultNS( "PostCode", contactAddress.postcode )); } if (contactAddress.country) { node.appendChild(this.createElementDefaultNS( "Country", contactAddress.country )); } return node; }, /** * Method: write_ol_MapExtension */ write_ol_MapExtension: function(context) { var node = this.createElementDefaultNS("Extension"); var bounds = context.maxExtent; if(bounds) { var maxExtent = this.createElementNS( this.namespaces.ol, "ol:maxExtent" ); this.setAttributes(maxExtent, { minx: bounds.left.toPrecision(18), miny: bounds.bottom.toPrecision(18), maxx: bounds.right.toPrecision(18), maxy: bounds.top.toPrecision(18) }); node.appendChild(maxExtent); } return node; }, /** * Method: write_wmc_LayerList * Create a LayerList node given an context object. * * Parameters: * context - {Object} Context object. * * Returns: * {Element} A WMC LayerList element node. */ write_wmc_LayerList: function(context) { var list = this.createElementDefaultNS("LayerList"); for(var i=0, len=context.layersContext.length; i<len; ++i) { list.appendChild(this.write_wmc_Layer(context.layersContext[i])); } return list; }, /** * Method: write_wmc_Layer * Create a Layer node given a layer context object. * * Parameters: * context - {Object} A layer context object.} * * Returns: * {Element} A WMC Layer element node. */ write_wmc_Layer: function(context) { var node = this.createElementDefaultNS( "Layer", null, { queryable: context.queryable ? "1" : "0", hidden: context.visibility ? "0" : "1" } ); // required Server element node.appendChild(this.write_wmc_Server(context)); // required Name element node.appendChild(this.createElementDefaultNS( "Name", context.name )); // required Title element node.appendChild(this.createElementDefaultNS( "Title", context.title )); // optional Abstract element if (context["abstract"]) { node.appendChild(this.createElementDefaultNS( "Abstract", context["abstract"] )); } // optional DataURL element if (context.dataURL) { node.appendChild(this.write_wmc_URLType("DataURL", context.dataURL)); } // optional MetadataURL element if (context.metadataURL) { node.appendChild(this.write_wmc_URLType("MetadataURL", context.metadataURL)); } return node; }, /** * Method: write_wmc_LayerExtension * Add OpenLayers specific layer parameters to an Extension element. * * Parameters: * context - {Object} A layer context object. * * Returns: * {Element} A WMC Extension element (for a layer). */ write_wmc_LayerExtension: function(context) { var node = this.createElementDefaultNS("Extension"); var bounds = context.maxExtent; var maxExtent = this.createElementNS( this.namespaces.ol, "ol:maxExtent" ); this.setAttributes(maxExtent, { minx: bounds.left.toPrecision(18), miny: bounds.bottom.toPrecision(18), maxx: bounds.right.toPrecision(18), maxy: bounds.top.toPrecision(18) }); node.appendChild(maxExtent); if (context.tileSize && !context.singleTile) { var size = this.createElementNS( this.namespaces.ol, "ol:tileSize" ); this.setAttributes(size, context.tileSize); node.appendChild(size); } var properties = [ "transparent", "numZoomLevels", "units", "isBaseLayer", "opacity", "displayInLayerSwitcher", "singleTile" ]; var child; for(var i=0, len=properties.length; i<len; ++i) { child = this.createOLPropertyNode(context, properties[i]); if(child) { node.appendChild(child); } } return node; }, /** * Method: createOLPropertyNode * Create a node representing an OpenLayers property. If the property is * null or undefined, null will be returned. * * Parameters: * obj - {Object} An object. * prop - {String} A property. * * Returns: * {Element} A property node. */ createOLPropertyNode: function(obj, prop) { var node = null; if(obj[prop] != null) { node = this.createElementNS(this.namespaces.ol, "ol:" + prop); node.appendChild(this.createTextNode(obj[prop].toString())); } return node; }, /** * Method: write_wmc_Server * Create a Server node given a layer context object. * * Parameters: * context - {Object} Layer context object. * * Returns: * {Element} A WMC Server element node. */ write_wmc_Server: function(context) { var server = context.server; var node = this.createElementDefaultNS("Server"); var attributes = { service: "OGC:WMS", version: server.version }; if (server.title) { attributes.title = server.title; } this.setAttributes(node, attributes); // required OnlineResource element node.appendChild(this.write_wmc_OnlineResource(server.url)); return node; }, /** * Method: write_wmc_URLType * Create a LogoURL/DescriptionURL/MetadataURL/DataURL/LegendURL node given a object and elementName. * * Parameters: * elName - {String} Name of element (LogoURL/DescriptionURL/MetadataURL/LegendURL) * url - {String} URL string value * attr - {Object} Optional attributes (width, height, format) * * Returns: * {Element} A WMC element node. */ write_wmc_URLType: function(elName, url, attr) { var node = this.createElementDefaultNS(elName); node.appendChild(this.write_wmc_OnlineResource(url)); if (attr) { var optionalAttributes = ["width", "height", "format"]; for (var i=0; i<optionalAttributes.length; i++) { if (optionalAttributes[i] in attr) { node.setAttribute(optionalAttributes[i], attr[optionalAttributes[i]]); } } } return node; }, /** * Method: write_wmc_DimensionList */ write_wmc_DimensionList: function(context) { var node = this.createElementDefaultNS("DimensionList"); var required_attributes = { name: true, units: true, unitSymbol: true, userValue: true }; for (var dim in context.dimensions) { var attributes = {}; var dimension = context.dimensions[dim]; for (var name in dimension) { if (typeof dimension[name] == "boolean") { attributes[name] = Number(dimension[name]); } else { attributes[name] = dimension[name]; } } var values = ""; if (attributes.values) { values = attributes.values.join(","); delete attributes.values; } node.appendChild(this.createElementDefaultNS( "Dimension", values, attributes )); } return node; }, /** * Method: write_wmc_FormatList * Create a FormatList node given a layer context. * * Parameters: * context - {Object} Layer context object. * * Returns: * {Element} A WMC FormatList element node. */ write_wmc_FormatList: function(context) { var node = this.createElementDefaultNS("FormatList"); for (var i=0, len=context.formats.length; i<len; i++) { var format = context.formats[i]; node.appendChild(this.createElementDefaultNS( "Format", format.value, (format.current && format.current == true) ? {current: "1"} : null )); } return node; }, /** * Method: write_wmc_StyleList * Create a StyleList node given a layer context. * * Parameters: * layer - {Object} Layer context object. * * Returns: * {Element} A WMC StyleList element node. */ write_wmc_StyleList: function(layer) { var node = this.createElementDefaultNS("StyleList"); var styles = layer.styles; if (styles && OpenLayers.Util.isArray(styles)) { var sld; for (var i=0, len=styles.length; i<len; i++) { var s = styles[i]; // three style types to consider // [1] linked SLD // [2] inline SLD // [3] named style // running child nodes always gets name, optionally gets href or body var style = this.createElementDefaultNS( "Style", null, (s.current && s.current == true) ? {current: "1"} : null ); if(s.href) { // [1] sld = this.createElementDefaultNS("SLD"); // Name is optional. if (s.name) { sld.appendChild(this.createElementDefaultNS("Name", s.name)); } // Title is optional. if (s.title) { sld.appendChild(this.createElementDefaultNS("Title", s.title)); } // LegendURL is optional if (s.legend) { sld.appendChild(this.write_wmc_URLType("LegendURL", s.legend.href, s.legend)); } var link = this.write_wmc_OnlineResource(s.href); sld.appendChild(link); style.appendChild(sld); } else if(s.body) { // [2] sld = this.createElementDefaultNS("SLD"); // Name is optional. if (s.name) { sld.appendChild(this.createElementDefaultNS("Name", s.name)); } // Title is optional. if (s.title) { sld.appendChild(this.createElementDefaultNS("Title", s.title)); } // LegendURL is optional if (s.legend) { sld.appendChild(this.write_wmc_URLType("LegendURL", s.legend.href, s.legend)); } // read in body as xml doc - assume proper namespace declarations var doc = OpenLayers.Format.XML.prototype.read.apply(this, [s.body]); // append to StyledLayerDescriptor node var imported = doc.documentElement; if(sld.ownerDocument && sld.ownerDocument.importNode) { imported = sld.ownerDocument.importNode(imported, true); } sld.appendChild(imported); style.appendChild(sld); } else { // [3] // both Name and Title are required. style.appendChild(this.createElementDefaultNS("Name", s.name)); style.appendChild(this.createElementDefaultNS("Title", s.title)); // Abstract is optional if (s['abstract']) { // abstract is a js keyword style.appendChild(this.createElementDefaultNS( "Abstract", s['abstract'] )); } // LegendURL is optional if (s.legend) { style.appendChild(this.write_wmc_URLType("LegendURL", s.legend.href, s.legend)); } } node.appendChild(style); } } return node; }, /** * Method: write_wmc_OnlineResource * Create an OnlineResource node given a URL. * * Parameters: * href - {String} URL for the resource. * * Returns: * {Element} A WMC OnlineResource element node. */ write_wmc_OnlineResource: function(href) { var node = this.createElementDefaultNS("OnlineResource"); this.setAttributeNS(node, this.namespaces.xlink, "xlink:type", "simple"); this.setAttributeNS(node, this.namespaces.xlink, "xlink:href", href); return node; }, /** * Method: getOnlineResource_href */ getOnlineResource_href: function(node) { var object = {}; var links = node.getElementsByTagName("OnlineResource"); if(links.length > 0) { this.read_wmc_OnlineResource(object, links[0]); } return object.href; }, CLASS_NAME: "OpenLayers.Format.WMC.v1" }); /* ====================================================================== OpenLayers/Format/WMC/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/WMC/v1.js */ /** * Class: OpenLayers.Format.WMC.v1_0_0 * Read and write WMC version 1.0.0. * * Inherits from: * - <OpenLayers.Format.WMC.v1> */ OpenLayers.Format.WMC.v1_0_0 = OpenLayers.Class( OpenLayers.Format.WMC.v1, { /** * Constant: VERSION * {String} 1.0.0 */ VERSION: "1.0.0", /** * Property: schemaLocation * {String} http://www.opengis.net/context * http://schemas.opengis.net/context/1.0.0/context.xsd */ schemaLocation: "http://www.opengis.net/context http://schemas.opengis.net/context/1.0.0/context.xsd", /** * Constructor: OpenLayers.Format.WMC.v1_0_0 * Instances of this class are not created directly. Use the * <OpenLayers.Format.WMC> constructor instead. * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ initialize: function(options) { OpenLayers.Format.WMC.v1.prototype.initialize.apply( this, [options] ); }, /** * Method: read_wmc_SRS */ read_wmc_SRS: function(layerContext, node) { var srs = this.getChildValue(node); if (typeof layerContext.projections != "object") { layerContext.projections = {}; } var values = srs.split(/ +/); for (var i=0, len=values.length; i<len; i++) { layerContext.projections[values[i]] = true; } }, /** * Method: write_wmc_Layer * Create a Layer node given a layer context object. This method adds * elements specific to version 1.0.0. * * Parameters: * context - {Object} A layer context object.} * * Returns: * {Element} A WMC Layer element node. */ write_wmc_Layer: function(context) { var node = OpenLayers.Format.WMC.v1.prototype.write_wmc_Layer.apply( this, [context] ); // optional SRS element(s) if (context.srs) { var projections = []; for(var name in context.srs) { projections.push(name); } node.appendChild(this.createElementDefaultNS("SRS", projections.join(" "))); } // optional FormatList element node.appendChild(this.write_wmc_FormatList(context)); // optional StyleList element node.appendChild(this.write_wmc_StyleList(context)); // optional DimensionList element if (context.dimensions) { node.appendChild(this.write_wmc_DimensionList(context)); } // OpenLayers specific properties go in an Extension element node.appendChild(this.write_wmc_LayerExtension(context)); }, CLASS_NAME: "OpenLayers.Format.WMC.v1_0_0" }); /* ====================================================================== OpenLayers/Format/WMC/v1_1_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/WMC/v1.js */ /** * Class: OpenLayers.Format.WMC.v1_1_0 * Read and write WMC version 1.1.0. * * Differences between 1.1.0 and 1.0.0: * - 1.1.0 Layers have optional sld:MinScaleDenominator and * sld:MaxScaleDenominator * * Inherits from: * - <OpenLayers.Format.WMC.v1> */ OpenLayers.Format.WMC.v1_1_0 = OpenLayers.Class( OpenLayers.Format.WMC.v1, { /** * Constant: VERSION * {String} 1.1.0 */ VERSION: "1.1.0", /** * Property: schemaLocation * {String} http://www.opengis.net/context * http://schemas.opengis.net/context/1.1.0/context.xsd */ schemaLocation: "http://www.opengis.net/context http://schemas.opengis.net/context/1.1.0/context.xsd", /** * Constructor: OpenLayers.Format.WMC.v1_1_0 * Instances of this class are not created directly. Use the * <OpenLayers.Format.WMC> constructor instead. * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ initialize: function(options) { OpenLayers.Format.WMC.v1.prototype.initialize.apply( this, [options] ); }, /** * Method: read_sld_MinScaleDenominator * Read a sld:MinScaleDenominator node. * * Parameters: * layerContext - {Object} An object representing a layer. * node - {Element} An element node. */ read_sld_MinScaleDenominator: function(layerContext, node) { var minScaleDenominator = parseFloat(this.getChildValue(node)); if (minScaleDenominator > 0) { layerContext.maxScale = minScaleDenominator; } }, /** * Method: read_sld_MaxScaleDenominator * Read a sld:MaxScaleDenominator node. * * Parameters: * layerContext - {Object} An object representing a layer. * node - {Element} An element node. */ read_sld_MaxScaleDenominator: function(layerContext, node) { layerContext.minScale = parseFloat(this.getChildValue(node)); }, /** * Method: read_wmc_SRS */ read_wmc_SRS: function(layerContext, node) { if (! ("srs" in layerContext)) { layerContext.srs = {}; } layerContext.srs[this.getChildValue(node)] = true; }, /** * Method: write_wmc_Layer * Create a Layer node given a layer context object. This method adds * elements specific to version 1.1.0. * * Parameters: * context - {Object} A layer context object.} * * Returns: * {Element} A WMC Layer element node. */ write_wmc_Layer: function(context) { var node = OpenLayers.Format.WMC.v1.prototype.write_wmc_Layer.apply( this, [context] ); // min/max scale denominator elements go before the 4th element in v1 if(context.maxScale) { var minSD = this.createElementNS( this.namespaces.sld, "sld:MinScaleDenominator" ); minSD.appendChild(this.createTextNode(context.maxScale.toPrecision(16))); node.appendChild(minSD); } if(context.minScale) { var maxSD = this.createElementNS( this.namespaces.sld, "sld:MaxScaleDenominator" ); maxSD.appendChild(this.createTextNode(context.minScale.toPrecision(16))); node.appendChild(maxSD); } // optional SRS element(s) if (context.srs) { for(var name in context.srs) { node.appendChild(this.createElementDefaultNS("SRS", name)); } } // optional FormatList element node.appendChild(this.write_wmc_FormatList(context)); // optional StyleList element node.appendChild(this.write_wmc_StyleList(context)); // optional DimensionList element if (context.dimensions) { node.appendChild(this.write_wmc_DimensionList(context)); } // OpenLayers specific properties go in an Extension element node.appendChild(this.write_wmc_LayerExtension(context)); return node; }, CLASS_NAME: "OpenLayers.Format.WMC.v1_1_0" }); /* ====================================================================== 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/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/SLD/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/Rule.js * @requires OpenLayers/Format/SLD.js * @requires OpenLayers/Format/Filter/v1_0_0.js * @requires OpenLayers/Symbolizer/Point.js * @requires OpenLayers/Symbolizer/Line.js * @requires OpenLayers/Symbolizer/Polygon.js * @requires OpenLayers/Symbolizer/Text.js * @requires OpenLayers/Symbolizer/Raster.js */ /** * Class: OpenLayers.Format.SLD.v1 * Superclass for SLD version 1 parsers. * * Inherits from: * - <OpenLayers.Format.Filter.v1_0_0> */ OpenLayers.Format.SLD.v1 = OpenLayers.Class(OpenLayers.Format.Filter.v1_0_0, { /** * Property: namespaces * {Object} Mapping of namespace aliases to namespace URIs. */ namespaces: { sld: "http://www.opengis.net/sld", 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: "sld", /** * Property: schemaLocation * {String} Schema location for a particular minor version. */ schemaLocation: null, /** * APIProperty: multipleSymbolizers * {Boolean} Support multiple symbolizers per rule. Default is false. if * true, an OpenLayers.Style2 instance will be created to represent * user styles instead of an OpenLayers.Style instace. The * OpenLayers.Style2 class allows collections of rules with multiple * symbolizers, but is not currently useful for client side rendering. * If multiple symbolizers is true, multiple FeatureTypeStyle elements * are preserved in reading/writing by setting symbolizer zIndex values. * In addition, the <defaultSymbolizer> property is ignored if * multiple symbolizers are supported (defaults should be applied * when rendering). */ multipleSymbolizers: false, /** * Property: featureTypeCounter * {Number} Private counter for multiple feature type styles. */ featureTypeCounter: null, /** * APIProperty: defaultSymbolizer. * {Object} A symbolizer with the SLD defaults. */ defaultSymbolizer: { fillColor: "#808080", fillOpacity: 1, strokeColor: "#000000", strokeOpacity: 1, strokeWidth: 1, strokeDashstyle: "solid", pointRadius: 3, graphicName: "square" }, /** * Constructor: OpenLayers.Format.SLD.v1 * Instances of this class are not created directly. Use the * <OpenLayers.Format.SLD> constructor instead. * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ /** * Method: read * * Parameters: * data - {DOMElement} An SLD document element. * options - {Object} Options for the reader. * * Valid options: * namedLayersAsArray - {Boolean} Generate a namedLayers array. If false, * the namedLayers property value will be an object keyed by layer name. * Default is false. * * Returns: * {Object} An object representing the SLD. */ read: function(data, options) { options = OpenLayers.Util.applyDefaults(options, this.options); var sld = { namedLayers: options.namedLayersAsArray === true ? [] : {} }; this.readChildNodes(data, sld); return sld; }, /** * Property: readers * Contains public functions, grouped by namespace prefix, that will * be applied when a namespaced node is found matching the function * name. The function will be applied in the scope of this parser * with two arguments: the node being read and a context object passed * from the parent. */ readers: OpenLayers.Util.applyDefaults({ "sld": { "StyledLayerDescriptor": function(node, sld) { sld.version = node.getAttribute("version"); this.readChildNodes(node, sld); }, "Name": function(node, obj) { obj.name = this.getChildValue(node); }, "Title": function(node, obj) { obj.title = this.getChildValue(node); }, "Abstract": function(node, obj) { obj.description = this.getChildValue(node); }, "NamedLayer": function(node, sld) { var layer = { userStyles: [], namedStyles: [] }; this.readChildNodes(node, layer); // give each of the user styles this layer name for(var i=0, len=layer.userStyles.length; i<len; ++i) { layer.userStyles[i].layerName = layer.name; } if(OpenLayers.Util.isArray(sld.namedLayers)) { sld.namedLayers.push(layer); } else { sld.namedLayers[layer.name] = layer; } }, "NamedStyle": function(node, layer) { layer.namedStyles.push( this.getChildName(node.firstChild) ); }, "UserStyle": function(node, layer) { var obj = {defaultsPerSymbolizer: true, rules: []}; this.featureTypeCounter = -1; this.readChildNodes(node, obj); var style; if (this.multipleSymbolizers) { delete obj.defaultsPerSymbolizer; style = new OpenLayers.Style2(obj); } else { style = new OpenLayers.Style(this.defaultSymbolizer, obj); } layer.userStyles.push(style); }, "IsDefault": function(node, style) { if(this.getChildValue(node) == "1") { style.isDefault = true; } }, "FeatureTypeStyle": function(node, style) { ++this.featureTypeCounter; var obj = { rules: this.multipleSymbolizers ? style.rules : [] }; this.readChildNodes(node, obj); if (!this.multipleSymbolizers) { style.rules = obj.rules; } }, "Rule": function(node, obj) { var config; if (this.multipleSymbolizers) { config = {symbolizers: []}; } var rule = new OpenLayers.Rule(config); this.readChildNodes(node, rule); obj.rules.push(rule); }, "ElseFilter": function(node, rule) { rule.elseFilter = true; }, "MinScaleDenominator": function(node, rule) { rule.minScaleDenominator = parseFloat(this.getChildValue(node)); }, "MaxScaleDenominator": function(node, rule) { rule.maxScaleDenominator = parseFloat(this.getChildValue(node)); }, "TextSymbolizer": function(node, rule) { var config = {}; this.readChildNodes(node, config); if (this.multipleSymbolizers) { config.zIndex = this.featureTypeCounter; rule.symbolizers.push( new OpenLayers.Symbolizer.Text(config) ); } else { rule.symbolizer["Text"] = OpenLayers.Util.applyDefaults( config, rule.symbolizer["Text"] ); } }, "LabelPlacement": function(node, symbolizer) { this.readChildNodes(node, symbolizer); }, "PointPlacement": function(node, symbolizer) { var config = {}; this.readChildNodes(node, config); config.labelRotation = config.rotation; delete config.rotation; var labelAlign, x = symbolizer.labelAnchorPointX, y = symbolizer.labelAnchorPointY; if (x <= 1/3) { labelAlign = 'l'; } else if (x > 1/3 && x < 2/3) { labelAlign = 'c'; } else if (x >= 2/3) { labelAlign = 'r'; } if (y <= 1/3) { labelAlign += 'b'; } else if (y > 1/3 && y < 2/3) { labelAlign += 'm'; } else if (y >= 2/3) { labelAlign += 't'; } config.labelAlign = labelAlign; OpenLayers.Util.applyDefaults(symbolizer, config); }, "AnchorPoint": function(node, symbolizer) { this.readChildNodes(node, symbolizer); }, "AnchorPointX": function(node, symbolizer) { var labelAnchorPointX = this.readers.ogc._expression.call(this, node); // always string, could be empty string if(labelAnchorPointX) { symbolizer.labelAnchorPointX = labelAnchorPointX; } }, "AnchorPointY": function(node, symbolizer) { var labelAnchorPointY = this.readers.ogc._expression.call(this, node); // always string, could be empty string if(labelAnchorPointY) { symbolizer.labelAnchorPointY = labelAnchorPointY; } }, "Displacement": function(node, symbolizer) { this.readChildNodes(node, symbolizer); }, "DisplacementX": function(node, symbolizer) { var labelXOffset = this.readers.ogc._expression.call(this, node); // always string, could be empty string if(labelXOffset) { symbolizer.labelXOffset = labelXOffset; } }, "DisplacementY": function(node, symbolizer) { var labelYOffset = this.readers.ogc._expression.call(this, node); // always string, could be empty string if(labelYOffset) { symbolizer.labelYOffset = labelYOffset; } }, "LinePlacement": function(node, symbolizer) { this.readChildNodes(node, symbolizer); }, "PerpendicularOffset": function(node, symbolizer) { var labelPerpendicularOffset = this.readers.ogc._expression.call(this, node); // always string, could be empty string if(labelPerpendicularOffset) { symbolizer.labelPerpendicularOffset = labelPerpendicularOffset; } }, "Label": function(node, symbolizer) { var value = this.readers.ogc._expression.call(this, node); if (value) { symbolizer.label = value; } }, "Font": function(node, symbolizer) { this.readChildNodes(node, symbolizer); }, "Halo": function(node, symbolizer) { // halo has a fill, so send fresh object var obj = {}; this.readChildNodes(node, obj); symbolizer.haloRadius = obj.haloRadius; symbolizer.haloColor = obj.fillColor; symbolizer.haloOpacity = obj.fillOpacity; }, "Radius": function(node, symbolizer) { var radius = this.readers.ogc._expression.call(this, node); if(radius != null) { // radius is only used for halo symbolizer.haloRadius = radius; } }, "RasterSymbolizer": function(node, rule) { var config = {}; this.readChildNodes(node, config); if (this.multipleSymbolizers) { config.zIndex = this.featureTypeCounter; rule.symbolizers.push( new OpenLayers.Symbolizer.Raster(config) ); } else { rule.symbolizer["Raster"] = OpenLayers.Util.applyDefaults( config, rule.symbolizer["Raster"] ); } }, "Geometry": function(node, obj) { obj.geometry = {}; this.readChildNodes(node, obj.geometry); }, "ColorMap": function(node, symbolizer) { symbolizer.colorMap = []; this.readChildNodes(node, symbolizer.colorMap); }, "ColorMapEntry": function(node, colorMap) { var q = node.getAttribute("quantity"); var o = node.getAttribute("opacity"); colorMap.push({ color: node.getAttribute("color"), quantity: q !== null ? parseFloat(q) : undefined, label: node.getAttribute("label") || undefined, opacity: o !== null ? parseFloat(o) : undefined }); }, "LineSymbolizer": function(node, rule) { var config = {}; this.readChildNodes(node, config); if (this.multipleSymbolizers) { config.zIndex = this.featureTypeCounter; rule.symbolizers.push( new OpenLayers.Symbolizer.Line(config) ); } else { rule.symbolizer["Line"] = OpenLayers.Util.applyDefaults( config, rule.symbolizer["Line"] ); } }, "PolygonSymbolizer": function(node, rule) { var config = { fill: false, stroke: false }; if (!this.multipleSymbolizers) { config = rule.symbolizer["Polygon"] || config; } this.readChildNodes(node, config); if (this.multipleSymbolizers) { config.zIndex = this.featureTypeCounter; rule.symbolizers.push( new OpenLayers.Symbolizer.Polygon(config) ); } else { rule.symbolizer["Polygon"] = config; } }, "PointSymbolizer": function(node, rule) { var config = { fill: false, stroke: false, graphic: false }; if (!this.multipleSymbolizers) { config = rule.symbolizer["Point"] || config; } this.readChildNodes(node, config); if (this.multipleSymbolizers) { config.zIndex = this.featureTypeCounter; rule.symbolizers.push( new OpenLayers.Symbolizer.Point(config) ); } else { rule.symbolizer["Point"] = config; } }, "Stroke": function(node, symbolizer) { symbolizer.stroke = true; this.readChildNodes(node, symbolizer); }, "Fill": function(node, symbolizer) { symbolizer.fill = true; this.readChildNodes(node, symbolizer); }, "CssParameter": function(node, symbolizer) { var cssProperty = node.getAttribute("name"); var symProperty = this.cssMap[cssProperty]; // for labels, fill should map to fontColor and fill-opacity // to fontOpacity if (symbolizer.label) { if (cssProperty === 'fill') { symProperty = "fontColor"; } else if (cssProperty === 'fill-opacity') { symProperty = "fontOpacity"; } } if(symProperty) { // Limited support for parsing of OGC expressions var value = this.readers.ogc._expression.call(this, node); // always string, could be an empty string if(value) { symbolizer[symProperty] = value; } } }, "Graphic": function(node, symbolizer) { symbolizer.graphic = true; var graphic = {}; // painter's order not respected here, clobber previous with next this.readChildNodes(node, graphic); // directly properties with names that match symbolizer properties var properties = [ "stroke", "strokeColor", "strokeWidth", "strokeOpacity", "strokeLinecap", "fill", "fillColor", "fillOpacity", "graphicName", "rotation", "graphicFormat" ]; var prop, value; for(var i=0, len=properties.length; i<len; ++i) { prop = properties[i]; value = graphic[prop]; if(value != undefined) { symbolizer[prop] = value; } } // set other generic properties with specific graphic property names if(graphic.opacity != undefined) { symbolizer.graphicOpacity = graphic.opacity; } if(graphic.size != undefined) { var pointRadius = graphic.size / 2; if (isNaN(pointRadius)) { // likely a property name symbolizer.graphicWidth = graphic.size; } else { symbolizer.pointRadius = graphic.size / 2; } } if(graphic.href != undefined) { symbolizer.externalGraphic = graphic.href; } if(graphic.rotation != undefined) { symbolizer.rotation = graphic.rotation; } }, "ExternalGraphic": function(node, graphic) { this.readChildNodes(node, graphic); }, "Mark": function(node, graphic) { this.readChildNodes(node, graphic); }, "WellKnownName": function(node, graphic) { graphic.graphicName = this.getChildValue(node); }, "Opacity": function(node, obj) { var opacity = this.readers.ogc._expression.call(this, node); // always string, could be empty string if(opacity) { obj.opacity = opacity; } }, "Size": function(node, obj) { var size = this.readers.ogc._expression.call(this, node); // always string, could be empty string if(size) { obj.size = size; } }, "Rotation": function(node, obj) { var rotation = this.readers.ogc._expression.call(this, node); // always string, could be empty string if(rotation) { obj.rotation = rotation; } }, "OnlineResource": function(node, obj) { obj.href = this.getAttributeNS( node, this.namespaces.xlink, "href" ); }, "Format": function(node, graphic) { graphic.graphicFormat = this.getChildValue(node); } } }, OpenLayers.Format.Filter.v1_0_0.prototype.readers), /** * Property: cssMap * {Object} Object mapping supported css property names to OpenLayers * symbolizer property names. */ cssMap: { "stroke": "strokeColor", "stroke-opacity": "strokeOpacity", "stroke-width": "strokeWidth", "stroke-linecap": "strokeLinecap", "stroke-dasharray": "strokeDashstyle", "fill": "fillColor", "fill-opacity": "fillOpacity", "font-family": "fontFamily", "font-size": "fontSize", "font-weight": "fontWeight", "font-style": "fontStyle" }, /** * Method: getCssProperty * Given a symbolizer property, get the corresponding CSS property * from the <cssMap>. * * Parameters: * sym - {String} A symbolizer property name. * * Returns: * {String} A CSS property name or null if none found. */ getCssProperty: function(sym) { var css = null; for(var prop in this.cssMap) { if(this.cssMap[prop] == sym) { css = prop; break; } } return css; }, /** * Method: getGraphicFormat * Given a href for an external graphic, try to determine the mime-type. * This method doesn't try too hard, and will fall back to * <defaultGraphicFormat> if one of the known <graphicFormats> is not * the file extension of the provided href. * * Parameters: * href - {String} * * Returns: * {String} The graphic format. */ getGraphicFormat: function(href) { var format, regex; for(var key in this.graphicFormats) { if(this.graphicFormats[key].test(href)) { format = key; break; } } return format || this.defaultGraphicFormat; }, /** * Property: defaultGraphicFormat * {String} If none other can be determined from <getGraphicFormat>, this * default will be returned. */ defaultGraphicFormat: "image/png", /** * Property: graphicFormats * {Object} Mapping of image mime-types to regular extensions matching * well-known file extensions. */ graphicFormats: { "image/jpeg": /\.jpe?g$/i, "image/gif": /\.gif$/i, "image/png": /\.png$/i }, /** * Method: write * * Parameters: * sld - {Object} An object representing the SLD. * * Returns: * {DOMElement} The root of an SLD document. */ write: function(sld) { return this.writers.sld.StyledLayerDescriptor.apply(this, [sld]); }, /** * Property: writers * As a compliment to the readers property, this structure contains public * writing functions grouped by namespace alias and named like the * node names they produce. */ writers: OpenLayers.Util.applyDefaults({ "sld": { "_OGCExpression": function(nodeName, value) { // only the simplest of ogc:expression handled // {label: "some text and a ${propertyName}"} var node = this.createElementNSPlus(nodeName); var tokens = typeof value == "string" ? value.split("${") : [value]; node.appendChild(this.createTextNode(tokens[0])); var item, last; for(var i=1, len=tokens.length; i<len; i++) { item = tokens[i]; last = item.indexOf("}"); if(last > 0) { this.writeNode( "ogc:PropertyName", {property: item.substring(0, last)}, node ); node.appendChild( this.createTextNode(item.substring(++last)) ); } else { // no ending }, so this is a literal ${ node.appendChild( this.createTextNode("${" + item) ); } } return node; }, "StyledLayerDescriptor": function(sld) { var root = this.createElementNSPlus( "sld:StyledLayerDescriptor", {attributes: { "version": this.VERSION, "xsi:schemaLocation": this.schemaLocation }} ); // For ArcGIS Server it is necessary to define this // at the root level (see ticket:2166). root.setAttribute("xmlns:ogc", this.namespaces.ogc); root.setAttribute("xmlns:gml", this.namespaces.gml); // add in optional name if(sld.name) { this.writeNode("Name", sld.name, root); } // add in optional title if(sld.title) { this.writeNode("Title", sld.title, root); } // add in optional description if(sld.description) { this.writeNode("Abstract", sld.description, root); } // add in named layers // allow namedLayers to be an array if(OpenLayers.Util.isArray(sld.namedLayers)) { for(var i=0, len=sld.namedLayers.length; i<len; ++i) { this.writeNode("NamedLayer", sld.namedLayers[i], root); } } else { for(var name in sld.namedLayers) { this.writeNode("NamedLayer", sld.namedLayers[name], root); } } return root; }, "Name": function(name) { return this.createElementNSPlus("sld:Name", {value: name}); }, "Title": function(title) { return this.createElementNSPlus("sld:Title", {value: title}); }, "Abstract": function(description) { return this.createElementNSPlus( "sld:Abstract", {value: description} ); }, "NamedLayer": function(layer) { var node = this.createElementNSPlus("sld:NamedLayer"); // add in required name this.writeNode("Name", layer.name, node); // optional sld:LayerFeatureConstraints here // add in named styles if(layer.namedStyles) { for(var i=0, len=layer.namedStyles.length; i<len; ++i) { this.writeNode( "NamedStyle", layer.namedStyles[i], node ); } } // add in user styles if(layer.userStyles) { for(var i=0, len=layer.userStyles.length; i<len; ++i) { this.writeNode( "UserStyle", layer.userStyles[i], node ); } } return node; }, "NamedStyle": function(name) { var node = this.createElementNSPlus("sld:NamedStyle"); this.writeNode("Name", name, node); return node; }, "UserStyle": function(style) { var node = this.createElementNSPlus("sld:UserStyle"); // add in optional name if(style.name) { this.writeNode("Name", style.name, node); } // add in optional title if(style.title) { this.writeNode("Title", style.title, node); } // add in optional description if(style.description) { this.writeNode("Abstract", style.description, node); } // add isdefault if(style.isDefault) { this.writeNode("IsDefault", style.isDefault, node); } // add FeatureTypeStyles if (this.multipleSymbolizers && style.rules) { // group style objects by symbolizer zIndex var rulesByZ = { 0: [] }; var zValues = [0]; var rule, ruleMap, symbolizer, zIndex, clone; for (var i=0, ii=style.rules.length; i<ii; ++i) { rule = style.rules[i]; if (rule.symbolizers) { ruleMap = {}; for (var j=0, jj=rule.symbolizers.length; j<jj; ++j) { symbolizer = rule.symbolizers[j]; zIndex = symbolizer.zIndex; if (!(zIndex in ruleMap)) { clone = rule.clone(); clone.symbolizers = []; ruleMap[zIndex] = clone; } ruleMap[zIndex].symbolizers.push(symbolizer.clone()); } for (zIndex in ruleMap) { if (!(zIndex in rulesByZ)) { zValues.push(zIndex); rulesByZ[zIndex] = []; } rulesByZ[zIndex].push(ruleMap[zIndex]); } } else { // no symbolizers in rule rulesByZ[0].push(rule.clone()); } } // write one FeatureTypeStyle per zIndex zValues.sort(); var rules; for (var i=0, ii=zValues.length; i<ii; ++i) { rules = rulesByZ[zValues[i]]; if (rules.length > 0) { clone = style.clone(); clone.rules = rulesByZ[zValues[i]]; this.writeNode("FeatureTypeStyle", clone, node); } } } else { this.writeNode("FeatureTypeStyle", style, node); } return node; }, "IsDefault": function(bool) { return this.createElementNSPlus( "sld:IsDefault", {value: (bool) ? "1" : "0"} ); }, "FeatureTypeStyle": function(style) { var node = this.createElementNSPlus("sld:FeatureTypeStyle"); // OpenLayers currently stores no Name, Title, Abstract, // FeatureTypeName, or SemanticTypeIdentifier information // related to FeatureTypeStyle // add in rules for(var i=0, len=style.rules.length; i<len; ++i) { this.writeNode("Rule", style.rules[i], node); } return node; }, "Rule": function(rule) { var node = this.createElementNSPlus("sld:Rule"); // add in optional name if(rule.name) { this.writeNode("Name", rule.name, node); } // add in optional title if(rule.title) { this.writeNode("Title", rule.title, node); } // add in optional description if(rule.description) { this.writeNode("Abstract", rule.description, node); } // add in LegendGraphic here // add in optional filters if(rule.elseFilter) { this.writeNode("ElseFilter", null, node); } else if(rule.filter) { this.writeNode("ogc:Filter", rule.filter, node); } // add in scale limits if(rule.minScaleDenominator != undefined) { this.writeNode( "MinScaleDenominator", rule.minScaleDenominator, node ); } if(rule.maxScaleDenominator != undefined) { this.writeNode( "MaxScaleDenominator", rule.maxScaleDenominator, node ); } var type, symbolizer; if (this.multipleSymbolizers && rule.symbolizers) { var symbolizer; for (var i=0, ii=rule.symbolizers.length; i<ii; ++i) { symbolizer = rule.symbolizers[i]; type = symbolizer.CLASS_NAME.split(".").pop(); this.writeNode( type + "Symbolizer", symbolizer, node ); } } else { // add in symbolizers (relies on geometry type keys) var types = OpenLayers.Style.SYMBOLIZER_PREFIXES; for(var i=0, len=types.length; i<len; ++i) { type = types[i]; symbolizer = rule.symbolizer[type]; if(symbolizer) { this.writeNode( type + "Symbolizer", symbolizer, node ); } } } return node; }, "ElseFilter": function() { return this.createElementNSPlus("sld:ElseFilter"); }, "MinScaleDenominator": function(scale) { return this.createElementNSPlus( "sld:MinScaleDenominator", {value: scale} ); }, "MaxScaleDenominator": function(scale) { return this.createElementNSPlus( "sld:MaxScaleDenominator", {value: scale} ); }, "LineSymbolizer": function(symbolizer) { var node = this.createElementNSPlus("sld:LineSymbolizer"); this.writeNode("Stroke", symbolizer, node); return node; }, "Stroke": function(symbolizer) { var node = this.createElementNSPlus("sld:Stroke"); // GraphicFill here // GraphicStroke here // add in CssParameters if(symbolizer.strokeColor != undefined) { this.writeNode( "CssParameter", {symbolizer: symbolizer, key: "strokeColor"}, node ); } if(symbolizer.strokeOpacity != undefined) { this.writeNode( "CssParameter", {symbolizer: symbolizer, key: "strokeOpacity"}, node ); } if(symbolizer.strokeWidth != undefined) { this.writeNode( "CssParameter", {symbolizer: symbolizer, key: "strokeWidth"}, node ); } if(symbolizer.strokeDashstyle != undefined && symbolizer.strokeDashstyle !== "solid") { // assumes valid stroke-dasharray value this.writeNode( "CssParameter", {symbolizer: symbolizer, key: "strokeDashstyle"}, node ); } if(symbolizer.strokeLinecap != undefined) { this.writeNode( "CssParameter", {symbolizer: symbolizer, key: "strokeLinecap"}, node ); } return node; }, "CssParameter": function(obj) { // not handling ogc:expressions for now return this.createElementNSPlus("sld:CssParameter", { attributes: {name: this.getCssProperty(obj.key)}, value: obj.symbolizer[obj.key] }); }, "TextSymbolizer": function(symbolizer) { var node = this.createElementNSPlus("sld:TextSymbolizer"); // add in optional Label if(symbolizer.label != null) { this.writeNode("Label", symbolizer.label, node); } // add in optional Font if(symbolizer.fontFamily != null || symbolizer.fontSize != null || symbolizer.fontWeight != null || symbolizer.fontStyle != null) { this.writeNode("Font", symbolizer, node); } // add in optional LabelPlacement if (symbolizer.labelAnchorPointX != null || symbolizer.labelAnchorPointY != null || symbolizer.labelAlign != null || symbolizer.labelXOffset != null || symbolizer.labelYOffset != null || symbolizer.labelRotation != null || symbolizer.labelPerpendicularOffset != null) { this.writeNode("LabelPlacement", symbolizer, node); } // add in optional Halo if(symbolizer.haloRadius != null || symbolizer.haloColor != null || symbolizer.haloOpacity != null) { this.writeNode("Halo", symbolizer, node); } // add in optional Fill if(symbolizer.fontColor != null || symbolizer.fontOpacity != null) { this.writeNode("Fill", { fillColor: symbolizer.fontColor, fillOpacity: symbolizer.fontOpacity }, node); } return node; }, "LabelPlacement": function(symbolizer) { var node = this.createElementNSPlus("sld:LabelPlacement"); // PointPlacement and LinePlacement are choices, so don't output both if ((symbolizer.labelAnchorPointX != null || symbolizer.labelAnchorPointY != null || symbolizer.labelAlign != null || symbolizer.labelXOffset != null || symbolizer.labelYOffset != null || symbolizer.labelRotation != null) && symbolizer.labelPerpendicularOffset == null) { this.writeNode("PointPlacement", symbolizer, node); } if (symbolizer.labelPerpendicularOffset != null) { this.writeNode("LinePlacement", symbolizer, node); } return node; }, "LinePlacement": function(symbolizer) { var node = this.createElementNSPlus("sld:LinePlacement"); this.writeNode("PerpendicularOffset", symbolizer.labelPerpendicularOffset, node); return node; }, "PerpendicularOffset": function(value) { return this.createElementNSPlus("sld:PerpendicularOffset", { value: value }); }, "PointPlacement": function(symbolizer) { var node = this.createElementNSPlus("sld:PointPlacement"); if (symbolizer.labelAnchorPointX != null || symbolizer.labelAnchorPointY != null || symbolizer.labelAlign != null) { this.writeNode("AnchorPoint", symbolizer, node); } if (symbolizer.labelXOffset != null || symbolizer.labelYOffset != null) { this.writeNode("Displacement", symbolizer, node); } if (symbolizer.labelRotation != null) { this.writeNode("Rotation", symbolizer.labelRotation, node); } return node; }, "AnchorPoint": function(symbolizer) { var node = this.createElementNSPlus("sld:AnchorPoint"); var x = symbolizer.labelAnchorPointX, y = symbolizer.labelAnchorPointY; if (x != null) { this.writeNode("AnchorPointX", x, node); } if (y != null) { this.writeNode("AnchorPointY", y, node); } if (x == null && y == null) { var xAlign = symbolizer.labelAlign.substr(0, 1), yAlign = symbolizer.labelAlign.substr(1, 1); if (xAlign === "l") { x = 0; } else if (xAlign === "c") { x = 0.5; } else if (xAlign === "r") { x = 1; } if (yAlign === "b") { y = 0; } else if (yAlign === "m") { y = 0.5; } else if (yAlign === "t") { y = 1; } this.writeNode("AnchorPointX", x, node); this.writeNode("AnchorPointY", y, node); } return node; }, "AnchorPointX": function(value) { return this.createElementNSPlus("sld:AnchorPointX", { value: value }); }, "AnchorPointY": function(value) { return this.createElementNSPlus("sld:AnchorPointY", { value: value }); }, "Displacement": function(symbolizer) { var node = this.createElementNSPlus("sld:Displacement"); if (symbolizer.labelXOffset != null) { this.writeNode("DisplacementX", symbolizer.labelXOffset, node); } if (symbolizer.labelYOffset != null) { this.writeNode("DisplacementY", symbolizer.labelYOffset, node); } return node; }, "DisplacementX": function(value) { return this.createElementNSPlus("sld:DisplacementX", { value: value }); }, "DisplacementY": function(value) { return this.createElementNSPlus("sld:DisplacementY", { value: value }); }, "Font": function(symbolizer) { var node = this.createElementNSPlus("sld:Font"); // add in CssParameters if(symbolizer.fontFamily) { this.writeNode( "CssParameter", {symbolizer: symbolizer, key: "fontFamily"}, node ); } if(symbolizer.fontSize) { this.writeNode( "CssParameter", {symbolizer: symbolizer, key: "fontSize"}, node ); } if(symbolizer.fontWeight) { this.writeNode( "CssParameter", {symbolizer: symbolizer, key: "fontWeight"}, node ); } if(symbolizer.fontStyle) { this.writeNode( "CssParameter", {symbolizer: symbolizer, key: "fontStyle"}, node ); } return node; }, "Label": function(label) { return this.writers.sld._OGCExpression.call( this, "sld:Label", label ); }, "Halo": function(symbolizer) { var node = this.createElementNSPlus("sld:Halo"); if(symbolizer.haloRadius) { this.writeNode("Radius", symbolizer.haloRadius, node); } if(symbolizer.haloColor || symbolizer.haloOpacity) { this.writeNode("Fill", { fillColor: symbolizer.haloColor, fillOpacity: symbolizer.haloOpacity }, node); } return node; }, "Radius": function(value) { return this.createElementNSPlus("sld:Radius", { value: value }); }, "RasterSymbolizer": function(symbolizer) { var node = this.createElementNSPlus("sld:RasterSymbolizer"); if (symbolizer.geometry) { this.writeNode("Geometry", symbolizer.geometry, node); } if (symbolizer.opacity) { this.writeNode("Opacity", symbolizer.opacity, node); } if (symbolizer.colorMap) { this.writeNode("ColorMap", symbolizer.colorMap, node); } return node; }, "Geometry": function(geometry) { var node = this.createElementNSPlus("sld:Geometry"); if (geometry.property) { this.writeNode("ogc:PropertyName", geometry, node); } return node; }, "ColorMap": function(colorMap) { var node = this.createElementNSPlus("sld:ColorMap"); for (var i=0, len=colorMap.length; i<len; ++i) { this.writeNode("ColorMapEntry", colorMap[i], node); } return node; }, "ColorMapEntry": function(colorMapEntry) { var node = this.createElementNSPlus("sld:ColorMapEntry"); var a = colorMapEntry; node.setAttribute("color", a.color); a.opacity !== undefined && node.setAttribute("opacity", parseFloat(a.opacity)); a.quantity !== undefined && node.setAttribute("quantity", parseFloat(a.quantity)); a.label !== undefined && node.setAttribute("label", a.label); return node; }, "PolygonSymbolizer": function(symbolizer) { var node = this.createElementNSPlus("sld:PolygonSymbolizer"); if(symbolizer.fill !== false) { this.writeNode("Fill", symbolizer, node); } if(symbolizer.stroke !== false) { this.writeNode("Stroke", symbolizer, node); } return node; }, "Fill": function(symbolizer) { var node = this.createElementNSPlus("sld:Fill"); // GraphicFill here // add in CssParameters if(symbolizer.fillColor) { this.writeNode( "CssParameter", {symbolizer: symbolizer, key: "fillColor"}, node ); } if(symbolizer.fillOpacity != null) { this.writeNode( "CssParameter", {symbolizer: symbolizer, key: "fillOpacity"}, node ); } return node; }, "PointSymbolizer": function(symbolizer) { var node = this.createElementNSPlus("sld:PointSymbolizer"); this.writeNode("Graphic", symbolizer, node); return node; }, "Graphic": function(symbolizer) { var node = this.createElementNSPlus("sld:Graphic"); if(symbolizer.externalGraphic != undefined) { this.writeNode("ExternalGraphic", symbolizer, node); } else { this.writeNode("Mark", symbolizer, node); } if(symbolizer.graphicOpacity != undefined) { this.writeNode("Opacity", symbolizer.graphicOpacity, node); } if(symbolizer.pointRadius != undefined) { this.writeNode("Size", symbolizer.pointRadius * 2, node); } else if (symbolizer.graphicWidth != undefined) { this.writeNode("Size", symbolizer.graphicWidth, node); } if(symbolizer.rotation != undefined) { this.writeNode("Rotation", symbolizer.rotation, node); } return node; }, "ExternalGraphic": function(symbolizer) { var node = this.createElementNSPlus("sld:ExternalGraphic"); this.writeNode( "OnlineResource", symbolizer.externalGraphic, node ); var format = symbolizer.graphicFormat || this.getGraphicFormat(symbolizer.externalGraphic); this.writeNode("Format", format, node); return node; }, "Mark": function(symbolizer) { var node = this.createElementNSPlus("sld:Mark"); if(symbolizer.graphicName) { this.writeNode("WellKnownName", symbolizer.graphicName, node); } if (symbolizer.fill !== false) { this.writeNode("Fill", symbolizer, node); } if (symbolizer.stroke !== false) { this.writeNode("Stroke", symbolizer, node); } return node; }, "WellKnownName": function(name) { return this.createElementNSPlus("sld:WellKnownName", { value: name }); }, "Opacity": function(value) { return this.createElementNSPlus("sld:Opacity", { value: value }); }, "Size": function(value) { return this.writers.sld._OGCExpression.call( this, "sld:Size", value ); }, "Rotation": function(value) { return this.createElementNSPlus("sld:Rotation", { value: value }); }, "OnlineResource": function(href) { return this.createElementNSPlus("sld:OnlineResource", { attributes: { "xlink:type": "simple", "xlink:href": href } }); }, "Format": function(format) { return this.createElementNSPlus("sld:Format", { value: format }); } } }, OpenLayers.Format.Filter.v1_0_0.prototype.writers), CLASS_NAME: "OpenLayers.Format.SLD.v1" }); /* ====================================================================== OpenLayers/Format/SLD/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/SLD/v1.js * @requires OpenLayers/Format/Filter/v1_0_0.js */ /** * Class: OpenLayers.Format.SLD.v1_0_0 * Write SLD version 1.0.0. * * Inherits from: * - <OpenLayers.Format.SLD.v1> */ OpenLayers.Format.SLD.v1_0_0 = OpenLayers.Class( OpenLayers.Format.SLD.v1, { /** * Constant: VERSION * {String} 1.0.0 */ VERSION: "1.0.0", /** * Property: schemaLocation * {String} http://www.opengis.net/sld * http://schemas.opengis.net/sld/1.0.0/StyledLayerDescriptor.xsd */ schemaLocation: "http://www.opengis.net/sld http://schemas.opengis.net/sld/1.0.0/StyledLayerDescriptor.xsd", /** * Constructor: OpenLayers.Format.SLD.v1_0_0 * Instances of this class are not created directly. Use the * <OpenLayers.Format.SLD> constructor instead. * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ CLASS_NAME: "OpenLayers.Format.SLD.v1_0_0" }); /* ====================================================================== OpenLayers/Format/OWSContext/v0_3_1.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/KML.js * @requires OpenLayers/Format/GML.js * @requires OpenLayers/Format/GML/v2.js * @requires OpenLayers/Format/SLD/v1_0_0.js * @requires OpenLayers/Format/OWSContext.js * @requires OpenLayers/Format/OWSCommon/v1_0_0.js */ /** * Class: OpenLayers.Format.OWSContext.v0_3_1 * Read and write OWSContext version 0.3.1. * * Inherits from: * - <OpenLayers.Format.XML> */ OpenLayers.Format.OWSContext.v0_3_1 = OpenLayers.Class(OpenLayers.Format.XML, { /** * Property: namespaces * {Object} Mapping of namespace aliases to namespace URIs. */ namespaces: { owc: "http://www.opengis.net/ows-context", gml: "http://www.opengis.net/gml", kml: "http://www.opengis.net/kml/2.2", ogc: "http://www.opengis.net/ogc", ows: "http://www.opengis.net/ows", sld: "http://www.opengis.net/sld", xlink: "http://www.w3.org/1999/xlink", xsi: "http://www.w3.org/2001/XMLSchema-instance" }, /** * Constant: VERSION * {String} 0.3.1 */ VERSION: "0.3.1", /** * Property: schemaLocation * {String} Schema location */ schemaLocation: "http://www.opengis.net/ows-context http://www.ogcnetwork.net/schemas/owc/0.3.1/owsContext.xsd", /** * Property: defaultPrefix * {String} Default namespace prefix to use. */ defaultPrefix: "owc", /** * 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: regExes * Compiled regular expressions for manipulating strings. */ regExes: { trimSpace: (/^\s*|\s*$/g), removeSpace: (/\s*/g), splitSpace: (/\s+/), trimComma: (/\s*,\s*/g) }, /** * Property: featureNS * {String} The namespace uri to use for writing InlineGeometry */ featureNS: "http://mapserver.gis.umn.edu/mapserver", /** * Property: featureType * {String} The name to use as the feature type when writing out * InlineGeometry */ featureType: 'vector', /** * Property: geometryName * {String} The name to use for the geometry attribute when writing out * InlineGeometry */ geometryName: 'geometry', /** * Property: nestingLayerLookup * {Object} Hashtable lookup for nesting layer nodes. Used while writing * the OWS context document. It is necessary to keep track of the * nestingPaths for which nesting layer nodes have already been * created, so (nesting) layer nodes are added to those nodes. * * For example: * * If there are three layers with nestingPaths: * layer1.metadata.nestingPath = "a/b/" * layer2.metadata.nestingPath = "a/b/" * layer2.metadata.nestingPath = "a/c" * * then a nesting layer node "a" should be created once and added * to the resource list, a nesting layer node "b" should be created * once and added under "a", and a nesting layer node "c" should be * created and added under "a". The lookup paths for these nodes * will be "a", "a/b", and "a/c" respectively. */ nestingLayerLookup: null, /** * Constructor: OpenLayers.Format.OWSContext.v0_3_1 * Instances of this class are not created directly. Use the * <OpenLayers.Format.OWSContext> 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]); OpenLayers.Format.GML.v2.prototype.setGeometryTypes.call(this); }, /** * Method: setNestingPath * Set the nestingPath property of the layer depending on the position * of the layer in hierarchy of layers. * * Parameters: * l - {Object} An object that may have a layersContext array property. * */ setNestingPath : function(l){ if(l.layersContext){ for (var i = 0, len = l.layersContext.length; i < len; i++) { var layerContext = l.layersContext[i]; var nPath = []; var nTitle = l.title || ""; if(l.metadata && l.metadata.nestingPath){ nPath = l.metadata.nestingPath.slice(); } if (nTitle != "") { nPath.push(nTitle); } layerContext.metadata.nestingPath = nPath; if(layerContext.layersContext){ this.setNestingPath(layerContext); } } } }, /** * Function: decomposeNestingPath * Takes a nestingPath like "a/b/c" and decomposes it into subpaths: * "a", "a/b", "a/b/c" * * Parameters: * nPath - {Array} the nesting path * * Returns: * Array({String}) Array with subpaths, or empty array if there is nothing * to decompose */ decomposeNestingPath: function(nPath){ var a = []; if (OpenLayers.Util.isArray(nPath)) { var path = nPath.slice(); while (path.length > 0) { a.push(path.slice()); path.pop(); } a.reverse(); } return a; }, /** * APIMethod: read * Read OWS context data from a string or DOMElement, and return a list * of layers. * * Parameters: * data - {String} or {DOMElement} data to read/parse. * * Returns: * {Object} The context object with a flat layer list as a property named * layersContext. */ 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 context = {}; this.readNode(data, context); // since an OWSContext can be nested we need to go through this // structure recursively this.setNestingPath({layersContext : context.layersContext}); // after nesting path has been set, create a flat list of layers var layers = []; this.processLayer(layers, context); delete context.layersContext; context.layersContext = layers; return context; }, /** * Method: processLayer * Recursive function to get back a flat list of layers from the hierarchic * layer structure. * * Parameters: * layerArray - {Array({Object})} Array of layerContext objects * layer - {Object} layerContext object */ processLayer: function(layerArray, layer) { if (layer.layersContext) { for (var i=0, len = layer.layersContext.length; i<len; i++) { var l = layer.layersContext[i]; layerArray.push(l); if (l.layersContext) { this.processLayer(layerArray, l); } } } }, /** * APIMethod: write * * Parameters: * context - {Object} An object representing the map context. * options - {Object} Optional object. * * Returns: * {String} An OWS Context document string. */ write: function(context, options) { var name = "OWSContext"; this.nestingLayerLookup = {}; //start with empty lookup options = options || {}; OpenLayers.Util.applyDefaults(options, context); var root = this.writeNode(name, options); this.nestingLayerLookup = null; //clear lookup this.setAttributeNS( root, this.namespaces["xsi"], "xsi:schemaLocation", this.schemaLocation ); return OpenLayers.Format.XML.prototype.write.apply(this, [root]); }, /** * 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": { "Document": function(node, obj) { obj.features = new OpenLayers.Format.KML( {kmlns: this.namespaces.kml, extractStyles: true}).read(node); } }, "owc": { "OWSContext": function(node, obj) { this.readChildNodes(node, obj); }, "General": function(node, obj) { this.readChildNodes(node, obj); }, "ResourceList": function(node, obj) { this.readChildNodes(node, obj); }, "Layer": function(node, obj) { var layerContext = { metadata: {}, visibility: (node.getAttribute("hidden") != "1"), queryable: (node.getAttribute("queryable") == "1"), opacity: ((node.getAttribute("opacity") != null) ? parseFloat(node.getAttribute("opacity")) : null), name: node.getAttribute("name"), /* A category layer is a dummy layer meant for creating hierarchies. It is not a physical layer in the OpenLayers sense. The assumption we make here is that category layers do not have a name attribute */ categoryLayer: (node.getAttribute("name") == null), formats: [], styles: [] }; if (!obj.layersContext) { obj.layersContext = []; } obj.layersContext.push(layerContext); this.readChildNodes(node, layerContext); }, "InlineGeometry": function(node, obj) { obj.features = []; var elements = this.getElementsByTagNameNS(node, this.namespaces.gml, "featureMember"); var el; if (elements.length >= 1) { el = elements[0]; } if (el && el.firstChild) { var featurenode = (el.firstChild.nextSibling) ? el.firstChild.nextSibling : el.firstChild; this.setNamespace("feature", featurenode.namespaceURI); this.featureType = featurenode.localName || featurenode.nodeName.split(":").pop(); this.readChildNodes(node, obj); } }, "Server": function(node, obj) { // when having multiple Server types, we prefer WMS if ((!obj.service && !obj.version) || (obj.service != OpenLayers.Format.Context.serviceTypes.WMS)) { obj.service = node.getAttribute("service"); obj.version = node.getAttribute("version"); this.readChildNodes(node, obj); } }, "Name": function(node, obj) { obj.name = this.getChildValue(node); this.readChildNodes(node, obj); }, "Title": function(node, obj) { obj.title = this.getChildValue(node); this.readChildNodes(node, obj); }, "StyleList": function(node, obj) { this.readChildNodes(node, obj.styles); }, "Style": function(node, obj) { var style = {}; obj.push(style); this.readChildNodes(node, style); }, "LegendURL": function(node, obj) { var legend = {}; obj.legend = legend; this.readChildNodes(node, legend); }, "OnlineResource": function(node, obj) { obj.url = this.getAttributeNS(node, this.namespaces.xlink, "href"); this.readChildNodes(node, obj); } }, "ows": OpenLayers.Format.OWSCommon.v1_0_0.prototype.readers.ows, "gml": OpenLayers.Format.GML.v2.prototype.readers.gml, "sld": OpenLayers.Format.SLD.v1_0_0.prototype.readers.sld, "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: { "owc": { "OWSContext": function(options) { var node = this.createElementNSPlus("OWSContext", { attributes: { version: this.VERSION, id: options.id || OpenLayers.Util.createUniqueID("OpenLayers_OWSContext_") } }); this.writeNode("General", options, node); this.writeNode("ResourceList", options, node); return node; }, "General": function(options) { var node = this.createElementNSPlus("General"); this.writeNode("ows:BoundingBox", options, node); this.writeNode("ows:Title", options.title || 'OpenLayers OWSContext', node); return node; }, "ResourceList": function(options) { var node = this.createElementNSPlus("ResourceList"); for (var i=0, len=options.layers.length; i<len; i++) { var layer = options.layers[i]; var decomposedPath = this.decomposeNestingPath(layer.metadata.nestingPath); this.writeNode("_Layer", {layer: layer, subPaths: decomposedPath}, node); } return node; }, "Server": function(options) { var node = this.createElementNSPlus("Server", {attributes: { version: options.version, service: options.service } }); this.writeNode("OnlineResource", options, node); return node; }, "OnlineResource": function(options) { var node = this.createElementNSPlus("OnlineResource", {attributes: { "xlink:href": options.url } }); return node; }, "InlineGeometry": function(layer) { var node = this.createElementNSPlus("InlineGeometry"), dataExtent = layer.getDataExtent(); if (dataExtent !== null) { this.writeNode("gml:boundedBy", dataExtent, node); } for (var i=0, len=layer.features.length; i<len; i++) { this.writeNode("gml:featureMember", layer.features[i], node); } return node; }, "StyleList": function(styles) { var node = this.createElementNSPlus("StyleList"); for (var i=0, len=styles.length; i<len; i++) { this.writeNode("Style", styles[i], node); } return node; }, "Style": function(style) { var node = this.createElementNSPlus("Style"); this.writeNode("Name", style, node); this.writeNode("Title", style, node); if (style.legend) { this.writeNode("LegendURL", style, node); } return node; }, "Name": function(obj) { var node = this.createElementNSPlus("Name", { value: obj.name }); return node; }, "Title": function(obj) { var node = this.createElementNSPlus("Title", { value: obj.title }); return node; }, "LegendURL": function(style) { var node = this.createElementNSPlus("LegendURL"); this.writeNode("OnlineResource", style.legend, node); return node; }, "_WMS": function(layer) { var node = this.createElementNSPlus("Layer", {attributes: { name: layer.params.LAYERS, queryable: layer.queryable ? "1" : "0", hidden: layer.visibility ? "0" : "1", opacity: layer.hasOwnProperty("opacity") ? layer.opacity : null} }); this.writeNode("ows:Title", layer.name, node); this.writeNode("ows:OutputFormat", layer.params.FORMAT, node); this.writeNode("Server", {service: OpenLayers.Format.Context.serviceTypes.WMS, version: layer.params.VERSION, url: layer.url}, node); if (layer.metadata.styles && layer.metadata.styles.length > 0) { this.writeNode("StyleList", layer.metadata.styles, node); } return node; }, "_Layer": function(options) { var layer, subPaths, node, title; layer = options.layer; subPaths = options.subPaths; node = null; title = null; // subPaths is an array of an array // recursively calling _Layer writer eats up subPaths, until a // real writer is called and nodes are returned. if(subPaths.length > 0){ var path = subPaths[0].join("/"); var index = path.lastIndexOf("/"); node = this.nestingLayerLookup[path]; title = (index > 0)?path.substring(index + 1, path.length):path; if(!node){ // category layer node = this.createElementNSPlus("Layer"); this.writeNode("ows:Title", title, node); this.nestingLayerLookup[path] = node; } options.subPaths.shift();//remove a path after each call this.writeNode("_Layer", options, node); return node; } else { // write out the actual layer if (layer instanceof OpenLayers.Layer.WMS) { node = this.writeNode("_WMS", layer); } else if (layer instanceof OpenLayers.Layer.Vector) { if (layer.protocol instanceof OpenLayers.Protocol.WFS.v1) { node = this.writeNode("_WFS", layer); } else if (layer.protocol instanceof OpenLayers.Protocol.HTTP) { if (layer.protocol.format instanceof OpenLayers.Format.GML) { layer.protocol.format.version = "2.1.2"; node = this.writeNode("_GML", layer); } else if (layer.protocol.format instanceof OpenLayers.Format.KML) { layer.protocol.format.version = "2.2"; node = this.writeNode("_KML", layer); } } else { // write out as inline GML since we have no idea // about the original Format this.setNamespace("feature", this.featureNS); node = this.writeNode("_InlineGeometry", layer); } } if (layer.options.maxScale) { this.writeNode("sld:MinScaleDenominator", layer.options.maxScale, node); } if (layer.options.minScale) { this.writeNode("sld:MaxScaleDenominator", layer.options.minScale, node); } this.nestingLayerLookup[layer.name] = node; return node; } }, "_WFS": function(layer) { var node = this.createElementNSPlus("Layer", {attributes: { name: layer.protocol.featurePrefix + ":" + layer.protocol.featureType, hidden: layer.visibility ? "0" : "1" } }); this.writeNode("ows:Title", layer.name, node); this.writeNode("Server", {service: OpenLayers.Format.Context.serviceTypes.WFS, version: layer.protocol.version, url: layer.protocol.url}, node); return node; }, "_InlineGeometry": function(layer) { var node = this.createElementNSPlus("Layer", {attributes: { name: this.featureType, hidden: layer.visibility ? "0" : "1" } }); this.writeNode("ows:Title", layer.name, node); this.writeNode("InlineGeometry", layer, node); return node; }, "_GML": function(layer) { var node = this.createElementNSPlus("Layer"); this.writeNode("ows:Title", layer.name, node); this.writeNode("Server", {service: OpenLayers.Format.Context.serviceTypes.GML, url: layer.protocol.url, version: layer.protocol.format.version}, node); return node; }, "_KML": function(layer) { var node = this.createElementNSPlus("Layer"); this.writeNode("ows:Title", layer.name, node); this.writeNode("Server", {service: OpenLayers.Format.Context.serviceTypes.KML, version: layer.protocol.format.version, url: layer.protocol.url}, node); return node; } }, "gml": OpenLayers.Util.applyDefaults({ "boundedBy": function(bounds) { var node = this.createElementNSPlus("gml:boundedBy"); this.writeNode("gml:Box", bounds, node); return node; } }, OpenLayers.Format.GML.v2.prototype.writers.gml), "ows": OpenLayers.Format.OWSCommon.v1_0_0.prototype.writers.ows, "sld": OpenLayers.Format.SLD.v1_0_0.prototype.writers.sld, "feature": OpenLayers.Format.GML.v2.prototype.writers.feature }, CLASS_NAME: "OpenLayers.Format.OWSContext.v0_3_1" }); /* ====================================================================== OpenLayers/Format/WMSCapabilities/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/WMSCapabilities.js * @requires OpenLayers/Format/OGCExceptionReport.js * @requires OpenLayers/Format/XML.js */ /** * Class: OpenLayers.Format.WMSCapabilities.v1 * Abstract class not to be instantiated directly. Creates * the common parts for both WMS 1.1.X and WMS 1.3.X. * * Inherits from: * - <OpenLayers.Format.XML> */ OpenLayers.Format.WMSCapabilities.v1 = OpenLayers.Class( OpenLayers.Format.XML, { /** * Property: namespaces * {Object} Mapping of namespace aliases to namespace URIs. */ namespaces: { wms: "http://www.opengis.net/wms", xlink: "http://www.w3.org/1999/xlink", xsi: "http://www.w3.org/2001/XMLSchema-instance" }, /** * Property: defaultPrefix */ defaultPrefix: "wms", /** * Constructor: OpenLayers.Format.WMSCapabilities.v1 * Create an instance of one of the subclasses. * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ /** * APIMethod: read * Read capabilities data from a string, and return a list of layers. * * Parameters: * data - {String} or {DOMElement} data to read/parse. * * Returns: * {Array} List of named layers. */ read: function(data) { if(typeof data == "string") { data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); } var raw = data; if(data && data.nodeType == 9) { data = data.documentElement; } var capabilities = {}; this.readNode(data, capabilities); if (capabilities.service === undefined) { // an exception must have occurred, so parse it var parser = new OpenLayers.Format.OGCExceptionReport(); capabilities.error = parser.read(raw); } return capabilities; }, /** * 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: { "wms": { "Service": function(node, obj) { obj.service = {}; this.readChildNodes(node, obj.service); }, "Name": function(node, obj) { obj.name = this.getChildValue(node); }, "Title": function(node, obj) { obj.title = this.getChildValue(node); }, "Abstract": function(node, obj) { obj["abstract"] = this.getChildValue(node); }, "BoundingBox": function(node, obj) { var bbox = {}; bbox.bbox = [ parseFloat(node.getAttribute("minx")), parseFloat(node.getAttribute("miny")), parseFloat(node.getAttribute("maxx")), parseFloat(node.getAttribute("maxy")) ]; var res = { x: parseFloat(node.getAttribute("resx")), y: parseFloat(node.getAttribute("resy")) }; if (! (isNaN(res.x) && isNaN(res.y))) { bbox.res = res; } // return the bbox so that descendant classes can set the // CRS and SRS and add it to the obj return bbox; }, "OnlineResource": function(node, obj) { obj.href = this.getAttributeNS(node, this.namespaces.xlink, "href"); }, "ContactInformation": function(node, obj) { obj.contactInformation = {}; this.readChildNodes(node, obj.contactInformation); }, "ContactPersonPrimary": function(node, obj) { obj.personPrimary = {}; this.readChildNodes(node, obj.personPrimary); }, "ContactPerson": function(node, obj) { obj.person = this.getChildValue(node); }, "ContactOrganization": function(node, obj) { obj.organization = this.getChildValue(node); }, "ContactPosition": function(node, obj) { obj.position = this.getChildValue(node); }, "ContactAddress": function(node, obj) { obj.contactAddress = {}; this.readChildNodes(node, obj.contactAddress); }, "AddressType": function(node, obj) { obj.type = this.getChildValue(node); }, "Address": function(node, obj) { obj.address = this.getChildValue(node); }, "City": function(node, obj) { obj.city = this.getChildValue(node); }, "StateOrProvince": function(node, obj) { obj.stateOrProvince = this.getChildValue(node); }, "PostCode": function(node, obj) { obj.postcode = this.getChildValue(node); }, "Country": function(node, obj) { obj.country = this.getChildValue(node); }, "ContactVoiceTelephone": function(node, obj) { obj.phone = this.getChildValue(node); }, "ContactFacsimileTelephone": function(node, obj) { obj.fax = this.getChildValue(node); }, "ContactElectronicMailAddress": function(node, obj) { obj.email = this.getChildValue(node); }, "Fees": function(node, obj) { var fees = this.getChildValue(node); if (fees && fees.toLowerCase() != "none") { obj.fees = fees; } }, "AccessConstraints": function(node, obj) { var constraints = this.getChildValue(node); if (constraints && constraints.toLowerCase() != "none") { obj.accessConstraints = constraints; } }, "Capability": function(node, obj) { obj.capability = { nestedLayers: [], layers: [] }; this.readChildNodes(node, obj.capability); }, "Request": function(node, obj) { obj.request = {}; this.readChildNodes(node, obj.request); }, "GetCapabilities": function(node, obj) { obj.getcapabilities = {formats: []}; this.readChildNodes(node, obj.getcapabilities); }, "Format": function(node, obj) { if (OpenLayers.Util.isArray(obj.formats)) { obj.formats.push(this.getChildValue(node)); } else { obj.format = this.getChildValue(node); } }, "DCPType": function(node, obj) { this.readChildNodes(node, obj); }, "HTTP": function(node, obj) { this.readChildNodes(node, obj); }, "Get": function(node, obj) { obj.get = {}; this.readChildNodes(node, obj.get); // backwards compatibility if (!obj.href) { obj.href = obj.get.href; } }, "Post": function(node, obj) { obj.post = {}; this.readChildNodes(node, obj.post); // backwards compatibility if (!obj.href) { obj.href = obj.get.href; } }, "GetMap": function(node, obj) { obj.getmap = {formats: []}; this.readChildNodes(node, obj.getmap); }, "GetFeatureInfo": function(node, obj) { obj.getfeatureinfo = {formats: []}; this.readChildNodes(node, obj.getfeatureinfo); }, "Exception": function(node, obj) { obj.exception = {formats: []}; this.readChildNodes(node, obj.exception); }, "Layer": function(node, obj) { var parentLayer, capability; if (obj.capability) { capability = obj.capability; parentLayer = obj; } else { capability = obj; } var attrNode = node.getAttributeNode("queryable"); var queryable = (attrNode && attrNode.specified) ? node.getAttribute("queryable") : null; attrNode = node.getAttributeNode("cascaded"); var cascaded = (attrNode && attrNode.specified) ? node.getAttribute("cascaded") : null; attrNode = node.getAttributeNode("opaque"); var opaque = (attrNode && attrNode.specified) ? node.getAttribute('opaque') : null; var noSubsets = node.getAttribute('noSubsets'); var fixedWidth = node.getAttribute('fixedWidth'); var fixedHeight = node.getAttribute('fixedHeight'); var parent = parentLayer || {}, extend = OpenLayers.Util.extend; var layer = { nestedLayers: [], styles: parentLayer ? [].concat(parentLayer.styles) : [], srs: parentLayer ? extend({}, parent.srs) : {}, metadataURLs: [], bbox: parentLayer ? extend({}, parent.bbox) : {}, llbbox: parent.llbbox, dimensions: parentLayer ? extend({}, parent.dimensions) : {}, authorityURLs: parentLayer ? extend({}, parent.authorityURLs) : {}, identifiers: {}, keywords: [], queryable: (queryable && queryable !== "") ? (queryable === "1" || queryable === "true" ) : (parent.queryable || false), cascaded: (cascaded !== null) ? parseInt(cascaded) : (parent.cascaded || 0), opaque: opaque ? (opaque === "1" || opaque === "true" ) : (parent.opaque || false), noSubsets: (noSubsets !== null) ? (noSubsets === "1" || noSubsets === "true" ) : (parent.noSubsets || false), fixedWidth: (fixedWidth != null) ? parseInt(fixedWidth) : (parent.fixedWidth || 0), fixedHeight: (fixedHeight != null) ? parseInt(fixedHeight) : (parent.fixedHeight || 0), minScale: parent.minScale, maxScale: parent.maxScale, attribution: parent.attribution }; obj.nestedLayers.push(layer); layer.capability = capability; this.readChildNodes(node, layer); delete layer.capability; if(layer.name) { var parts = layer.name.split(":"), request = capability.request, gfi = request.getfeatureinfo; if(parts.length > 0) { layer.prefix = parts[0]; } capability.layers.push(layer); if (layer.formats === undefined) { layer.formats = request.getmap.formats; } if (layer.infoFormats === undefined && gfi) { layer.infoFormats = gfi.formats; } } }, "Attribution": function(node, obj) { obj.attribution = {}; this.readChildNodes(node, obj.attribution); }, "LogoURL": function(node, obj) { obj.logo = { width: node.getAttribute("width"), height: node.getAttribute("height") }; this.readChildNodes(node, obj.logo); }, "Style": function(node, obj) { var style = {}; obj.styles.push(style); this.readChildNodes(node, style); }, "LegendURL": function(node, obj) { var legend = { width: node.getAttribute("width"), height: node.getAttribute("height") }; obj.legend = legend; this.readChildNodes(node, legend); }, "MetadataURL": function(node, obj) { var metadataURL = {type: node.getAttribute("type")}; obj.metadataURLs.push(metadataURL); this.readChildNodes(node, metadataURL); }, "DataURL": function(node, obj) { obj.dataURL = {}; this.readChildNodes(node, obj.dataURL); }, "FeatureListURL": function(node, obj) { obj.featureListURL = {}; this.readChildNodes(node, obj.featureListURL); }, "AuthorityURL": function(node, obj) { var name = node.getAttribute("name"); var authority = {}; this.readChildNodes(node, authority); obj.authorityURLs[name] = authority.href; }, "Identifier": function(node, obj) { var authority = node.getAttribute("authority"); obj.identifiers[authority] = this.getChildValue(node); }, "KeywordList": function(node, obj) { this.readChildNodes(node, obj); }, "SRS": function(node, obj) { obj.srs[this.getChildValue(node)] = true; } } }, CLASS_NAME: "OpenLayers.Format.WMSCapabilities.v1" }); /* ====================================================================== OpenLayers/Format/WMSCapabilities/v1_1.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/WMSCapabilities/v1.js */ /** * Class: OpenLayers.Format.WMSCapabilities.v1_1 * Abstract class not to be instantiated directly. * * Inherits from: * - <OpenLayers.Format.WMSCapabilities.v1> */ OpenLayers.Format.WMSCapabilities.v1_1 = OpenLayers.Class( OpenLayers.Format.WMSCapabilities.v1, { /** * 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: { "wms": OpenLayers.Util.applyDefaults({ "WMT_MS_Capabilities": function(node, obj) { this.readChildNodes(node, obj); }, "Keyword": function(node, obj) { if (obj.keywords) { obj.keywords.push(this.getChildValue(node)); } }, "DescribeLayer": function(node, obj) { obj.describelayer = {formats: []}; this.readChildNodes(node, obj.describelayer); }, "GetLegendGraphic": function(node, obj) { obj.getlegendgraphic = {formats: []}; this.readChildNodes(node, obj.getlegendgraphic); }, "GetStyles": function(node, obj) { obj.getstyles = {formats: []}; this.readChildNodes(node, obj.getstyles); }, "PutStyles": function(node, obj) { obj.putstyles = {formats: []}; this.readChildNodes(node, obj.putstyles); }, "UserDefinedSymbolization": function(node, obj) { var userSymbols = { supportSLD: parseInt(node.getAttribute("SupportSLD")) == 1, userLayer: parseInt(node.getAttribute("UserLayer")) == 1, userStyle: parseInt(node.getAttribute("UserStyle")) == 1, remoteWFS: parseInt(node.getAttribute("RemoteWFS")) == 1 }; obj.userSymbols = userSymbols; }, "LatLonBoundingBox": function(node, obj) { obj.llbbox = [ parseFloat(node.getAttribute("minx")), parseFloat(node.getAttribute("miny")), parseFloat(node.getAttribute("maxx")), parseFloat(node.getAttribute("maxy")) ]; }, "BoundingBox": function(node, obj) { var bbox = OpenLayers.Format.WMSCapabilities.v1.prototype.readers["wms"].BoundingBox.apply(this, [node, obj]); bbox.srs = node.getAttribute("SRS"); obj.bbox[bbox.srs] = bbox; }, "ScaleHint": function(node, obj) { var min = node.getAttribute("min"); var max = node.getAttribute("max"); var rad2 = Math.pow(2, 0.5); var ipm = OpenLayers.INCHES_PER_UNIT["m"]; if (min != 0) { obj.maxScale = parseFloat( ((min / rad2) * ipm * OpenLayers.DOTS_PER_INCH).toPrecision(13) ); } if (max != Number.POSITIVE_INFINITY) { obj.minScale = parseFloat( ((max / rad2) * ipm * OpenLayers.DOTS_PER_INCH).toPrecision(13) ); } }, "Dimension": function(node, obj) { var name = node.getAttribute("name").toLowerCase(); var dim = { name: name, units: node.getAttribute("units"), unitsymbol: node.getAttribute("unitSymbol") }; obj.dimensions[dim.name] = dim; }, "Extent": function(node, obj) { var name = node.getAttribute("name").toLowerCase(); if (name in obj["dimensions"]) { var extent = obj.dimensions[name]; extent.nearestVal = node.getAttribute("nearestValue") === "1"; extent.multipleVal = node.getAttribute("multipleValues") === "1"; extent.current = node.getAttribute("current") === "1"; extent["default"] = node.getAttribute("default") || ""; var values = this.getChildValue(node); extent.values = values.split(","); } } }, OpenLayers.Format.WMSCapabilities.v1.prototype.readers["wms"]) }, CLASS_NAME: "OpenLayers.Format.WMSCapabilities.v1_1" }); /* ====================================================================== OpenLayers/Format/WMSCapabilities/v1_1_1.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/WMSCapabilities/v1_1.js */ /** * Class: OpenLayers.Format.WMSCapabilities/v1_1_1 * Read WMS Capabilities version 1.1.1. * * Note on <ScaleHint> parsing: If the 'min' attribute is set to "0", no * maxScale will be set on the layer object. If the 'max' attribute is set to * "Infinity", no minScale will be set. This makes it easy to create proper * {<OpenLayers.Layer.WMS>} configurations directly from the layer object * literals returned by this format, because no minScale/maxScale modifications * need to be made. * * Inherits from: * - <OpenLayers.Format.WMSCapabilities.v1_1> */ OpenLayers.Format.WMSCapabilities.v1_1_1 = OpenLayers.Class( OpenLayers.Format.WMSCapabilities.v1_1, { /** * Property: version * {String} The specific parser version. */ version: "1.1.1", /** * Constructor: OpenLayers.Format.WMSCapabilities.v1_1_1 * Create a new parser for WMS capabilities version 1.1.1. * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ /** * 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: { "wms": OpenLayers.Util.applyDefaults({ "SRS": function(node, obj) { obj.srs[this.getChildValue(node)] = true; } }, OpenLayers.Format.WMSCapabilities.v1_1.prototype.readers["wms"]) }, CLASS_NAME: "OpenLayers.Format.WMSCapabilities.v1_1_1" }); /* ====================================================================== OpenLayers/Format/WMSCapabilities/v1_3.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/WMSCapabilities/v1.js */ /** * Class: OpenLayers.Format.WMSCapabilities/v1_3 * Abstract base class for WMS Capabilities version 1.3.X. * SLD 1.1.0 adds in the extra operations DescribeLayer and GetLegendGraphic, * see: http://schemas.opengis.net/sld/1.1.0/sld_capabilities.xsd * * Inherits from: * - <OpenLayers.Format.WMSCapabilities.v1> */ OpenLayers.Format.WMSCapabilities.v1_3 = OpenLayers.Class( OpenLayers.Format.WMSCapabilities.v1, { /** * 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: { "wms": OpenLayers.Util.applyDefaults({ "WMS_Capabilities": function(node, obj) { this.readChildNodes(node, obj); }, "LayerLimit": function(node, obj) { obj.layerLimit = parseInt(this.getChildValue(node)); }, "MaxWidth": function(node, obj) { obj.maxWidth = parseInt(this.getChildValue(node)); }, "MaxHeight": function(node, obj) { obj.maxHeight = parseInt(this.getChildValue(node)); }, "BoundingBox": function(node, obj) { var bbox = OpenLayers.Format.WMSCapabilities.v1.prototype.readers["wms"].BoundingBox.apply(this, [node, obj]); bbox.srs = node.getAttribute("CRS"); obj.bbox[bbox.srs] = bbox; }, "CRS": function(node, obj) { // CRS is the synonym of SRS this.readers.wms.SRS.apply(this, [node, obj]); }, "EX_GeographicBoundingBox": function(node, obj) { // replacement of LatLonBoundingBox obj.llbbox = []; this.readChildNodes(node, obj.llbbox); }, "westBoundLongitude": function(node, obj) { obj[0] = this.getChildValue(node); }, "eastBoundLongitude": function(node, obj) { obj[2] = this.getChildValue(node); }, "southBoundLatitude": function(node, obj) { obj[1] = this.getChildValue(node); }, "northBoundLatitude": function(node, obj) { obj[3] = this.getChildValue(node); }, "MinScaleDenominator": function(node, obj) { obj.maxScale = parseFloat(this.getChildValue(node)).toPrecision(16); }, "MaxScaleDenominator": function(node, obj) { obj.minScale = parseFloat(this.getChildValue(node)).toPrecision(16); }, "Dimension": function(node, obj) { // dimension has extra attributes: default, multipleValues, // nearestValue, current which used to be part of Extent. It now // also contains the values. var name = node.getAttribute("name").toLowerCase(); var dim = { name: name, units: node.getAttribute("units"), unitsymbol: node.getAttribute("unitSymbol"), nearestVal: node.getAttribute("nearestValue") === "1", multipleVal: node.getAttribute("multipleValues") === "1", "default": node.getAttribute("default") || "", current: node.getAttribute("current") === "1", values: this.getChildValue(node).split(",") }; // Theoretically there can be more dimensions with the same // name, but with a different unit. Until we meet such a case, // let's just keep the same structure as the WMS 1.1 // GetCapabilities parser uses. We will store the last // one encountered. obj.dimensions[dim.name] = dim; }, "Keyword": function(node, obj) { // TODO: should we change the structure of keyword in v1.js? // Make it an object with a value instead of a string? var keyword = {value: this.getChildValue(node), vocabulary: node.getAttribute("vocabulary")}; if (obj.keywords) { obj.keywords.push(keyword); } } }, OpenLayers.Format.WMSCapabilities.v1.prototype.readers["wms"]), "sld": { "UserDefinedSymbolization": function(node, obj) { this.readers.wms.UserDefinedSymbolization.apply(this, [node, obj]); // add the two extra attributes obj.userSymbols.inlineFeature = parseInt(node.getAttribute("InlineFeature")) == 1; obj.userSymbols.remoteWCS = parseInt(node.getAttribute("RemoteWCS")) == 1; }, "DescribeLayer": function(node, obj) { this.readers.wms.DescribeLayer.apply(this, [node, obj]); }, "GetLegendGraphic": function(node, obj) { this.readers.wms.GetLegendGraphic.apply(this, [node, obj]); } } }, CLASS_NAME: "OpenLayers.Format.WMSCapabilities.v1_3" }); /* ====================================================================== OpenLayers/Format/WMSCapabilities/v1_3_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/WMSCapabilities/v1_3.js */ /** * Class: OpenLayers.Format.WMSCapabilities/v1_3_0 * Read WMS Capabilities version 1.3.0. * SLD 1.1.0 adds in the extra operations DescribeLayer and GetLegendGraphic, * see: http://schemas.opengis.net/sld/1.1.0/sld_capabilities.xsd * * Inherits from: * - <OpenLayers.Format.WMSCapabilities.v1_3> */ OpenLayers.Format.WMSCapabilities.v1_3_0 = OpenLayers.Class( OpenLayers.Format.WMSCapabilities.v1_3, { /** * Property: version * {String} The specific parser version. */ version: "1.3.0", CLASS_NAME: "OpenLayers.Format.WMSCapabilities.v1_3_0" }); /* ====================================================================== OpenLayers/Format/WMSCapabilities/v1_1_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/WMSCapabilities/v1_1.js */ /** * Class: OpenLayers.Format.WMSCapabilities/v1_1_0 * Read WMS Capabilities version 1.1.0. * * Inherits from: * - <OpenLayers.Format.WMSCapabilities.v1_1> */ OpenLayers.Format.WMSCapabilities.v1_1_0 = OpenLayers.Class( OpenLayers.Format.WMSCapabilities.v1_1, { /** * Property: version * {String} The specific parser version. */ version: "1.1.0", /** * Constructor: OpenLayers.Format.WMSCapabilities.v1_1_0 * Create a new parser for WMS capabilities version 1.1.0. * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ /** * 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: { "wms": OpenLayers.Util.applyDefaults({ "SRS": function(node, obj) { var srs = this.getChildValue(node); var values = srs.split(/ +/); for (var i=0, len=values.length; i<len; i++) { obj.srs[values[i]] = true; } } }, OpenLayers.Format.WMSCapabilities.v1_1.prototype.readers["wms"]) }, CLASS_NAME: "OpenLayers.Format.WMSCapabilities.v1_1_0" }); /* ====================================================================== OpenLayers/Format/WMSCapabilities/v1_1_1_WMSC.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/WMSCapabilities/v1_1_1.js */ /** * Class: OpenLayers.Format.WMSCapabilities/v1_1_1_WMSC * Read WMS-C Capabilities version 1.1.1. * * Inherits from: * - <OpenLayers.Format.WMSCapabilities.v1_1_1> */ OpenLayers.Format.WMSCapabilities.v1_1_1_WMSC = OpenLayers.Class( OpenLayers.Format.WMSCapabilities.v1_1_1, { /** * Property: version * {String} The specific parser version. */ version: "1.1.1", /** * Property: profile * {String} The specific profile */ profile: "WMSC", /** * Constructor: OpenLayers.Format.WMSCapabilities.v1_1_1 * Create a new parser for WMS-C capabilities version 1.1.1. * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ /** * 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: { "wms": OpenLayers.Util.applyDefaults({ "VendorSpecificCapabilities": function(node, obj) { obj.vendorSpecific = {tileSets: []}; this.readChildNodes(node, obj.vendorSpecific); }, "TileSet": function(node, vendorSpecific) { var tileset = {srs: {}, bbox: {}, resolutions: []}; this.readChildNodes(node, tileset); vendorSpecific.tileSets.push(tileset); }, "Resolutions": function(node, tileset) { var res = this.getChildValue(node).split(" "); for (var i=0, len=res.length; i<len; i++) { if (res[i] != "") { tileset.resolutions.push(parseFloat(res[i])); } } }, "Width": function(node, tileset) { tileset.width = parseInt(this.getChildValue(node)); }, "Height": function(node, tileset) { tileset.height = parseInt(this.getChildValue(node)); }, "Layers": function(node, tileset) { tileset.layers = this.getChildValue(node); }, "Styles": function(node, tileset) { tileset.styles = this.getChildValue(node); } }, OpenLayers.Format.WMSCapabilities.v1_1_1.prototype.readers["wms"]) }, CLASS_NAME: "OpenLayers.Format.WMSCapabilities.v1_1_1_WMSC" }); /* ====================================================================== OpenLayers/Format/SLD/v1_0_0_GeoServer.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/SLD/v1_0_0.js */ /** * Class: OpenLayers.Format.SLD/v1_0_0_GeoServer * Read and write SLD version 1.0.0 with GeoServer-specific enhanced options. * See http://svn.osgeo.org/geotools/trunk/modules/extension/xsd/xsd-sld/src/main/resources/org/geotools/sld/bindings/StyledLayerDescriptor.xsd * for more information. * * Inherits from: * - <OpenLayers.Format.SLD.v1_0_0> */ OpenLayers.Format.SLD.v1_0_0_GeoServer = OpenLayers.Class( OpenLayers.Format.SLD.v1_0_0, { /** * Property: version * {String} The specific parser version. */ version: "1.0.0", /** * Property: profile * {String} The specific profile */ profile: "GeoServer", /** * Constructor: OpenLayers.Format.SLD.v1_0_0_GeoServer * Create a new parser for GeoServer-enhanced SLD version 1.0.0. * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ /** * 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: OpenLayers.Util.applyDefaults({ "sld": OpenLayers.Util.applyDefaults({ "Priority": function(node, obj) { var value = this.readers.ogc._expression.call(this, node); if (value) { obj.priority = value; } }, "VendorOption": function(node, obj) { if (!obj.vendorOptions) { obj.vendorOptions = {}; } obj.vendorOptions[node.getAttribute("name")] = this.getChildValue(node); }, "TextSymbolizer": function(node, rule) { OpenLayers.Format.SLD.v1_0_0.prototype.readers.sld.TextSymbolizer.apply(this, arguments); var symbolizer = this.multipleSymbolizers ? rule.symbolizers[rule.symbolizers.length-1] : rule.symbolizer["Text"]; if (symbolizer.graphic === undefined) { symbolizer.graphic = false; } } }, OpenLayers.Format.SLD.v1_0_0.prototype.readers["sld"]) }, OpenLayers.Format.SLD.v1_0_0.prototype.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: OpenLayers.Util.applyDefaults({ "sld": OpenLayers.Util.applyDefaults({ "Priority": function(priority) { return this.writers.sld._OGCExpression.call( this, "sld:Priority", priority ); }, "VendorOption": function(option) { return this.createElementNSPlus("sld:VendorOption", { attributes: {name: option.name}, value: option.value }); }, "TextSymbolizer": function(symbolizer) { var writers = OpenLayers.Format.SLD.v1_0_0.prototype.writers; var node = writers["sld"]["TextSymbolizer"].apply(this, arguments); if (symbolizer.graphic !== false && (symbolizer.externalGraphic || symbolizer.graphicName)) { this.writeNode("Graphic", symbolizer, node); } if ("priority" in symbolizer) { this.writeNode("Priority", symbolizer.priority, node); } return this.addVendorOptions(node, symbolizer); }, "PointSymbolizer": function(symbolizer) { var writers = OpenLayers.Format.SLD.v1_0_0.prototype.writers; var node = writers["sld"]["PointSymbolizer"].apply(this, arguments); return this.addVendorOptions(node, symbolizer); }, "LineSymbolizer": function(symbolizer) { var writers = OpenLayers.Format.SLD.v1_0_0.prototype.writers; var node = writers["sld"]["LineSymbolizer"].apply(this, arguments); return this.addVendorOptions(node, symbolizer); }, "PolygonSymbolizer": function(symbolizer) { var writers = OpenLayers.Format.SLD.v1_0_0.prototype.writers; var node = writers["sld"]["PolygonSymbolizer"].apply(this, arguments); return this.addVendorOptions(node, symbolizer); } }, OpenLayers.Format.SLD.v1_0_0.prototype.writers["sld"]) }, OpenLayers.Format.SLD.v1_0_0.prototype.writers), /** * Method: addVendorOptions * Add in the VendorOption tags and return the node again. * * Parameters: * node - {DOMElement} A DOM node. * symbolizer - {Object} * * Returns: * {DOMElement} A DOM node. */ addVendorOptions: function(node, symbolizer) { var options = symbolizer.vendorOptions; if (options) { for (var key in symbolizer.vendorOptions) { this.writeNode("VendorOption", { name: key, value: symbolizer.vendorOptions[key] }, node); } } return node; }, CLASS_NAME: "OpenLayers.Format.SLD.v1_0_0_GeoServer" }); /* ====================================================================== OpenLayers/Format/CSWGetRecords/v2_0_2.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/CSWGetRecords.js * @requires OpenLayers/Format/Filter/v1_0_0.js * @requires OpenLayers/Format/Filter/v1_1_0.js * @requires OpenLayers/Format/OWSCommon/v1_0_0.js */ /** * Class: OpenLayers.Format.CSWGetRecords.v2_0_2 * A format for creating CSWGetRecords v2.0.2 transactions. * Create a new instance with the * <OpenLayers.Format.CSWGetRecords.v2_0_2> constructor. * * Inherits from: * - <OpenLayers.Format.XML> */ OpenLayers.Format.CSWGetRecords.v2_0_2 = OpenLayers.Class(OpenLayers.Format.XML, { /** * Property: namespaces * {Object} Mapping of namespace aliases to namespace URIs. */ namespaces: { csw: "http://www.opengis.net/cat/csw/2.0.2", dc: "http://purl.org/dc/elements/1.1/", dct: "http://purl.org/dc/terms/", gmd: "http://www.isotc211.org/2005/gmd", geonet: "http://www.fao.org/geonetwork", ogc: "http://www.opengis.net/ogc", ows: "http://www.opengis.net/ows", xlink: "http://www.w3.org/1999/xlink", xsi: "http://www.w3.org/2001/XMLSchema-instance" }, /** * Property: defaultPrefix * {String} The default prefix (used by Format.XML). */ defaultPrefix: "csw", /** * Property: version * {String} CSW version number. */ version: "2.0.2", /** * Property: schemaLocation * {String} http://www.opengis.net/cat/csw/2.0.2 * http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd */ schemaLocation: "http://www.opengis.net/cat/csw/2.0.2 http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd", /** * APIProperty: requestId * {String} Value of the requestId attribute of the GetRecords element. */ requestId: null, /** * APIProperty: resultType * {String} Value of the resultType attribute of the GetRecords element, * specifies the result type in the GetRecords response, "hits" is * the default. */ resultType: null, /** * APIProperty: outputFormat * {String} Value of the outputFormat attribute of the GetRecords element, * specifies the format of the GetRecords response, * "application/xml" is the default. */ outputFormat: null, /** * APIProperty: outputSchema * {String} Value of the outputSchema attribute of the GetRecords element, * specifies the schema of the GetRecords response. */ outputSchema: null, /** * APIProperty: startPosition * {String} Value of the startPosition attribute of the GetRecords element, * specifies the start position (offset+1) for the GetRecords response, * 1 is the default. */ startPosition: null, /** * APIProperty: maxRecords * {String} Value of the maxRecords attribute of the GetRecords element, * specifies the maximum number of records in the GetRecords response, * 10 is the default. */ maxRecords: null, /** * APIProperty: DistributedSearch * {String} Value of the csw:DistributedSearch element, used when writing * a csw:GetRecords document. */ DistributedSearch: null, /** * APIProperty: ResponseHandler * {Array({String})} Values of the csw:ResponseHandler elements, used when * writting a csw:GetRecords document. */ ResponseHandler: null, /** * APIProperty: Query * {String} Value of the csw:Query element, used when writing a csw:GetRecords * document. */ Query: null, /** * Property: regExes * Compiled regular expressions for manipulating strings. */ regExes: { trimSpace: (/^\s*|\s*$/g), removeSpace: (/\s*/g), splitSpace: (/\s+/), trimComma: (/\s*,\s*/g) }, /** * Constructor: OpenLayers.Format.CSWGetRecords.v2_0_2 * A class for parsing and generating CSWGetRecords v2.0.2 transactions. * * Parameters: * options - {Object} Optional object whose properties will be set on the * instance. * * Valid options properties (documented as class properties): * - requestId * - resultType * - outputFormat * - outputSchema * - startPosition * - maxRecords * - DistributedSearch * - ResponseHandler * - Query */ initialize: function(options) { OpenLayers.Format.XML.prototype.initialize.apply(this, [options]); }, /** * APIMethod: read * Parse the response from a GetRecords request. */ 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 obj = {}; this.readNode(data, obj); 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: { "csw": { "GetRecordsResponse": function(node, obj) { obj.records = []; this.readChildNodes(node, obj); var version = this.getAttributeNS(node, "", 'version'); if (version != "") { obj.version = version; } }, "RequestId": function(node, obj) { obj.RequestId = this.getChildValue(node); }, "SearchStatus": function(node, obj) { obj.SearchStatus = {}; var timestamp = this.getAttributeNS(node, "", 'timestamp'); if (timestamp != "") { obj.SearchStatus.timestamp = timestamp; } }, "SearchResults": function(node, obj) { this.readChildNodes(node, obj); var attrs = node.attributes; var SearchResults = {}; for(var i=0, len=attrs.length; i<len; ++i) { if ((attrs[i].name == "numberOfRecordsMatched") || (attrs[i].name == "numberOfRecordsReturned") || (attrs[i].name == "nextRecord")) { SearchResults[attrs[i].name] = parseInt(attrs[i].nodeValue); } else { SearchResults[attrs[i].name] = attrs[i].nodeValue; } } obj.SearchResults = SearchResults; }, "SummaryRecord": function(node, obj) { var record = {type: "SummaryRecord"}; this.readChildNodes(node, record); obj.records.push(record); }, "BriefRecord": function(node, obj) { var record = {type: "BriefRecord"}; this.readChildNodes(node, record); obj.records.push(record); }, "DCMIRecord": function(node, obj) { var record = {type: "DCMIRecord"}; this.readChildNodes(node, record); obj.records.push(record); }, "Record": function(node, obj) { var record = {type: "Record"}; this.readChildNodes(node, record); obj.records.push(record); }, "*": function(node, obj) { var name = node.localName || node.nodeName.split(":").pop(); obj[name] = this.getChildValue(node); } }, "geonet": { "info": function(node, obj) { var gninfo = {}; this.readChildNodes(node, gninfo); obj.gninfo = gninfo; } }, "dc": { // audience, contributor, coverage, creator, date, description, format, // identifier, language, provenance, publisher, relation, rights, // rightsHolder, source, subject, title, type, URI "*": function(node, obj) { var name = node.localName || node.nodeName.split(":").pop(); if (!(OpenLayers.Util.isArray(obj[name]))) { obj[name] = []; } var dc_element = {}; var attrs = node.attributes; for(var i=0, len=attrs.length; i<len; ++i) { dc_element[attrs[i].name] = attrs[i].nodeValue; } dc_element.value = this.getChildValue(node); if (dc_element.value != "") { obj[name].push(dc_element); } } }, "dct": { // abstract, modified, spatial "*": function(node, obj) { var name = node.localName || node.nodeName.split(":").pop(); if (!(OpenLayers.Util.isArray(obj[name]))) { obj[name] = []; } obj[name].push(this.getChildValue(node)); } }, "ows": OpenLayers.Util.applyDefaults({ "BoundingBox": function(node, obj) { if (obj.bounds) { obj.BoundingBox = [{crs: obj.projection, value: [ obj.bounds.left, obj.bounds.bottom, obj.bounds.right, obj.bounds.top ] }]; delete obj.projection; delete obj.bounds; } OpenLayers.Format.OWSCommon.v1_0_0.prototype.readers["ows"]["BoundingBox"].apply( this, arguments); } }, OpenLayers.Format.OWSCommon.v1_0_0.prototype.readers["ows"]) }, /** * Method: write * Given an configuration js object, write a CSWGetRecords request. * * Parameters: * options - {Object} A object mapping the request. * * Returns: * {String} A serialized CSWGetRecords request. */ write: function(options) { var node = this.writeNode("csw:GetRecords", options); node.setAttribute("xmlns:gmd", this.namespaces.gmd); 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: { "csw": { "GetRecords": function(options) { if (!options) { options = {}; } var node = this.createElementNSPlus("csw:GetRecords", { attributes: { service: "CSW", version: this.version, requestId: options.requestId || this.requestId, resultType: options.resultType || this.resultType, outputFormat: options.outputFormat || this.outputFormat, outputSchema: options.outputSchema || this.outputSchema, startPosition: options.startPosition || this.startPosition, maxRecords: options.maxRecords || this.maxRecords } }); if (options.DistributedSearch || this.DistributedSearch) { this.writeNode( "csw:DistributedSearch", options.DistributedSearch || this.DistributedSearch, node ); } var ResponseHandler = options.ResponseHandler || this.ResponseHandler; if (OpenLayers.Util.isArray(ResponseHandler) && ResponseHandler.length > 0) { // ResponseHandler must be a non-empty array for(var i=0, len=ResponseHandler.length; i<len; i++) { this.writeNode( "csw:ResponseHandler", ResponseHandler[i], node ); } } this.writeNode("Query", options.Query || this.Query, node); return node; }, "DistributedSearch": function(options) { var node = this.createElementNSPlus("csw:DistributedSearch", { attributes: { hopCount: options.hopCount } }); return node; }, "ResponseHandler": function(options) { var node = this.createElementNSPlus("csw:ResponseHandler", { value: options.value }); return node; }, "Query": function(options) { if (!options) { options = {}; } var node = this.createElementNSPlus("csw:Query", { attributes: { typeNames: options.typeNames || "csw:Record" } }); var ElementName = options.ElementName; if (OpenLayers.Util.isArray(ElementName) && ElementName.length > 0) { // ElementName must be a non-empty array for(var i=0, len=ElementName.length; i<len; i++) { this.writeNode( "csw:ElementName", ElementName[i], node ); } } else { this.writeNode( "csw:ElementSetName", options.ElementSetName || {value: 'summary'}, node ); } if (options.Constraint) { this.writeNode( "csw:Constraint", options.Constraint, node ); } if (options.SortBy) { this.writeNode( "ogc:SortBy", options.SortBy, node ); } return node; }, "ElementName": function(options) { var node = this.createElementNSPlus("csw:ElementName", { value: options.value }); return node; }, "ElementSetName": function(options) { var node = this.createElementNSPlus("csw:ElementSetName", { attributes: { typeNames: options.typeNames }, value: options.value }); return node; }, "Constraint": function(options) { var node = this.createElementNSPlus("csw:Constraint", { attributes: { version: options.version } }); if (options.Filter) { var format = new OpenLayers.Format.Filter({ version: options.version }); node.appendChild(format.write(options.Filter)); } else if (options.CqlText) { var child = this.createElementNSPlus("CqlText", { value: options.CqlText.value }); node.appendChild(child); } return node; } }, "ogc": OpenLayers.Format.Filter.v1_1_0.prototype.writers["ogc"] }, CLASS_NAME: "OpenLayers.Format.CSWGetRecords.v2_0_2" }); /* ====================================================================== OpenLayers/Format/ArcXML/Features.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/ArcXML.js */ /** * Class: OpenLayers.Format.ArcXML.Features * Read/Write ArcXML features. Create a new instance with the * <OpenLayers.Format.ArcXML.Features> constructor. * * Inherits from: * - <OpenLayers.Format.XML> */ OpenLayers.Format.ArcXML.Features = OpenLayers.Class(OpenLayers.Format.XML, { /** * Constructor: OpenLayers.Format.ArcXML.Features * Create a new parser/writer for ArcXML Features. Create an instance of this class * to get a set of features from an ArcXML response. * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ /** * APIMethod: read * Read data from a string of ArcXML, and return a set of OpenLayers features. * * Parameters: * data - {String} or {DOMElement} data to read/parse. * * Returns: * {Array(<OpenLayers.Feature.Vector>)} A collection of features. */ read: function(data) { var axl = new OpenLayers.Format.ArcXML(); var parsed = axl.read(data); return parsed.features.feature; } }); /* ====================================================================== OpenLayers/Format/XLS/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/XLS.js * @requires OpenLayers/Format/GML/v3.js */ /** * Class: OpenLayers.Format.XLS.v1 * Superclass for XLS version 1 parsers. Only supports GeocodeRequest for now. * * Inherits from: * - <OpenLayers.Format.XML> */ OpenLayers.Format.XLS.v1 = OpenLayers.Class(OpenLayers.Format.XML, { /** * Property: namespaces * {Object} Mapping of namespace aliases to namespace URIs. */ namespaces: { xls: "http://www.opengis.net/xls", gml: "http://www.opengis.net/gml", xsi: "http://www.w3.org/2001/XMLSchema-instance" }, /** * Property: regExes * Compiled regular expressions for manipulating strings. */ regExes: { trimSpace: (/^\s*|\s*$/g), removeSpace: (/\s*/g), splitSpace: (/\s+/), trimComma: (/\s*,\s*/g) }, /** * 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: defaultPrefix */ defaultPrefix: "xls", /** * Property: schemaLocation * {String} Schema location for a particular minor version. */ schemaLocation: null, /** * Constructor: OpenLayers.Format.XLS.v1 * Instances of this class are not created directly. Use the * <OpenLayers.Format.XLS> constructor instead. * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ /** * Method: read * * Parameters: * data - {DOMElement} An XLS document element. * options - {Object} Options for the reader. * * Returns: * {Object} An object representing the XLSResponse. */ read: function(data, options) { options = OpenLayers.Util.applyDefaults(options, this.options); var xls = {}; this.readChildNodes(data, xls); return xls; }, /** * 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: { "xls": { "XLS": function(node, xls) { xls.version = node.getAttribute("version"); this.readChildNodes(node, xls); }, "Response": function(node, xls) { this.readChildNodes(node, xls); }, "GeocodeResponse": function(node, xls) { xls.responseLists = []; this.readChildNodes(node, xls); }, "GeocodeResponseList": function(node, xls) { var responseList = { features: [], numberOfGeocodedAddresses: parseInt(node.getAttribute("numberOfGeocodedAddresses")) }; xls.responseLists.push(responseList); this.readChildNodes(node, responseList); }, "GeocodedAddress": function(node, responseList) { var feature = new OpenLayers.Feature.Vector(); responseList.features.push(feature); this.readChildNodes(node, feature); // post-process geometry feature.geometry = feature.components[0]; }, "GeocodeMatchCode": function(node, feature) { feature.attributes.matchCode = { accuracy: parseFloat(node.getAttribute("accuracy")), matchType: node.getAttribute("matchType") }; }, "Address": function(node, feature) { var address = { countryCode: node.getAttribute("countryCode"), addressee: node.getAttribute("addressee"), street: [], place: [] }; feature.attributes.address = address; this.readChildNodes(node, address); }, "freeFormAddress": function(node, address) { address.freeFormAddress = this.getChildValue(node); }, "StreetAddress": function(node, address) { this.readChildNodes(node, address); }, "Building": function(node, address) { address.building = { 'number': node.getAttribute("number"), subdivision: node.getAttribute("subdivision"), buildingName: node.getAttribute("buildingName") }; }, "Street": function(node, address) { // only support the built-in primitive type for now address.street.push(this.getChildValue(node)); }, "Place": function(node, address) { // type is one of CountrySubdivision, // CountrySecondarySubdivision, Municipality or // MunicipalitySubdivision address.place[node.getAttribute("type")] = this.getChildValue(node); }, "PostalCode": function(node, address) { address.postalCode = this.getChildValue(node); } }, "gml": OpenLayers.Format.GML.v3.prototype.readers.gml }, /** * Method: write * * Parameters: * request - {Object} An object representing the geocode request. * * Returns: * {DOMElement} The root of an XLS document. */ write: function(request) { return this.writers.xls.XLS.apply(this, [request]); }, /** * 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: { "xls": { "XLS": function(request) { var root = this.createElementNSPlus( "xls:XLS", {attributes: { "version": this.VERSION, "xsi:schemaLocation": this.schemaLocation }} ); this.writeNode("RequestHeader", request.header, root); this.writeNode("Request", request, root); return root; }, "RequestHeader": function(header) { return this.createElementNSPlus("xls:RequestHeader"); }, "Request": function(request) { var node = this.createElementNSPlus("xls:Request", { attributes: { methodName: "GeocodeRequest", requestID: request.requestID || "", version: this.VERSION } }); this.writeNode("GeocodeRequest", request.addresses, node); return node; }, "GeocodeRequest": function(addresses) { var node = this.createElementNSPlus("xls:GeocodeRequest"); for (var i=0, len=addresses.length; i<len; i++) { this.writeNode("Address", addresses[i], node); } return node; }, "Address": function(address) { var node = this.createElementNSPlus("xls:Address", { attributes: { countryCode: address.countryCode } }); if (address.freeFormAddress) { this.writeNode("freeFormAddress", address.freeFormAddress, node); } else { if (address.street) { this.writeNode("StreetAddress", address, node); } if (address.municipality) { this.writeNode("Municipality", address.municipality, node); } if (address.countrySubdivision) { this.writeNode("CountrySubdivision", address.countrySubdivision, node); } if (address.postalCode) { this.writeNode("PostalCode", address.postalCode, node); } } return node; }, "freeFormAddress": function(freeFormAddress) { return this.createElementNSPlus("freeFormAddress", {value: freeFormAddress}); }, "StreetAddress": function(address) { var node = this.createElementNSPlus("xls:StreetAddress"); if (address.building) { this.writeNode(node, "Building", address.building); } var street = address.street; if (!(OpenLayers.Util.isArray(street))) { street = [street]; } for (var i=0, len=street.length; i < len; i++) { this.writeNode("Street", street[i], node); } return node; }, "Building": function(building) { return this.createElementNSPlus("xls:Building", { attributes: { "number": building["number"], "subdivision": building.subdivision, "buildingName": building.buildingName } }); }, "Street": function(street) { return this.createElementNSPlus("xls:Street", {value: street}); }, "Municipality": function(municipality) { return this.createElementNSPlus("xls:Place", { attributes: { type: "Municipality" }, value: municipality }); }, "CountrySubdivision": function(countrySubdivision) { return this.createElementNSPlus("xls:Place", { attributes: { type: "CountrySubdivision" }, value: countrySubdivision }); }, "PostalCode": function(postalCode) { return this.createElementNSPlus("xls:PostalCode", { value: postalCode }); } } }, CLASS_NAME: "OpenLayers.Format.XLS.v1" }); /* ====================================================================== OpenLayers/Format/XLS/v1_1_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/XLS/v1.js */ /** * Class: OpenLayers.Format.XLS.v1_1_0 * Read / write XLS version 1.1.0. * * Inherits from: * - <OpenLayers.Format.XLS.v1> */ OpenLayers.Format.XLS.v1_1_0 = OpenLayers.Class( OpenLayers.Format.XLS.v1, { /** * Constant: VERSION * {String} 1.1 */ VERSION: "1.1", /** * Property: schemaLocation * {String} http://www.opengis.net/xls * http://schemas.opengis.net/ols/1.1.0/LocationUtilityService.xsd */ schemaLocation: "http://www.opengis.net/xls http://schemas.opengis.net/ols/1.1.0/LocationUtilityService.xsd", /** * Constructor: OpenLayers.Format.XLS.v1_1_0 * Instances of this class are not created directly. Use the * <OpenLayers.Format.XLS> constructor instead. * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ CLASS_NAME: "OpenLayers.Format.XLS.v1_1_0" }); // Support non standard implementation OpenLayers.Format.XLS.v1_1 = OpenLayers.Format.XLS.v1_1_0; /* ====================================================================== OpenLayers/Format/WPSCapabilities/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/WPSCapabilities.js * @requires OpenLayers/Format/OWSCommon/v1_1_0.js */ /** * Class: OpenLayers.Format.WPSCapabilities.v1_0_0 * Read WPS Capabilities version 1.0.0. * * Inherits from: * - <OpenLayers.Format.XML> */ OpenLayers.Format.WPSCapabilities.v1_0_0 = OpenLayers.Class( OpenLayers.Format.XML, { /** * Property: namespaces * {Object} Mapping of namespace aliases to namespace URIs. */ namespaces: { ows: "http://www.opengis.net/ows/1.1", wps: "http://www.opengis.net/wps/1.0.0", xlink: "http://www.w3.org/1999/xlink" }, /** * Property: regExes * Compiled regular expressions for manipulating strings. */ regExes: { trimSpace: (/^\s*|\s*$/g), removeSpace: (/\s*/g), splitSpace: (/\s+/), trimComma: (/\s*,\s*/g) }, /** * Constructor: OpenLayers.Format.WPSCapabilities.v1_0_0 * Create a new parser for WPS capabilities version 1.0.0. * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ initialize: function(options) { OpenLayers.Format.XML.prototype.initialize.apply(this, [options]); }, /** * APIMethod: read * Read capabilities data from a string, and return info about the WPS. * * Parameters: * data - {String} or {DOMElement} data to read/parse. * * Returns: * {Object} Information about the WPS service. */ 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 capabilities = {}; this.readNode(data, capabilities); return capabilities; }, /** * 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: { "wps": { "Capabilities": function(node, obj) { this.readChildNodes(node, obj); }, "ProcessOfferings": function(node, obj) { obj.processOfferings = {}; this.readChildNodes(node, obj.processOfferings); }, "Process": function(node, processOfferings) { var processVersion = this.getAttributeNS(node, this.namespaces.wps, "processVersion"); var process = {processVersion: processVersion}; this.readChildNodes(node, process); processOfferings[process.identifier] = process; }, "Languages": function(node, obj) { obj.languages = []; this.readChildNodes(node, obj.languages); }, "Default": function(node, languages) { var language = {isDefault: true}; this.readChildNodes(node, language); languages.push(language); }, "Supported": function(node, languages) { var language = {}; this.readChildNodes(node, language); languages.push(language); } }, "ows": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers["ows"] }, CLASS_NAME: "OpenLayers.Format.WPSCapabilities.v1_0_0" }); /* ====================================================================== OpenLayers/Format/CSWGetDomain/v2_0_2.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/CSWGetDomain.js */ /** * Class: OpenLayers.Format.CSWGetDomain.v2_0_2 * A format for creating CSWGetDomain v2.0.2 transactions. * Create a new instance with the * <OpenLayers.Format.CSWGetDomain.v2_0_2> constructor. * * Inherits from: * - <OpenLayers.Format.XML> */ OpenLayers.Format.CSWGetDomain.v2_0_2 = 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", csw: "http://www.opengis.net/cat/csw/2.0.2" }, /** * Property: defaultPrefix * {String} The default prefix (used by Format.XML). */ defaultPrefix: "csw", /** * Property: version * {String} CSW version number. */ version: "2.0.2", /** * Property: schemaLocation * {String} http://www.opengis.net/cat/csw/2.0.2 * http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd */ schemaLocation: "http://www.opengis.net/cat/csw/2.0.2 http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd", /** * APIProperty: PropertyName * {String} Value of the csw:PropertyName element, used when * writing a GetDomain document. */ PropertyName: null, /** * APIProperty: ParameterName * {String} Value of the csw:ParameterName element, used when * writing a GetDomain document. */ ParameterName: null, /** * Constructor: OpenLayers.Format.CSWGetDomain.v2_0_2 * A class for parsing and generating CSWGetDomain v2.0.2 transactions. * * Parameters: * options - {Object} Optional object whose properties will be set on the * instance. * * Valid options properties: * - PropertyName * - ParameterName */ /** * APIMethod: read * Parse the response from a GetDomain request. */ 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 obj = {}; this.readNode(data, obj); 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: { "csw": { "GetDomainResponse": function(node, obj) { this.readChildNodes(node, obj); }, "DomainValues": function(node, obj) { if (!(OpenLayers.Util.isArray(obj.DomainValues))) { obj.DomainValues = []; } var attrs = node.attributes; var domainValue = {}; for(var i=0, len=attrs.length; i<len; ++i) { domainValue[attrs[i].name] = attrs[i].nodeValue; } this.readChildNodes(node, domainValue); obj.DomainValues.push(domainValue); }, "PropertyName": function(node, obj) { obj.PropertyName = this.getChildValue(node); }, "ParameterName": function(node, obj) { obj.ParameterName = this.getChildValue(node); }, "ListOfValues": function(node, obj) { if (!(OpenLayers.Util.isArray(obj.ListOfValues))) { obj.ListOfValues = []; } this.readChildNodes(node, obj.ListOfValues); }, "Value": function(node, obj) { var attrs = node.attributes; var value = {}; for(var i=0, len=attrs.length; i<len; ++i) { value[attrs[i].name] = attrs[i].nodeValue; } value.value = this.getChildValue(node); obj.push({Value: value}); }, "ConceptualScheme": function(node, obj) { obj.ConceptualScheme = {}; this.readChildNodes(node, obj.ConceptualScheme); }, "Name": function(node, obj) { obj.Name = this.getChildValue(node); }, "Document": function(node, obj) { obj.Document = this.getChildValue(node); }, "Authority": function(node, obj) { obj.Authority = this.getChildValue(node); }, "RangeOfValues": function(node, obj) { obj.RangeOfValues = {}; this.readChildNodes(node, obj.RangeOfValues); }, "MinValue": function(node, obj) { var attrs = node.attributes; var value = {}; for(var i=0, len=attrs.length; i<len; ++i) { value[attrs[i].name] = attrs[i].nodeValue; } value.value = this.getChildValue(node); obj.MinValue = value; }, "MaxValue": function(node, obj) { var attrs = node.attributes; var value = {}; for(var i=0, len=attrs.length; i<len; ++i) { value[attrs[i].name] = attrs[i].nodeValue; } value.value = this.getChildValue(node); obj.MaxValue = value; } } }, /** * APIMethod: write * Given an configuration js object, write a CSWGetDomain request. * * Parameters: * options - {Object} A object mapping the request. * * Returns: * {String} A serialized CSWGetDomain request. */ write: function(options) { var node = this.writeNode("csw:GetDomain", options); 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: { "csw": { "GetDomain": function(options) { var node = this.createElementNSPlus("csw:GetDomain", { attributes: { service: "CSW", version: this.version } }); if (options.PropertyName || this.PropertyName) { this.writeNode( "csw:PropertyName", options.PropertyName || this.PropertyName, node ); } else if (options.ParameterName || this.ParameterName) { this.writeNode( "csw:ParameterName", options.ParameterName || this.ParameterName, node ); } this.readChildNodes(node, options); return node; }, "PropertyName": function(value) { var node = this.createElementNSPlus("csw:PropertyName", { value: value }); return node; }, "ParameterName": function(value) { var node = this.createElementNSPlus("csw:ParameterName", { value: value }); return node; } } }, CLASS_NAME: "OpenLayers.Format.CSWGetDomain.v2_0_2" }); /* ====================================================================== OpenLayers/Format/WFSCapabilities/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/WFSCapabilities.js */ /** * Class: OpenLayers.Format.WFSCapabilities.v1 * Abstract class not to be instantiated directly. * * Inherits from: * - <OpenLayers.Format.XML> */ OpenLayers.Format.WFSCapabilities.v1 = OpenLayers.Class( OpenLayers.Format.XML, { /** * Property: namespaces * {Object} Mapping of namespace aliases to namespace URIs. */ namespaces: { wfs: "http://www.opengis.net/wfs", xlink: "http://www.w3.org/1999/xlink", xsi: "http://www.w3.org/2001/XMLSchema-instance", ows: "http://www.opengis.net/ows" }, /** * APIProperty: errorProperty * {String} Which property of the returned object to check for in order to * determine whether or not parsing has failed. In the case that the * errorProperty is undefined on the returned object, the document will be * run through an OGCExceptionReport parser. */ errorProperty: "featureTypeList", /** * Property: defaultPrefix */ defaultPrefix: "wfs", /** * Constructor: OpenLayers.Format.WFSCapabilities.v1_1 * Create an instance of one of the subclasses. * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ /** * APIMethod: read * Read capabilities data from a string, and return a list of layers. * * Parameters: * data - {String} or {DOMElement} data to read/parse. * * Returns: * {Array} List of named layers. */ read: function(data) { if(typeof data == "string") { data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); } var raw = data; if(data && data.nodeType == 9) { data = data.documentElement; } var capabilities = {}; this.readNode(data, capabilities); return capabilities; }, /** * 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": { "WFS_Capabilities": function(node, obj) { this.readChildNodes(node, obj); }, "FeatureTypeList": function(node, request) { request.featureTypeList = { featureTypes: [] }; this.readChildNodes(node, request.featureTypeList); }, "FeatureType": function(node, featureTypeList) { var featureType = {}; this.readChildNodes(node, featureType); featureTypeList.featureTypes.push(featureType); }, "Name": function(node, obj) { var name = this.getChildValue(node); if(name) { var parts = name.split(":"); obj.name = parts.pop(); if(parts.length > 0) { obj.featureNS = this.lookupNamespaceURI(node, parts[0]); } } }, "Title": function(node, obj) { var title = this.getChildValue(node); if(title) { obj.title = title; } }, "Abstract": function(node, obj) { var abst = this.getChildValue(node); if(abst) { obj["abstract"] = abst; } } } }, CLASS_NAME: "OpenLayers.Format.WFSCapabilities.v1" }); /* ====================================================================== OpenLayers/Format/WFSCapabilities/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/WFSCapabilities/v1.js */ /** * Class: OpenLayers.Format.WFSCapabilities/v1_0_0 * Read WFS Capabilities version 1.0.0. * * Inherits from: * - <OpenLayers.Format.WFSCapabilities.v1> */ OpenLayers.Format.WFSCapabilities.v1_0_0 = OpenLayers.Class( OpenLayers.Format.WFSCapabilities.v1, { /** * Constructor: OpenLayers.Format.WFSCapabilities.v1_0_0 * Create a new parser for WFS capabilities version 1.0.0. * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ /** * 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({ "Service": function(node, capabilities) { capabilities.service = {}; this.readChildNodes(node, capabilities.service); }, "Fees": function(node, service) { var fees = this.getChildValue(node); if (fees && fees.toLowerCase() != "none") { service.fees = fees; } }, "AccessConstraints": function(node, service) { var constraints = this.getChildValue(node); if (constraints && constraints.toLowerCase() != "none") { service.accessConstraints = constraints; } }, "OnlineResource": function(node, service) { var onlineResource = this.getChildValue(node); if (onlineResource && onlineResource.toLowerCase() != "none") { service.onlineResource = onlineResource; } }, "Keywords": function(node, service) { var keywords = this.getChildValue(node); if (keywords && keywords.toLowerCase() != "none") { service.keywords = keywords.split(', '); } }, "Capability": function(node, capabilities) { capabilities.capability = {}; this.readChildNodes(node, capabilities.capability); }, "Request": function(node, obj) { obj.request = {}; this.readChildNodes(node, obj.request); }, "GetFeature": function(node, request) { request.getfeature = { href: {}, // DCPType formats: [] // ResultFormat }; this.readChildNodes(node, request.getfeature); }, "ResultFormat": function(node, obj) { var children = node.childNodes; var childNode; for(var i=0; i<children.length; i++) { childNode = children[i]; if(childNode.nodeType == 1) { obj.formats.push(childNode.nodeName); } } }, "DCPType": function(node, obj) { this.readChildNodes(node, obj); }, "HTTP": function(node, obj) { this.readChildNodes(node, obj.href); }, "Get": function(node, obj) { obj.get = node.getAttribute("onlineResource"); }, "Post": function(node, obj) { obj.post = node.getAttribute("onlineResource"); }, "SRS": function(node, obj) { var srs = this.getChildValue(node); if (srs) { obj.srs = srs; } } }, OpenLayers.Format.WFSCapabilities.v1.prototype.readers["wfs"]) }, CLASS_NAME: "OpenLayers.Format.WFSCapabilities.v1_0_0" }); /* ====================================================================== OpenLayers/Format/WFSCapabilities/v1_1_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/WFSCapabilities/v1.js * @requires OpenLayers/Format/OWSCommon/v1.js */ /** * Class: OpenLayers.Format.WFSCapabilities/v1_1_0 * Read WFS Capabilities version 1.1.0. * * Inherits from: * - <OpenLayers.Format.WFSCapabilities> */ OpenLayers.Format.WFSCapabilities.v1_1_0 = OpenLayers.Class( OpenLayers.Format.WFSCapabilities.v1, { /** * Property: regExes * Compiled regular expressions for manipulating strings. */ regExes: { trimSpace: (/^\s*|\s*$/g), removeSpace: (/\s*/g), splitSpace: (/\s+/), trimComma: (/\s*,\s*/g) }, /** * Constructor: OpenLayers.Format.WFSCapabilities.v1_1_0 * Create a new parser for WFS capabilities version 1.1.0. * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ /** * 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({ "DefaultSRS": function(node, obj) { var defaultSRS = this.getChildValue(node); if (defaultSRS) { obj.srs = defaultSRS; } } }, OpenLayers.Format.WFSCapabilities.v1.prototype.readers["wfs"]), "ows": OpenLayers.Format.OWSCommon.v1.prototype.readers.ows }, CLASS_NAME: "OpenLayers.Format.WFSCapabilities.v1_1_0" }); /* ====================================================================== OpenLayers/Format/WMTSCapabilities/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/WMTSCapabilities.js * @requires OpenLayers/Format/OWSCommon/v1_1_0.js */ /** * Class: OpenLayers.Format.WMTSCapabilities.v1_0_0 * Read WMTS Capabilities version 1.0.0. * * Inherits from: * - <OpenLayers.Format.WMTSCapabilities> */ OpenLayers.Format.WMTSCapabilities.v1_0_0 = OpenLayers.Class( OpenLayers.Format.OWSCommon.v1_1_0, { /** * Property: version * {String} The parser version ("1.0.0"). */ version: "1.0.0", /** * Property: namespaces * {Object} Mapping of namespace aliases to namespace URIs. */ namespaces: { ows: "http://www.opengis.net/ows/1.1", wmts: "http://www.opengis.net/wmts/1.0", xlink: "http://www.w3.org/1999/xlink" }, /** * Property: yx * {Object} Members in the yx object are used to determine if a CRS URN * corresponds to a CRS with y,x axis order. Member names are CRS URNs * and values are boolean. Defaults come from the * <OpenLayers.Format.WMTSCapabilities> prototype. */ yx: null, /** * Property: defaultPrefix * {String} The default namespace alias for creating element nodes. */ defaultPrefix: "wmts", /** * Constructor: OpenLayers.Format.WMTSCapabilities.v1_0_0 * Create a new parser for WMTS capabilities version 1.0.0. * * 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]); this.options = options; var yx = OpenLayers.Util.extend( {}, OpenLayers.Format.WMTSCapabilities.prototype.yx ); this.yx = OpenLayers.Util.extend(yx, this.yx); }, /** * APIMethod: read * Read capabilities data from a string, and return info about the WMTS. * * Parameters: * data - {String} or {DOMElement} data to read/parse. * * Returns: * {Object} Information about the SOS service. */ 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 capabilities = {}; this.readNode(data, capabilities); capabilities.version = this.version; return capabilities; }, /** * 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: { "wmts": { "Capabilities": function(node, obj) { this.readChildNodes(node, obj); }, "Contents": function(node, obj) { obj.contents = {}; obj.contents.layers = []; obj.contents.tileMatrixSets = {}; this.readChildNodes(node, obj.contents); }, "Layer": function(node, obj) { var layer = { styles: [], formats: [], dimensions: [], tileMatrixSetLinks: [] }; layer.layers = []; this.readChildNodes(node, layer); obj.layers.push(layer); }, "Style": function(node, obj) { var style = {}; style.isDefault = (node.getAttribute("isDefault") === "true"); this.readChildNodes(node, style); obj.styles.push(style); }, "Format": function(node, obj) { obj.formats.push(this.getChildValue(node)); }, "TileMatrixSetLink": function(node, obj) { var tileMatrixSetLink = {}; this.readChildNodes(node, tileMatrixSetLink); obj.tileMatrixSetLinks.push(tileMatrixSetLink); }, "TileMatrixSet": function(node, obj) { // node could be child of wmts:Contents or wmts:TileMatrixSetLink // duck type wmts:Contents by looking for layers if (obj.layers) { // TileMatrixSet as object type in schema var tileMatrixSet = { matrixIds: [] }; this.readChildNodes(node, tileMatrixSet); obj.tileMatrixSets[tileMatrixSet.identifier] = tileMatrixSet; } else { // TileMatrixSet as string type in schema obj.tileMatrixSet = this.getChildValue(node); } }, "TileMatrix": function(node, obj) { var tileMatrix = { supportedCRS: obj.supportedCRS }; this.readChildNodes(node, tileMatrix); obj.matrixIds.push(tileMatrix); }, "ScaleDenominator": function(node, obj) { obj.scaleDenominator = parseFloat(this.getChildValue(node)); }, "TopLeftCorner": function(node, obj) { var topLeftCorner = this.getChildValue(node); var coords = topLeftCorner.split(" "); // decide on axis order for the given CRS var yx; if (obj.supportedCRS) { // extract out version from URN var crs = obj.supportedCRS.replace( /urn:ogc:def:crs:(\w+):.+:(\w+)$/, "urn:ogc:def:crs:$1::$2" ); yx = !!this.yx[crs]; } if (yx) { obj.topLeftCorner = new OpenLayers.LonLat( coords[1], coords[0] ); } else { obj.topLeftCorner = new OpenLayers.LonLat( coords[0], coords[1] ); } }, "TileWidth": function(node, obj) { obj.tileWidth = parseInt(this.getChildValue(node)); }, "TileHeight": function(node, obj) { obj.tileHeight = parseInt(this.getChildValue(node)); }, "MatrixWidth": function(node, obj) { obj.matrixWidth = parseInt(this.getChildValue(node)); }, "MatrixHeight": function(node, obj) { obj.matrixHeight = parseInt(this.getChildValue(node)); }, "ResourceURL": function(node, obj) { obj.resourceUrl = obj.resourceUrl || {}; var resourceType = node.getAttribute("resourceType"); if (!obj.resourceUrls) { obj.resourceUrls = []; } var resourceUrl = obj.resourceUrl[resourceType] = { format: node.getAttribute("format"), template: node.getAttribute("template"), resourceType: resourceType }; obj.resourceUrls.push(resourceUrl); }, // not used for now, can be added in the future though /*"Themes": function(node, obj) { obj.themes = []; this.readChildNodes(node, obj.themes); }, "Theme": function(node, obj) { var theme = {}; this.readChildNodes(node, theme); obj.push(theme); },*/ "WSDL": function(node, obj) { obj.wsdl = {}; obj.wsdl.href = node.getAttribute("xlink:href"); // TODO: other attributes of <WSDL> element }, "ServiceMetadataURL": function(node, obj) { obj.serviceMetadataUrl = {}; obj.serviceMetadataUrl.href = node.getAttribute("xlink:href"); // TODO: other attributes of <ServiceMetadataURL> element }, "LegendURL": function(node, obj) { obj.legend = {}; obj.legend.href = node.getAttribute("xlink:href"); obj.legend.format = node.getAttribute("format"); }, "Dimension": function(node, obj) { var dimension = {values: []}; this.readChildNodes(node, dimension); obj.dimensions.push(dimension); }, "Default": function(node, obj) { obj["default"] = this.getChildValue(node); }, "Value": function(node, obj) { obj.values.push(this.getChildValue(node)); } }, "ows": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers["ows"] }, CLASS_NAME: "OpenLayers.Format.WMTSCapabilities.v1_0_0" }); /* ====================================================================== OpenLayers/Format/WCSCapabilities/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/WCSCapabilities.js */ /** * Class: OpenLayers.Format.WCSCapabilities.v1 * Abstract class not to be instantiated directly. * * Inherits from: * - <OpenLayers.Format.XML> */ OpenLayers.Format.WCSCapabilities.v1 = OpenLayers.Class( OpenLayers.Format.XML, { regExes: { trimSpace: (/^\s*|\s*$/g), splitSpace: (/\s+/) }, /** * Property: defaultPrefix */ defaultPrefix: "wcs", /** * APIMethod: read * Read capabilities data from a string, and return a list of coverages. * * Parameters: * data - {String} or {DOMElement} data to read/parse. * * Returns: * {Array} List of named coverages. */ read: function(data) { if(typeof data == "string") { data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); } var raw = data; if(data && data.nodeType == 9) { data = data.documentElement; } var capabilities = {}; this.readNode(data, capabilities); return capabilities; }, CLASS_NAME: "OpenLayers.Format.WCSCapabilities.v1" }); /* ====================================================================== OpenLayers/Format/WCSCapabilities/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/WCSCapabilities/v1.js * @requires OpenLayers/Format/GML/v3.js */ /** * Class: OpenLayers.Format.WCSCapabilities/v1_0_0 * Read WCS Capabilities version 1.0.0. * * Inherits from: * - <OpenLayers.Format.WCSCapabilities.v1> */ OpenLayers.Format.WCSCapabilities.v1_0_0 = OpenLayers.Class( OpenLayers.Format.WCSCapabilities.v1, { /** * Constructor: OpenLayers.Format.WCSCapabilities.v1_0_0 * Create a new parser for WCS capabilities version 1.0.0. * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ /** * Property: namespaces * {Object} Mapping of namespace aliases to namespace URIs. */ namespaces: { wcs: "http://www.opengis.net/wcs", xlink: "http://www.w3.org/1999/xlink", xsi: "http://www.w3.org/2001/XMLSchema-instance", ows: "http://www.opengis.net/ows" }, /** * Property: errorProperty * {String} Which property of the returned object to check for in order to * determine whether or not parsing has failed. In the case that the * errorProperty is undefined on the returned object, the document will be * run through an OGCExceptionReport parser. */ errorProperty: "service", /** * 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: { "wcs": { "WCS_Capabilities": function(node, obj) { this.readChildNodes(node, obj); }, "Service": function(node, obj) { obj.service = {}; this.readChildNodes(node, obj.service); }, "name": function(node, service) { service.name = this.getChildValue(node); }, "label": function(node, service) { service.label = this.getChildValue(node); }, "keywords": function(node, service) { service.keywords = []; this.readChildNodes(node, service.keywords); }, "keyword": function(node, keywords) { // Append the keyword to the keywords list keywords.push(this.getChildValue(node)); }, "responsibleParty": function(node, service) { service.responsibleParty = {}; this.readChildNodes(node, service.responsibleParty); }, "individualName": function(node, responsibleParty) { responsibleParty.individualName = this.getChildValue(node); }, "organisationName": function(node, responsibleParty) { responsibleParty.organisationName = this.getChildValue(node); }, "positionName": function(node, responsibleParty) { responsibleParty.positionName = this.getChildValue(node); }, "contactInfo": function(node, responsibleParty) { responsibleParty.contactInfo = {}; this.readChildNodes(node, responsibleParty.contactInfo); }, "phone": function(node, contactInfo) { contactInfo.phone = {}; this.readChildNodes(node, contactInfo.phone); }, "voice": function(node, phone) { phone.voice = this.getChildValue(node); }, "facsimile": function(node, phone) { phone.facsimile = this.getChildValue(node); }, "address": function(node, contactInfo) { contactInfo.address = {}; this.readChildNodes(node, contactInfo.address); }, "deliveryPoint": function(node, address) { address.deliveryPoint = this.getChildValue(node); }, "city": function(node, address) { address.city = this.getChildValue(node); }, "postalCode": function(node, address) { address.postalCode = this.getChildValue(node); }, "country": function(node, address) { address.country = this.getChildValue(node); }, "electronicMailAddress": function(node, address) { address.electronicMailAddress = this.getChildValue(node); }, "fees": function(node, service) { service.fees = this.getChildValue(node); }, "accessConstraints": function(node, service) { service.accessConstraints = this.getChildValue(node); }, "ContentMetadata": function(node, obj) { obj.contentMetadata = []; this.readChildNodes(node, obj.contentMetadata); }, "CoverageOfferingBrief": function(node, contentMetadata) { var coverageOfferingBrief = {}; this.readChildNodes(node, coverageOfferingBrief); contentMetadata.push(coverageOfferingBrief); }, "name": function(node, coverageOfferingBrief) { coverageOfferingBrief.name = this.getChildValue(node); }, "label": function(node, coverageOfferingBrief) { coverageOfferingBrief.label = this.getChildValue(node); }, "lonLatEnvelope": function(node, coverageOfferingBrief) { var nodeList = this.getElementsByTagNameNS(node, "http://www.opengis.net/gml", "pos"); // We expect two nodes here, to create the corners of a bounding box if(nodeList.length == 2) { var min = {}; var max = {}; OpenLayers.Format.GML.v3.prototype.readers["gml"].pos.apply(this, [nodeList[0], min]); OpenLayers.Format.GML.v3.prototype.readers["gml"].pos.apply(this, [nodeList[1], max]); coverageOfferingBrief.lonLatEnvelope = {}; coverageOfferingBrief.lonLatEnvelope.srsName = node.getAttribute("srsName"); coverageOfferingBrief.lonLatEnvelope.min = min.points[0]; coverageOfferingBrief.lonLatEnvelope.max = max.points[0]; } } } }, CLASS_NAME: "OpenLayers.Format.WCSCapabilities.v1_0_0" }); /* ====================================================================== OpenLayers/Format/WCSCapabilities/v1_1_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/WCSCapabilities/v1.js * @requires OpenLayers/Format/OWSCommon/v1_1_0.js */ /** * Class: OpenLayers.Format.WCSCapabilities/v1_1_0 * Read WCS Capabilities version 1.1.0. * * Inherits from: * - <OpenLayers.Format.WCSCapabilities.v1> */ OpenLayers.Format.WCSCapabilities.v1_1_0 = OpenLayers.Class( OpenLayers.Format.WCSCapabilities.v1, { /** * Property: namespaces * {Object} Mapping of namespace aliases to namespace URIs. */ namespaces: { wcs: "http://www.opengis.net/wcs/1.1", xlink: "http://www.w3.org/1999/xlink", xsi: "http://www.w3.org/2001/XMLSchema-instance", ows: "http://www.opengis.net/ows/1.1" }, /** * APIProperty: errorProperty * {String} Which property of the returned object to check for in order to * determine whether or not parsing has failed. In the case that the * errorProperty is undefined on the returned object, the document will be * run through an OGCExceptionReport parser. */ errorProperty: "operationsMetadata", /** * Constructor: OpenLayers.Format.WCSCapabilities.v1_1_0 * Create a new parser for WCS capabilities version 1.1.0. * * Parameters: * options - {Object} An optional object whose properties will be set on * this instance. */ /** * 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: { "wcs": OpenLayers.Util.applyDefaults({ // In 1.0.0, this was WCS_Capabilties, in 1.1.0, it's Capabilities "Capabilities": function(node, obj) { this.readChildNodes(node, obj); }, "Contents": function(node, request) { request.contentMetadata = []; this.readChildNodes(node, request.contentMetadata); }, "CoverageSummary": function(node, contentMetadata) { var coverageSummary = {}; // Read the summary: this.readChildNodes(node, coverageSummary); // Add it to the contentMetadata array: contentMetadata.push(coverageSummary); }, "Identifier": function(node, coverageSummary) { coverageSummary.identifier = this.getChildValue(node); }, "Title": function(node, coverageSummary) { coverageSummary.title = this.getChildValue(node); }, "Abstract": function(node, coverageSummary) { coverageSummary["abstract"] = this.getChildValue(node); }, "SupportedCRS": function(node, coverageSummary) { var crs = this.getChildValue(node); if(crs) { if(!coverageSummary.supportedCRS) { coverageSummary.supportedCRS = []; } coverageSummary.supportedCRS.push(crs); } }, "SupportedFormat": function(node, coverageSummary) { var format = this.getChildValue(node); if(format) { if(!coverageSummary.supportedFormat) { coverageSummary.supportedFormat = []; } coverageSummary.supportedFormat.push(format); } } }, OpenLayers.Format.WCSCapabilities.v1.prototype.readers["wcs"]), "ows": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers["ows"] }, CLASS_NAME: "OpenLayers.Format.WCSCapabilities.v1_1_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/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/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/Renderer/VML.js ====================================================================== */ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for * full list of contributors). Published under the 2-clause BSD license. * See license.txt in the OpenLayers distribution or repository for the * full text of the license. */ /** * @requires OpenLayers/Renderer/Elements.js */ /** * Class: OpenLayers.Renderer.VML * Render vector features in browsers with VML capability. Construct a new * VML renderer with the <OpenLayers.Renderer.VML> constructor. * * Note that for all calculations in this class, we use (num | 0) to truncate a * float value to an integer. This is done because it seems that VML doesn't * support float values. * * Inherits from: * - <OpenLayers.Renderer.Elements> */ OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, { /** * Property: xmlns * {String} XML Namespace URN */ xmlns: "urn:schemas-microsoft-com:vml", /** * Property: symbolCache * {DOMElement} node holding symbols. This hash is keyed by symbol name, * and each value is a hash with a "path" and an "extent" property. */ symbolCache: {}, /** * Property: offset * {Object} Hash with "x" and "y" properties */ offset: null, /** * Constructor: OpenLayers.Renderer.VML * Create a new VML renderer. * * Parameters: * containerID - {String} The id for the element that contains the renderer */ initialize: function(containerID) { if (!this.supported()) { return; } if (!document.namespaces.olv) { document.namespaces.add("olv", this.xmlns); var style = document.createStyleSheet(); var shapes = ['shape','rect', 'oval', 'fill', 'stroke', 'imagedata', 'group','textbox']; for (var i = 0, len = shapes.length; i < len; i++) { style.addRule('olv\\:' + shapes[i], "behavior: url(#default#VML); " + "position: absolute; display: inline-block;"); } } OpenLayers.Renderer.Elements.prototype.initialize.apply(this, arguments); }, /** * APIMethod: supported * Determine whether a browser supports this renderer. * * Returns: * {Boolean} The browser supports the VML renderer */ supported: function() { return !!(document.namespaces); }, /** * Method: setExtent * Set the renderer's extent * * Parameters: * extent - {<OpenLayers.Bounds>} * resolutionChanged - {Boolean} * * Returns: * {Boolean} true to notify the layer that the new extent does not exceed * the coordinate range, and the features will not need to be redrawn. */ setExtent: function(extent, resolutionChanged) { var coordSysUnchanged = OpenLayers.Renderer.Elements.prototype.setExtent.apply(this, arguments); var resolution = this.getResolution(); var left = (extent.left/resolution) | 0; var top = (extent.top/resolution - this.size.h) | 0; if (resolutionChanged || !this.offset) { this.offset = {x: left, y: top}; left = 0; top = 0; } else { left = left - this.offset.x; top = top - this.offset.y; } var org = (left - this.xOffset) + " " + top; this.root.coordorigin = org; var roots = [this.root, this.vectorRoot, this.textRoot]; var root; for(var i=0, len=roots.length; i<len; ++i) { root = roots[i]; var size = this.size.w + " " + this.size.h; root.coordsize = size; } // flip the VML display Y axis upside down so it // matches the display Y axis of the map this.root.style.flip = "y"; return coordSysUnchanged; }, /** * Method: setSize * Set the size of the drawing surface * * Parameters: * size - {<OpenLayers.Size>} the size of the drawing surface */ setSize: function(size) { OpenLayers.Renderer.prototype.setSize.apply(this, arguments); // setting width and height on all roots to avoid flicker which we // would get with 100% width and height on child roots var roots = [ this.rendererRoot, this.root, this.vectorRoot, this.textRoot ]; var w = this.size.w + "px"; var h = this.size.h + "px"; var root; for(var i=0, len=roots.length; i<len; ++i) { root = roots[i]; root.style.width = w; root.style.height = h; } }, /** * Method: getNodeType * Get the node type for a geometry and style * * Parameters: * geometry - {<OpenLayers.Geometry>} * style - {Object} * * Returns: * {String} The corresponding node type for the specified geometry */ getNodeType: function(geometry, style) { var nodeType = null; switch (geometry.CLASS_NAME) { case "OpenLayers.Geometry.Point": if (style.externalGraphic) { nodeType = "olv:rect"; } else if (this.isComplexSymbol(style.graphicName)) { nodeType = "olv:shape"; } else { nodeType = "olv:oval"; } break; case "OpenLayers.Geometry.Rectangle": nodeType = "olv:rect"; break; case "OpenLayers.Geometry.LineString": case "OpenLayers.Geometry.LinearRing": case "OpenLayers.Geometry.Polygon": case "OpenLayers.Geometry.Curve": nodeType = "olv:shape"; break; default: break; } return nodeType; }, /** * Method: setStyle * Use to set all the style attributes to a VML node. * * Parameters: * node - {DOMElement} An VML element to decorate * style - {Object} * options - {Object} Currently supported options include * 'isFilled' {Boolean} and * 'isStroked' {Boolean} * geometry - {<OpenLayers.Geometry>} */ setStyle: function(node, style, options, geometry) { style = style || node._style; options = options || node._options; var fillColor = style.fillColor; var title = style.title || style.graphicTitle; if (title) { node.title = title; } if (node._geometryClass === "OpenLayers.Geometry.Point") { if (style.externalGraphic) { options.isFilled = true; var width = style.graphicWidth || style.graphicHeight; var height = style.graphicHeight || style.graphicWidth; width = width ? width : style.pointRadius*2; height = height ? height : style.pointRadius*2; var resolution = this.getResolution(); var xOffset = (style.graphicXOffset != undefined) ? style.graphicXOffset : -(0.5 * width); var yOffset = (style.graphicYOffset != undefined) ? style.graphicYOffset : -(0.5 * height); node.style.left = ((((geometry.x - this.featureDx)/resolution - this.offset.x)+xOffset) | 0) + "px"; node.style.top = (((geometry.y/resolution - this.offset.y)-(yOffset+height)) | 0) + "px"; node.style.width = width + "px"; node.style.height = height + "px"; node.style.flip = "y"; // modify fillColor and options for stroke styling below fillColor = "none"; options.isStroked = false; } else if (this.isComplexSymbol(style.graphicName)) { var cache = this.importSymbol(style.graphicName); node.path = cache.path; node.coordorigin = cache.left + "," + cache.bottom; var size = cache.size; node.coordsize = size + "," + size; this.drawCircle(node, geometry, style.pointRadius); node.style.flip = "y"; } else { this.drawCircle(node, geometry, style.pointRadius); } } // fill if (options.isFilled) { node.fillcolor = fillColor; } else { node.filled = "false"; } var fills = node.getElementsByTagName("fill"); var fill = (fills.length == 0) ? null : fills[0]; if (!options.isFilled) { if (fill) { node.removeChild(fill); } } else { if (!fill) { fill = this.createNode('olv:fill', node.id + "_fill"); } fill.opacity = style.fillOpacity; if (node._geometryClass === "OpenLayers.Geometry.Point" && style.externalGraphic) { // override fillOpacity if (style.graphicOpacity) { fill.opacity = style.graphicOpacity; } fill.src = style.externalGraphic; fill.type = "frame"; if (!(style.graphicWidth && style.graphicHeight)) { fill.aspect = "atmost"; } } if (fill.parentNode != node) { node.appendChild(fill); } } // additional rendering for rotated graphics or symbols var rotation = style.rotation; if ((rotation !== undefined || node._rotation !== undefined)) { node._rotation = rotation; if (style.externalGraphic) { this.graphicRotate(node, xOffset, yOffset, style); // make the fill fully transparent, because we now have // the graphic as imagedata element. We cannot just remove // the fill, because this is part of the hack described // in graphicRotate fill.opacity = 0; } else if(node._geometryClass === "OpenLayers.Geometry.Point") { node.style.rotation = rotation || 0; } } // stroke var strokes = node.getElementsByTagName("stroke"); var stroke = (strokes.length == 0) ? null : strokes[0]; if (!options.isStroked) { node.stroked = false; if (stroke) { stroke.on = false; } } else { if (!stroke) { stroke = this.createNode('olv:stroke', node.id + "_stroke"); node.appendChild(stroke); } stroke.on = true; stroke.color = style.strokeColor; stroke.weight = style.strokeWidth + "px"; stroke.opacity = style.strokeOpacity; stroke.endcap = style.strokeLinecap == 'butt' ? 'flat' : (style.strokeLinecap || 'round'); if (style.strokeDashstyle) { stroke.dashstyle = this.dashStyle(style); } } if (style.cursor != "inherit" && style.cursor != null) { node.style.cursor = style.cursor; } return node; }, /** * Method: graphicRotate * If a point is to be styled with externalGraphic and rotation, VML fills * cannot be used to display the graphic, because rotation of graphic * fills is not supported by the VML implementation of Internet Explorer. * This method creates a olv:imagedata element inside the VML node, * DXImageTransform.Matrix and BasicImage filters for rotation and * opacity, and a 3-step hack to remove rendering artefacts from the * graphic and preserve the ability of graphics to trigger events. * Finally, OpenLayers methods are used to determine the correct * insertion point of the rotated image, because DXImageTransform.Matrix * does the rotation without the ability to specify a rotation center * point. * * Parameters: * node - {DOMElement} * xOffset - {Number} rotation center relative to image, x coordinate * yOffset - {Number} rotation center relative to image, y coordinate * style - {Object} */ graphicRotate: function(node, xOffset, yOffset, style) { var style = style || node._style; var rotation = style.rotation || 0; var aspectRatio, size; if (!(style.graphicWidth && style.graphicHeight)) { // load the image to determine its size var img = new Image(); img.onreadystatechange = OpenLayers.Function.bind(function() { if(img.readyState == "complete" || img.readyState == "interactive") { aspectRatio = img.width / img.height; size = Math.max(style.pointRadius * 2, style.graphicWidth || 0, style.graphicHeight || 0); xOffset = xOffset * aspectRatio; style.graphicWidth = size * aspectRatio; style.graphicHeight = size; this.graphicRotate(node, xOffset, yOffset, style); } }, this); img.src = style.externalGraphic; // will be called again by the onreadystate handler return; } else { size = Math.max(style.graphicWidth, style.graphicHeight); aspectRatio = style.graphicWidth / style.graphicHeight; } var width = Math.round(style.graphicWidth || size * aspectRatio); var height = Math.round(style.graphicHeight || size); node.style.width = width + "px"; node.style.height = height + "px"; // Three steps are required to remove artefacts for images with // transparent backgrounds (resulting from using DXImageTransform // filters on svg objects), while preserving awareness for browser // events on images: // - Use the fill as usual (like for unrotated images) to handle // events // - specify an imagedata element with the same src as the fill // - style the imagedata element with an AlphaImageLoader filter // with empty src var image = document.getElementById(node.id + "_image"); if (!image) { image = this.createNode("olv:imagedata", node.id + "_image"); node.appendChild(image); } image.style.width = width + "px"; image.style.height = height + "px"; image.src = style.externalGraphic; image.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(" + "src='', sizingMethod='scale')"; var rot = rotation * Math.PI / 180; var sintheta = Math.sin(rot); var costheta = Math.cos(rot); // do the rotation on the image var filter = "progid:DXImageTransform.Microsoft.Matrix(M11=" + costheta + ",M12=" + (-sintheta) + ",M21=" + sintheta + ",M22=" + costheta + ",SizingMethod='auto expand')\n"; // set the opacity (needed for the imagedata) var opacity = style.graphicOpacity || style.fillOpacity; if (opacity && opacity != 1) { filter += "progid:DXImageTransform.Microsoft.BasicImage(opacity=" + opacity+")\n"; } node.style.filter = filter; // do the rotation again on a box, so we know the insertion point var centerPoint = new OpenLayers.Geometry.Point(-xOffset, -yOffset); var imgBox = new OpenLayers.Bounds(0, 0, width, height).toGeometry(); imgBox.rotate(style.rotation, centerPoint); var imgBounds = imgBox.getBounds(); node.style.left = Math.round( parseInt(node.style.left) + imgBounds.left) + "px"; node.style.top = Math.round( parseInt(node.style.top) - imgBounds.bottom) + "px"; }, /** * Method: postDraw * Does some node postprocessing to work around browser issues: * - Some versions of Internet Explorer seem to be unable to set fillcolor * and strokecolor to "none" correctly before the fill node is appended * to a visible vml node. This method takes care of that and sets * fillcolor and strokecolor again if needed. * - In some cases, a node won't become visible after being drawn. Setting * style.visibility to "visible" works around that. * * Parameters: * node - {DOMElement} */ postDraw: function(node) { node.style.visibility = "visible"; var fillColor = node._style.fillColor; var strokeColor = node._style.strokeColor; if (fillColor == "none" && node.fillcolor != fillColor) { node.fillcolor = fillColor; } if (strokeColor == "none" && node.strokecolor != strokeColor) { node.strokecolor = strokeColor; } }, /** * Method: setNodeDimension * Get the geometry's bounds, convert it to our vml coordinate system, * then set the node's position, size, and local coordinate system. * * Parameters: * node - {DOMElement} * geometry - {<OpenLayers.Geometry>} */ setNodeDimension: function(node, geometry) { var bbox = geometry.getBounds(); if(bbox) { var resolution = this.getResolution(); var scaledBox = new OpenLayers.Bounds(((bbox.left - this.featureDx)/resolution - this.offset.x) | 0, (bbox.bottom/resolution - this.offset.y) | 0, ((bbox.right - this.featureDx)/resolution - this.offset.x) | 0, (bbox.top/resolution - this.offset.y) | 0); // Set the internal coordinate system to draw the path node.style.left = scaledBox.left + "px"; node.style.top = scaledBox.top + "px"; node.style.width = scaledBox.getWidth() + "px"; node.style.height = scaledBox.getHeight() + "px"; node.coordorigin = scaledBox.left + " " + scaledBox.top; node.coordsize = scaledBox.getWidth()+ " " + scaledBox.getHeight(); } }, /** * Method: dashStyle * * Parameters: * style - {Object} * * Returns: * {String} A VML compliant 'stroke-dasharray' value */ dashStyle: function(style) { var dash = style.strokeDashstyle; switch (dash) { case 'solid': case 'dot': case 'dash': case 'dashdot': case 'longdash': case 'longdashdot': return dash; default: // very basic guessing of dash style patterns var parts = dash.split(/[ ,]/); if (parts.length == 2) { if (1*parts[0] >= 2*parts[1]) { return "longdash"; } return (parts[0] == 1 || parts[1] == 1) ? "dot" : "dash"; } else if (parts.length == 4) { return (1*parts[0] >= 2*parts[1]) ? "longdashdot" : "dashdot"; } return "solid"; } }, /** * Method: createNode * Create a new node * * Parameters: * type - {String} Kind of node to draw * id - {String} Id for node * * Returns: * {DOMElement} A new node of the given type and id */ createNode: function(type, id) { var node = document.createElement(type); if (id) { node.id = id; } // IE hack to make elements unselectable, to prevent 'blue flash' // while dragging vectors; #1410 node.unselectable = 'on'; node.onselectstart = OpenLayers.Function.False; return node; }, /** * Method: nodeTypeCompare * Determine whether a node is of a given type * * Parameters: * node - {DOMElement} An VML element * type - {String} Kind of node * * Returns: * {Boolean} Whether or not the specified node is of the specified type */ nodeTypeCompare: function(node, type) { //split type var subType = type; var splitIndex = subType.indexOf(":"); if (splitIndex != -1) { subType = subType.substr(splitIndex+1); } //split nodeName var nodeName = node.nodeName; splitIndex = nodeName.indexOf(":"); if (splitIndex != -1) { nodeName = nodeName.substr(splitIndex+1); } return (subType == nodeName); }, /** * Method: createRenderRoot * Create the renderer root * * Returns: * {DOMElement} The specific render engine's root element */ createRenderRoot: function() { return this.nodeFactory(this.container.id + "_vmlRoot", "div"); }, /** * Method: createRoot * Create the main root element * * Parameters: * suffix - {String} suffix to append to the id * * Returns: * {DOMElement} */ createRoot: function(suffix) { return this.nodeFactory(this.container.id + suffix, "olv:group"); }, /************************************** * * * GEOMETRY DRAWING FUNCTIONS * * * **************************************/ /** * Method: drawPoint * Render a point * * Parameters: * node - {DOMElement} * geometry - {<OpenLayers.Geometry>} * * Returns: * {DOMElement} or false if the point could not be drawn */ drawPoint: function(node, geometry) { return this.drawCircle(node, geometry, 1); }, /** * Method: drawCircle * Render a circle. * Size and Center a circle given geometry (x,y center) and radius * * Parameters: * node - {DOMElement} * geometry - {<OpenLayers.Geometry>} * radius - {float} * * Returns: * {DOMElement} or false if the circle could not ne drawn */ drawCircle: function(node, geometry, radius) { if(!isNaN(geometry.x)&& !isNaN(geometry.y)) { var resolution = this.getResolution(); node.style.left = ((((geometry.x - this.featureDx) /resolution - this.offset.x) | 0) - radius) + "px"; node.style.top = (((geometry.y /resolution - this.offset.y) | 0) - radius) + "px"; var diameter = radius * 2; node.style.width = diameter + "px"; node.style.height = diameter + "px"; return node; } return false; }, /** * Method: drawLineString * Render a linestring. * * Parameters: * node - {DOMElement} * geometry - {<OpenLayers.Geometry>} * * Returns: * {DOMElement} */ drawLineString: function(node, geometry) { return this.drawLine(node, geometry, false); }, /** * Method: drawLinearRing * Render a linearring * * Parameters: * node - {DOMElement} * geometry - {<OpenLayers.Geometry>} * * Returns: * {DOMElement} */ drawLinearRing: function(node, geometry) { return this.drawLine(node, geometry, true); }, /** * Method: DrawLine * Render a line. * * Parameters: * node - {DOMElement} * geometry - {<OpenLayers.Geometry>} * closeLine - {Boolean} Close the line? (make it a ring?) * * Returns: * {DOMElement} */ drawLine: function(node, geometry, closeLine) { this.setNodeDimension(node, geometry); var resolution = this.getResolution(); var numComponents = geometry.components.length; var parts = new Array(numComponents); var comp, x, y; for (var i = 0; i < numComponents; i++) { comp = geometry.components[i]; x = ((comp.x - this.featureDx)/resolution - this.offset.x) | 0; y = (comp.y/resolution - this.offset.y) | 0; parts[i] = " " + x + "," + y + " l "; } var end = (closeLine) ? " x e" : " e"; node.path = "m" + parts.join("") + end; return node; }, /** * Method: drawPolygon * Render a polygon * * Parameters: * node - {DOMElement} * geometry - {<OpenLayers.Geometry>} * * Returns: * {DOMElement} */ drawPolygon: function(node, geometry) { this.setNodeDimension(node, geometry); var resolution = this.getResolution(); var path = []; var j, jj, points, area, first, second, i, ii, comp, pathComp, x, y; for (j=0, jj=geometry.components.length; j<jj; j++) { path.push("m"); points = geometry.components[j].components; // we only close paths of interior rings with area area = (j === 0); first = null; second = null; for (i=0, ii=points.length; i<ii; i++) { comp = points[i]; x = ((comp.x - this.featureDx) / resolution - this.offset.x) | 0; y = (comp.y / resolution - this.offset.y) | 0; pathComp = " " + x + "," + y; path.push(pathComp); if (i==0) { path.push(" l"); } if (!area) { // IE improperly renders sub-paths that have no area. // Instead of checking the area of every ring, we confirm // the ring has at least three distinct points. This does // not catch all non-zero area cases, but it greatly improves // interior ring digitizing and is a minor performance hit // when rendering rings with many points. if (!first) { first = pathComp; } else if (first != pathComp) { if (!second) { second = pathComp; } else if (second != pathComp) { // stop looking area = true; } } } } path.push(area ? " x " : " "); } path.push("e"); node.path = path.join(""); return node; }, /** * Method: drawRectangle * Render a rectangle * * Parameters: * node - {DOMElement} * geometry - {<OpenLayers.Geometry>} * * Returns: * {DOMElement} */ drawRectangle: function(node, geometry) { var resolution = this.getResolution(); node.style.left = (((geometry.x - this.featureDx)/resolution - this.offset.x) | 0) + "px"; node.style.top = ((geometry.y/resolution - this.offset.y) | 0) + "px"; node.style.width = ((geometry.width/resolution) | 0) + "px"; node.style.height = ((geometry.height/resolution) | 0) + "px"; return node; }, /** * Method: drawText * This method is only called by the renderer itself. * * Parameters: * featureId - {String} * style - * location - {<OpenLayers.Geometry.Point>} */ drawText: function(featureId, style, location) { var label = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX, "olv:rect"); var textbox = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX + "_textbox", "olv:textbox"); var resolution = this.getResolution(); label.style.left = (((location.x - this.featureDx)/resolution - this.offset.x) | 0) + "px"; label.style.top = ((location.y/resolution - this.offset.y) | 0) + "px"; label.style.flip = "y"; textbox.innerText = style.label; if (style.cursor != "inherit" && style.cursor != null) { textbox.style.cursor = style.cursor; } if (style.fontColor) { textbox.style.color = style.fontColor; } if (style.fontOpacity) { textbox.style.filter = 'alpha(opacity=' + (style.fontOpacity * 100) + ')'; } if (style.fontFamily) { textbox.style.fontFamily = style.fontFamily; } if (style.fontSize) { textbox.style.fontSize = style.fontSize; } if (style.fontWeight) { textbox.style.fontWeight = style.fontWeight; } if (style.fontStyle) { textbox.style.fontStyle = style.fontStyle; } if(style.labelSelect === true) { label._featureId = featureId; textbox._featureId = featureId; textbox._geometry = location; textbox._geometryClass = location.CLASS_NAME; } textbox.style.whiteSpace = "nowrap"; // fun with IE: IE7 in standards compliant mode does not display any // text with a left inset of 0. So we set this to 1px and subtract one // pixel later when we set label.style.left textbox.inset = "1px,0px,0px,0px"; if(!label.parentNode) { label.appendChild(textbox); this.textRoot.appendChild(label); } var align = style.labelAlign || "cm"; if (align.length == 1) { align += "m"; } var xshift = textbox.clientWidth * (OpenLayers.Renderer.VML.LABEL_SHIFT[align.substr(0,1)]); var yshift = textbox.clientHeight * (OpenLayers.Renderer.VML.LABEL_SHIFT[align.substr(1,1)]); label.style.left = parseInt(label.style.left)-xshift-1+"px"; label.style.top = parseInt(label.style.top)+yshift+"px"; }, /** * Method: moveRoot * moves this renderer's root to a different renderer. * * Parameters: * renderer - {<OpenLayers.Renderer>} target renderer for the moved root * root - {DOMElement} optional root node. To be used when this renderer * holds roots from multiple layers to tell this method which one to * detach * * Returns: * {Boolean} true if successful, false otherwise */ moveRoot: function(renderer) { var layer = this.map.getLayer(renderer.container.id); if(layer instanceof OpenLayers.Layer.Vector.RootContainer) { layer = this.map.getLayer(this.container.id); } layer && layer.renderer.clear(); OpenLayers.Renderer.Elements.prototype.moveRoot.apply(this, arguments); layer && layer.redraw(); }, /** * Method: importSymbol * add a new symbol definition from the rendererer's symbol hash * * Parameters: * graphicName - {String} name of the symbol to import * * Returns: * {Object} - hash of {DOMElement} "symbol" and {Number} "size" */ importSymbol: function (graphicName) { var id = this.container.id + "-" + graphicName; // check if symbol already exists in the cache var cache = this.symbolCache[id]; if (cache) { return cache; } var symbol = OpenLayers.Renderer.symbol[graphicName]; if (!symbol) { throw new Error(graphicName + ' is not a valid symbol name'); } var symbolExtent = new OpenLayers.Bounds( Number.MAX_VALUE, Number.MAX_VALUE, 0, 0); var pathitems = ["m"]; for (var i=0; i<symbol.length; i=i+2) { var x = symbol[i]; var y = symbol[i+1]; symbolExtent.left = Math.min(symbolExtent.left, x); symbolExtent.bottom = Math.min(symbolExtent.bottom, y); symbolExtent.right = Math.max(symbolExtent.right, x); symbolExtent.top = Math.max(symbolExtent.top, y); pathitems.push(x); pathitems.push(y); if (i == 0) { pathitems.push("l"); } } pathitems.push("x e"); var path = pathitems.join(" "); var diff = (symbolExtent.getWidth() - symbolExtent.getHeight()) / 2; if(diff > 0) { symbolExtent.bottom = symbolExtent.bottom - diff; symbolExtent.top = symbolExtent.top + diff; } else { symbolExtent.left = symbolExtent.left + diff; symbolExtent.right = symbolExtent.right - diff; } cache = { path: path, size: symbolExtent.getWidth(), // equals getHeight() now left: symbolExtent.left, bottom: symbolExtent.bottom }; this.symbolCache[id] = cache; return cache; }, CLASS_NAME: "OpenLayers.Renderer.VML" }); /** * Constant: OpenLayers.Renderer.VML.LABEL_SHIFT * {Object} */ OpenLayers.Renderer.VML.LABEL_SHIFT = { "l": 0, "c": .5, "r": 1, "t": 0, "m": .5, "b": 1 }; /* ====================================================================== OpenLayers/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/Layer/WorldWind.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.WorldWind * * Inherits from: * - <OpenLayers.Layer.Grid> */ OpenLayers.Layer.WorldWind = OpenLayers.Class(OpenLayers.Layer.Grid, { DEFAULT_PARAMS: { }, /** * APIProperty: isBaseLayer * {Boolean} WorldWind layer is a base layer by default. */ isBaseLayer: true, /** * APIProperty: lzd * {Float} LevelZeroTileSizeDegrees */ lzd: null, /** * APIProperty: zoomLevels * {Integer} Number of zoom levels. */ zoomLevels: null, /** * Constructor: OpenLayers.Layer.WorldWind * * Parameters: * name - {String} Name of Layer * url - {String} Base URL * lzd - {Float} Level zero tile size degrees * zoomLevels - {Integer} number of zoom levels * params - {Object} additional parameters * options - {Object} additional options */ initialize: function(name, url, lzd, zoomLevels, params, options) { this.lzd = lzd; this.zoomLevels = zoomLevels; var newArguments = []; newArguments.push(name, url, params, options); OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments); this.params = OpenLayers.Util.applyDefaults( this.params, this.DEFAULT_PARAMS ); }, /** * Method: getZoom * Convert map zoom to WW zoom. */ getZoom: function () { var zoom = this.map.getZoom(); var extent = this.map.getMaxExtent(); zoom = zoom - Math.log(this.maxResolution / (this.lzd/512))/Math.log(2); return zoom; }, /** * Method: getURL * * Parameters: * bounds - {<OpenLayers.Bounds>} * * Returns: * {String} A string with the layer's url and parameters and also the * passed-in bounds and appropriate tile size specified as * parameters */ getURL: function (bounds) { bounds = this.adjustBounds(bounds); var zoom = this.getZoom(); var extent = this.map.getMaxExtent(); var deg = this.lzd/Math.pow(2,this.getZoom()); var x = Math.floor((bounds.left - extent.left)/deg); var y = Math.floor((bounds.bottom - extent.bottom)/deg); if (this.map.getResolution() <= (this.lzd/512) && this.getZoom() <= this.zoomLevels) { return this.getFullRequestString( { L: zoom, X: x, Y: y }); } else { return OpenLayers.Util.getImageLocation("blank.gif"); } }, CLASS_NAME: "OpenLayers.Layer.WorldWind" }); /* ====================================================================== OpenLayers/Layer/WMTS.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.WMTS * Instances of the WMTS class allow viewing of tiles from a service that * implements the OGC WMTS specification version 1.0.0. * * Inherits from: * - <OpenLayers.Layer.Grid> */ OpenLayers.Layer.WMTS = OpenLayers.Class(OpenLayers.Layer.Grid, { /** * APIProperty: isBaseLayer * {Boolean} The layer will be considered a base layer. Default is true. */ isBaseLayer: true, /** * Property: version * {String} WMTS version. Default is "1.0.0". */ version: "1.0.0", /** * APIProperty: requestEncoding * {String} Request encoding. Can be "REST" or "KVP". Default is "KVP". */ requestEncoding: "KVP", /** * APIProperty: url * {String|Array(String)} The base URL or request URL template for the WMTS * service. Must be provided. Array is only supported for base URLs, not * for request URL templates. URL templates are only supported for * REST <requestEncoding>. */ url: null, /** * APIProperty: layer * {String} The layer identifier advertised by the WMTS service. Must be * provided. */ layer: null, /** * APIProperty: matrixSet * {String} One of the advertised matrix set identifiers. Must be provided. */ matrixSet: null, /** * APIProperty: style * {String} One of the advertised layer styles. Must be provided. */ style: null, /** * APIProperty: format * {String} The image MIME type. Default is "image/jpeg". */ format: "image/jpeg", /** * APIProperty: tileOrigin * {<OpenLayers.LonLat>} The top-left corner of the tile matrix in map * units. If the tile origin for each matrix in a set is different, * the <matrixIds> should include a topLeftCorner property. If * not provided, the tile origin will default to the top left corner * of the layer <maxExtent>. */ tileOrigin: null, /** * APIProperty: tileFullExtent * {<OpenLayers.Bounds>} The full extent of the tile set. If not supplied, * the layer's <maxExtent> property will be used. */ tileFullExtent: null, /** * APIProperty: formatSuffix * {String} For REST request encoding, an image format suffix must be * included in the request. If not provided, the suffix will be derived * from the <format> property. */ formatSuffix: null, /** * APIProperty: matrixIds * {Array} A list of tile matrix identifiers. If not provided, the matrix * identifiers will be assumed to be integers corresponding to the * map zoom level. If a list of strings is provided, each item should * be the matrix identifier that corresponds to the map zoom level. * Additionally, a list of objects can be provided. Each object should * describe the matrix as presented in the WMTS capabilities. These * objects should have the propertes shown below. * * Matrix properties: * identifier - {String} The matrix identifier (required). * scaleDenominator - {Number} The matrix scale denominator. * topLeftCorner - {<OpenLayers.LonLat>} The top left corner of the * matrix. Must be provided if different than the layer <tileOrigin>. * tileWidth - {Number} The tile width for the matrix. Must be provided * if different than the width given in the layer <tileSize>. * tileHeight - {Number} The tile height for the matrix. Must be provided * if different than the height given in the layer <tileSize>. */ matrixIds: null, /** * APIProperty: dimensions * {Array} For RESTful request encoding, extra dimensions may be specified. * Items in this list should be property names in the <params> object. * Values of extra dimensions will be determined from the corresponding * values in the <params> object. */ dimensions: null, /** * APIProperty: params * {Object} Extra parameters to include in tile requests. For KVP * <requestEncoding>, these properties will be encoded in the request * query string. For REST <requestEncoding>, these properties will * become part of the request path, with order determined by the * <dimensions> list. */ params: null, /** * APIProperty: zoomOffset * {Number} If your cache has more 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). Additionally, if this layer is to be used * as an overlay and the cache has fewer zoom levels than the base * layer, you can supply a negative zoomOffset. For example, if a * map zoom level of 1 corresponds to your cache level zero, you would * supply a -1 zoomOffset (and set the maxResolution of the layer * appropriately). The zoomOffset value has no effect if complete * matrix definitions (including scaleDenominator) are supplied in * the <matrixIds> property. Defaults to 0 (no zoom offset). */ 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, /** * Property: formatSuffixMap * {Object} a map between WMTS 'format' request parameter and tile image file suffix */ formatSuffixMap: { "image/png": "png", "image/png8": "png", "image/png24": "png", "image/png32": "png", "png": "png", "image/jpeg": "jpg", "image/jpg": "jpg", "jpeg": "jpg", "jpg": "jpg" }, /** * Property: matrix * {Object} Matrix definition for the current map resolution. Updated by * the <updateMatrixProperties> method. */ matrix: null, /** * Constructor: OpenLayers.Layer.WMTS * Create a new WMTS layer. * * Example: * (code) * var wmts = new OpenLayers.Layer.WMTS({ * name: "My WMTS Layer", * url: "http://example.com/wmts", * layer: "layer_id", * style: "default", * matrixSet: "matrix_id" * }); * (end) * * Parameters: * config - {Object} Configuration properties for the layer. * * Required configuration properties: * url - {String} The base url for the service. See the <url> property. * layer - {String} The layer identifier. See the <layer> property. * style - {String} The layer style identifier. See the <style> property. * matrixSet - {String} The tile matrix set identifier. See the <matrixSet> * property. * * Any other documented layer properties can be provided in the config object. */ initialize: function(config) { // confirm required properties are supplied var required = { url: true, layer: true, style: true, matrixSet: true }; for (var prop in required) { if (!(prop in config)) { throw new Error("Missing property '" + prop + "' in layer configuration."); } } config.params = OpenLayers.Util.upperCaseObject(config.params); var args = [config.name, config.url, config.params, config]; OpenLayers.Layer.Grid.prototype.initialize.apply(this, args); // determine format suffix (for REST) if (!this.formatSuffix) { this.formatSuffix = this.formatSuffixMap[this.format] || this.format.split("/").pop(); } // expand matrixIds (may be array of string or array of object) if (this.matrixIds) { var len = this.matrixIds.length; if (len && typeof this.matrixIds[0] === "string") { var ids = this.matrixIds; this.matrixIds = new Array(len); for (var i=0; i<len; ++i) { this.matrixIds[i] = {identifier: ids[i]}; } } } }, /** * Method: setMap */ setMap: function() { OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments); }, /** * Method: updateMatrixProperties * Called when map resolution changes to update matrix related properties. */ updateMatrixProperties: function() { this.matrix = this.getMatrix(); if (this.matrix) { if (this.matrix.topLeftCorner) { this.tileOrigin = this.matrix.topLeftCorner; } if (this.matrix.tileWidth && this.matrix.tileHeight) { this.tileSize = new OpenLayers.Size( this.matrix.tileWidth, this.matrix.tileHeight ); } if (!this.tileOrigin) { this.tileOrigin = new OpenLayers.LonLat( this.maxExtent.left, this.maxExtent.top ); } if (!this.tileFullExtent) { this.tileFullExtent = this.maxExtent; } } }, /** * 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) { if (zoomChanged || !this.matrix) { this.updateMatrixProperties(); } return OpenLayers.Layer.Grid.prototype.moveTo.apply(this, arguments); }, /** * APIMethod: clone * * Parameters: * obj - {Object} * * Returns: * {<OpenLayers.Layer.WMTS>} An exact clone of this <OpenLayers.Layer.WMTS> */ clone: function(obj) { if (obj == null) { obj = new OpenLayers.Layer.WMTS(this.options); } //get all additions from superclasses obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); // copy/set any non-init, non-simple values here return obj; }, /** * Method: getIdentifier * Get the current index in the matrixIds array. */ getIdentifier: function() { return this.getServerZoom(); }, /** * Method: getMatrix * Get the appropriate matrix definition for the current map resolution. */ getMatrix: function() { var matrix; if (!this.matrixIds || this.matrixIds.length === 0) { matrix = {identifier: this.getIdentifier()}; } else { // get appropriate matrix given the map scale if possible if ("scaleDenominator" in this.matrixIds[0]) { // scale denominator calculation based on WMTS spec var denom = OpenLayers.METERS_PER_INCH * OpenLayers.INCHES_PER_UNIT[this.units] * this.getServerResolution() / 0.28E-3; var diff = Number.POSITIVE_INFINITY; var delta; for (var i=0, ii=this.matrixIds.length; i<ii; ++i) { delta = Math.abs(1 - (this.matrixIds[i].scaleDenominator / denom)); if (delta < diff) { diff = delta; matrix = this.matrixIds[i]; } } } else { // fall back on zoom as index matrix = this.matrixIds[this.getIdentifier()]; } } return matrix; }, /** * Method: getTileInfo * Get tile information for a given location at the current map resolution. * * Parameters: * loc - {<OpenLayers.LonLat} A location in map coordinates. * * Returns: * {Object} An object with "col", "row", "i", and "j" properties. The col * and row values are zero based tile indexes from the top left. The * i and j values are the number of pixels to the left and top * (respectively) of the given location within the target tile. */ getTileInfo: function(loc) { var res = this.getServerResolution(); var fx = (loc.lon - this.tileOrigin.lon) / (res * this.tileSize.w); var fy = (this.tileOrigin.lat - loc.lat) / (res * this.tileSize.h); var col = Math.floor(fx); var row = Math.floor(fy); return { col: col, row: row, i: Math.floor((fx - col) * this.tileSize.w), j: Math.floor((fy - row) * this.tileSize.h) }; }, /** * Method: getURL * * Parameters: * bounds - {<OpenLayers.Bounds>} * * Returns: * {String} A URL for the tile corresponding to the given bounds. */ getURL: function(bounds) { bounds = this.adjustBounds(bounds); var url = ""; if (!this.tileFullExtent || this.tileFullExtent.intersectsBounds(bounds)) { var center = bounds.getCenterLonLat(); var info = this.getTileInfo(center); var matrixId = this.matrix.identifier; var dimensions = this.dimensions, params; if (OpenLayers.Util.isArray(this.url)) { url = this.selectUrl([ this.version, this.style, this.matrixSet, this.matrix.identifier, info.row, info.col ].join(","), this.url); } else { url = this.url; } if (this.requestEncoding.toUpperCase() === "REST") { params = this.params; if (url.indexOf("{") !== -1) { var template = url.replace(/\{/g, "${"); var context = { // spec does not make clear if capital S or not style: this.style, Style: this.style, TileMatrixSet: this.matrixSet, TileMatrix: this.matrix.identifier, TileRow: info.row, TileCol: info.col }; if (dimensions) { var dimension, i; for (i=dimensions.length-1; i>=0; --i) { dimension = dimensions[i]; context[dimension] = params[dimension.toUpperCase()]; } } url = OpenLayers.String.format(template, context); } else { // include 'version', 'layer' and 'style' in tile resource url var path = this.version + "/" + this.layer + "/" + this.style + "/"; // append optional dimension path elements if (dimensions) { for (var i=0; i<dimensions.length; i++) { if (params[dimensions[i]]) { path = path + params[dimensions[i]] + "/"; } } } // append other required path elements path = path + this.matrixSet + "/" + this.matrix.identifier + "/" + info.row + "/" + info.col + "." + this.formatSuffix; if (!url.match(/\/$/)) { url = url + "/"; } url = url + path; } } else if (this.requestEncoding.toUpperCase() === "KVP") { // assemble all required parameters params = { SERVICE: "WMTS", REQUEST: "GetTile", VERSION: this.version, LAYER: this.layer, STYLE: this.style, TILEMATRIXSET: this.matrixSet, TILEMATRIX: this.matrix.identifier, TILEROW: info.row, TILECOL: info.col, FORMAT: this.format }; url = OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(this, [params]); } } return url; }, /** * APIMethod: mergeNewParams * Extend the existing layer <params> with new properties. Tiles will be * reloaded with updated params in the request. * * Parameters: * newParams - {Object} Properties to extend to existing <params>. */ mergeNewParams: function(newParams) { if (this.requestEncoding.toUpperCase() === "KVP") { return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply( this, [OpenLayers.Util.upperCaseObject(newParams)] ); } }, CLASS_NAME: "OpenLayers.Layer.WMTS" }); /* ====================================================================== OpenLayers/Layer/ArcIMS.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 * @requires OpenLayers/Format/ArcXML.js * @requires OpenLayers/Request.js */ /** * Class: OpenLayers.Layer.ArcIMS * Instances of OpenLayers.Layer.ArcIMS are used to display data from ESRI ArcIMS * Mapping Services. Create a new ArcIMS layer with the <OpenLayers.Layer.ArcIMS> * constructor. * * Inherits from: * - <OpenLayers.Layer.Grid> */ OpenLayers.Layer.ArcIMS = OpenLayers.Class(OpenLayers.Layer.Grid, { /** * Constant: DEFAULT_PARAMS * {Object} Default query string parameters. */ DEFAULT_PARAMS: { ClientVersion: "9.2", ServiceName: '' }, /** * APIProperty: featureCoordSys * {String} Code for feature coordinate system. Default is "4326". */ featureCoordSys: "4326", /** * APIProperty: filterCoordSys * {String} Code for filter coordinate system. Default is "4326". */ filterCoordSys: "4326", /** * APIProperty: layers * {Array} An array of objects with layer properties. */ layers: null, /** * APIProperty: async * {Boolean} Request images asynchronously. Default is true. */ async: true, /** * APIProperty: name * {String} Layer name. Default is "ArcIMS". */ name: "ArcIMS", /** * APIProperty: isBaseLayer * {Boolean} The layer is a base layer. Default is true. */ isBaseLayer: true, /** * Constant: DEFAULT_OPTIONS * {Object} Default layers properties. */ DEFAULT_OPTIONS: { tileSize: new OpenLayers.Size(512, 512), featureCoordSys: "4326", filterCoordSys: "4326", layers: null, isBaseLayer: true, async: true, name: "ArcIMS" }, /** * Constructor: OpenLayers.Layer.ArcIMS * Create a new ArcIMS layer object. * * Example: * (code) * var arcims = new OpenLayers.Layer.ArcIMS( * "Global Sample", * "http://sample.avencia.com/servlet/com.esri.esrimap.Esrimap", * { * service: "OpenLayers_Sample", * layers: [ * // layers to manipulate * {id: "1", visible: true} * ] * } * ); * (end) * * Parameters: * name - {String} A name for the layer * url - {String} Base url for the ArcIMS server * options - {Object} Optional object with properties to be set on the * layer. */ initialize: function(name, url, options) { this.tileSize = new OpenLayers.Size(512, 512); // parameters this.params = OpenLayers.Util.applyDefaults( {ServiceName: options.serviceName}, this.DEFAULT_PARAMS ); this.options = OpenLayers.Util.applyDefaults( options, this.DEFAULT_OPTIONS ); OpenLayers.Layer.Grid.prototype.initialize.apply( this, [name, url, this.params, options] ); //layer is transparent if (this.transparent) { // unless explicitly set in options, make layer an overlay if (!this.isBaseLayer) { this.isBaseLayer = false; } // jpegs can never be transparent, so intelligently switch the // format, depending on the browser's capabilities if (this.format == "image/jpeg") { this.format = OpenLayers.Util.alphaHack() ? "image/gif" : "image/png"; } } // create an empty layer list if no layers specified in the options if (this.options.layers === null) { this.options.layers = []; } }, /** * Method: getURL * Return an image url this layer. * * Parameters: * bounds - {<OpenLayers.Bounds>} A bounds representing the bbox for the * request. * * Returns: * {String} A string with the map image's url. */ getURL: function(bounds) { var url = ""; bounds = this.adjustBounds(bounds); // create an arcxml request to generate the image var axlReq = new OpenLayers.Format.ArcXML( OpenLayers.Util.extend(this.options, { requesttype: "image", envelope: bounds.toArray(), tileSize: this.tileSize }) ); // create a synchronous ajax request to get an arcims image var req = new OpenLayers.Request.POST({ url: this.getFullRequestString(), data: axlReq.write(), async: false }); // if the response exists if (req != null) { var doc = req.responseXML; if (!doc || !doc.documentElement) { doc = req.responseText; } // create a new arcxml format to read the response var axlResp = new OpenLayers.Format.ArcXML(); var arcxml = axlResp.read(doc); url = this.getUrlOrImage(arcxml.image.output); } return url; }, /** * Method: getURLasync * Get an image url this layer asynchronously, and execute a callback * when the image url is generated. * * Parameters: * bounds - {<OpenLayers.Bounds>} A bounds representing the bbox for the * request. * callback - {Function} Function to call when image url is retrieved. * scope - {Object} The scope of the callback method. */ getURLasync: function(bounds, callback, scope) { bounds = this.adjustBounds(bounds); // create an arcxml request to generate the image var axlReq = new OpenLayers.Format.ArcXML( OpenLayers.Util.extend(this.options, { requesttype: "image", envelope: bounds.toArray(), tileSize: this.tileSize }) ); // create an asynchronous ajax request to get an arcims image OpenLayers.Request.POST({ url: this.getFullRequestString(), async: true, data: axlReq.write(), callback: function(req) { // process the response from ArcIMS, and call the callback function // to set the image URL var doc = req.responseXML; if (!doc || !doc.documentElement) { doc = req.responseText; } // create a new arcxml format to read the response var axlResp = new OpenLayers.Format.ArcXML(); var arcxml = axlResp.read(doc); callback.call(scope, this.getUrlOrImage(arcxml.image.output)); }, scope: this }); }, /** * Method: getUrlOrImage * Extract a url or image from the ArcXML image output. * * Parameters: * output - {Object} The image.output property of the object returned from * the ArcXML format read method. * * Returns: * {String} A URL for an image (potentially with the data protocol). */ getUrlOrImage: function(output) { var ret = ""; if(output.url) { // If the image response output url is a string, then the image // data is not inline. ret = output.url; } else if(output.data) { // The image data is inline and base64 encoded, create a data // url for the image. This will only work for small images, // due to browser url length limits. ret = "data:image/" + output.type + ";base64," + output.data; } return ret; }, /** * Method: setLayerQuery * Set the query definition on this layer. Query definitions are used to * render parts of the spatial data in an image, and can be used to * filter features or layers in the ArcIMS service. * * Parameters: * id - {String} The ArcIMS layer ID. * querydef - {Object} The query definition to apply to this layer. */ setLayerQuery: function(id, querydef) { // find the matching layer, if it exists for (var lyr = 0; lyr < this.options.layers.length; lyr++) { if (id == this.options.layers[lyr].id) { // replace this layer definition this.options.layers[lyr].query = querydef; return; } } // no layer found, create a new definition this.options.layers.push({id: id, visible: true, query: querydef}); }, /** * Method: getFeatureInfo * Get feature information from ArcIMS. Using the applied geometry, apply * the options to the query (buffer, area/envelope intersection), and * query the ArcIMS service. * * A note about accuracy: * ArcIMS interprets the accuracy attribute in feature requests to be * something like the 'modulus' operator on feature coordinates, * applied to the database geometry of the feature. It doesn't round, * so your feature coordinates may be up to (1 x accuracy) offset from * the actual feature coordinates. If the accuracy of the layer is not * specified, the accuracy will be computed to be approximately 1 * feature coordinate per screen pixel. * * Parameters: * geometry - {<OpenLayers.LonLat>} or {<OpenLayers.Geometry.Polygon>} The * geometry to use when making the query. This should be a closed * polygon for behavior approximating a free selection. * layer - {Object} The ArcIMS layer definition. This is an anonymous object * that looks like: * (code) * { * id: "ArcXML layer ID", // the ArcXML layer ID * query: { * where: "STATE = 'PA'", // the where clause of the query * accuracy: 100 // the accuracy of the returned feature * } * } * (end) * options - {Object} Object with non-default properties to set on the layer. * Supported properties are buffer, callback, scope, and any other * properties applicable to the ArcXML format. Set the 'callback' and * 'scope' for an object and function to recieve the parsed features * from ArcIMS. */ getFeatureInfo: function(geometry, layer, options) { // set the buffer to 1 unit (dd/m/ft?) by default var buffer = options.buffer || 1; // empty callback by default var callback = options.callback || function() {}; // default scope is window (global) var scope = options.scope || window; // apply these option to the request options var requestOptions = {}; OpenLayers.Util.extend(requestOptions, this.options); // this is a feature request requestOptions.requesttype = "feature"; if (geometry instanceof OpenLayers.LonLat) { // create an envelope if the geometry is really a lon/lat requestOptions.polygon = null; requestOptions.envelope = [ geometry.lon - buffer, geometry.lat - buffer, geometry.lon + buffer, geometry.lat + buffer ]; } else if (geometry instanceof OpenLayers.Geometry.Polygon) { // use the polygon assigned, and empty the envelope requestOptions.envelope = null; requestOptions.polygon = geometry; } // create an arcxml request to get feature requests var arcxml = new OpenLayers.Format.ArcXML(requestOptions); // apply any get feature options to the arcxml request OpenLayers.Util.extend(arcxml.request.get_feature, options); arcxml.request.get_feature.layer = layer.id; if (typeof layer.query.accuracy == "number") { // set the accuracy if it was specified arcxml.request.get_feature.query.accuracy = layer.query.accuracy; } else { // guess that the accuracy is 1 per screen pixel var mapCenter = this.map.getCenter(); var viewPx = this.map.getViewPortPxFromLonLat(mapCenter); viewPx.x++; var mapOffCenter = this.map.getLonLatFromPixel(viewPx); arcxml.request.get_feature.query.accuracy = mapOffCenter.lon - mapCenter.lon; } // set the get_feature query to be the same as the layer passed in arcxml.request.get_feature.query.where = layer.query.where; // use area_intersection arcxml.request.get_feature.query.spatialfilter.relation = "area_intersection"; // create a new asynchronous request to get the feature info OpenLayers.Request.POST({ url: this.getFullRequestString({'CustomService': 'Query'}), data: arcxml.write(), callback: function(request) { // parse the arcxml response var response = arcxml.parseResponse(request.responseText); if (!arcxml.iserror()) { // if the arcxml is not an error, call the callback with the features parsed callback.call(scope, response.features); } else { // if the arcxml is an error, return null features selected callback.call(scope, null); } } }); }, /** * Method: clone * Create a clone of this layer * * Returns: * {<OpenLayers.Layer.ArcIMS>} An exact clone of this layer */ clone: function (obj) { if (obj == null) { obj = new OpenLayers.Layer.ArcIMS(this.name, this.url, 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; }, CLASS_NAME: "OpenLayers.Layer.ArcIMS" }); /* ====================================================================== OpenLayers/Layer/Zoomify.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. */ /* * Development supported by a R&D grant DC08P02OUK006 - Old Maps Online * (www.oldmapsonline.org) from Ministry of Culture of the Czech Republic. */ /** * @requires OpenLayers/Layer/Grid.js */ /** * Class: OpenLayers.Layer.Zoomify * * Inherits from: * - <OpenLayers.Layer.Grid> */ OpenLayers.Layer.Zoomify = OpenLayers.Class(OpenLayers.Layer.Grid, { /** * Property: size * {<OpenLayers.Size>} The Zoomify image size in pixels. */ size: null, /** * APIProperty: isBaseLayer * {Boolean} */ isBaseLayer: true, /** * Property: standardTileSize * {Integer} The size of a standard (non-border) square tile in pixels. */ standardTileSize: 256, /** * Property: tileOriginCorner * {String} This layer uses top-left as tile origin **/ tileOriginCorner: "tl", /** * Property: numberOfTiers * {Integer} Depth of the Zoomify pyramid, number of tiers (zoom levels) * - filled during Zoomify pyramid initialization. */ numberOfTiers: 0, /** * Property: tileCountUpToTier * {Array(Integer)} Number of tiles up to the given tier of pyramid. * - filled during Zoomify pyramid initialization. */ tileCountUpToTier: null, /** * Property: tierSizeInTiles * {Array(<OpenLayers.Size>)} Size (in tiles) for each tier of pyramid. * - filled during Zoomify pyramid initialization. */ tierSizeInTiles: null, /** * Property: tierImageSize * {Array(<OpenLayers.Size>)} Image size in pixels for each pyramid tier. * - filled during Zoomify pyramid initialization. */ tierImageSize: null, /** * Constructor: OpenLayers.Layer.Zoomify * * Parameters: * name - {String} A name for the layer. * url - {String} - Relative or absolute path to the image or more * precisly to the TileGroup[X] directories root. * Flash plugin use the variable name "zoomifyImagePath" for this. * size - {<OpenLayers.Size>} The size (in pixels) of the image. * options - {Object} Hashtable of extra options to tag onto the layer */ initialize: function(name, url, size, options) { // initilize the Zoomify pyramid for given size this.initializeZoomify(size); OpenLayers.Layer.Grid.prototype.initialize.apply(this, [ name, url, size, {}, options ]); }, /** * Method: initializeZoomify * It generates constants for all tiers of the Zoomify pyramid * * Parameters: * size - {<OpenLayers.Size>} The size of the image in pixels * */ initializeZoomify: function( size ) { var imageSize = size.clone(); this.size = size.clone(); var tiles = new OpenLayers.Size( Math.ceil( imageSize.w / this.standardTileSize ), Math.ceil( imageSize.h / this.standardTileSize ) ); this.tierSizeInTiles = [tiles]; this.tierImageSize = [imageSize]; while (imageSize.w > this.standardTileSize || imageSize.h > this.standardTileSize ) { imageSize = new OpenLayers.Size( Math.floor( imageSize.w / 2 ), Math.floor( imageSize.h / 2 ) ); tiles = new OpenLayers.Size( Math.ceil( imageSize.w / this.standardTileSize ), Math.ceil( imageSize.h / this.standardTileSize ) ); this.tierSizeInTiles.push( tiles ); this.tierImageSize.push( imageSize ); } this.tierSizeInTiles.reverse(); this.tierImageSize.reverse(); this.numberOfTiers = this.tierSizeInTiles.length; var resolutions = [1]; this.tileCountUpToTier = [0]; for (var i = 1; i < this.numberOfTiers; i++) { resolutions.unshift(Math.pow(2, i)); this.tileCountUpToTier.push( this.tierSizeInTiles[i-1].w * this.tierSizeInTiles[i-1].h + this.tileCountUpToTier[i-1] ); } if (!this.serverResolutions) { this.serverResolutions = resolutions; } }, /** * APIMethod:destroy */ destroy: function() { // for now, nothing special to do here. OpenLayers.Layer.Grid.prototype.destroy.apply(this, arguments); // Remove from memory the Zoomify pyramid - is that enough? this.tileCountUpToTier.length = 0; this.tierSizeInTiles.length = 0; this.tierImageSize.length = 0; }, /** * APIMethod: clone * * Parameters: * obj - {Object} * * Returns: * {<OpenLayers.Layer.Zoomify>} An exact clone of this <OpenLayers.Layer.Zoomify> */ clone: function (obj) { if (obj == null) { obj = new OpenLayers.Layer.Zoomify(this.name, this.url, this.size, this.options); } //get all additions from superclasses obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); // copy/set any non-init, non-simple values here return obj; }, /** * Method: getURL * * Parameters: * bounds - {<OpenLayers.Bounds>} * * Returns: * {String} A string with the layer's url and parameters and also the * passed-in bounds and appropriate tile size specified as * parameters */ getURL: function (bounds) { bounds = this.adjustBounds(bounds); var res = this.getServerResolution(); var x = Math.round((bounds.left - this.tileOrigin.lon) / (res * this.tileSize.w)); var y = Math.round((this.tileOrigin.lat - bounds.top) / (res * this.tileSize.h)); var z = this.getZoomForResolution( res ); var tileIndex = x + y * this.tierSizeInTiles[z].w + this.tileCountUpToTier[z]; var path = "TileGroup" + Math.floor( (tileIndex) / 256 ) + "/" + z + "-" + x + "-" + y + ".jpg"; var url = this.url; if (OpenLayers.Util.isArray(url)) { url = this.selectUrl(path, url); } return url + path; }, /** * Method: getImageSize * getImageSize returns size for a particular tile. If bounds are given as * first argument, size is calculated (bottom-right tiles are non square). * */ getImageSize: function() { if (arguments.length > 0) { var bounds = this.adjustBounds(arguments[0]); var res = this.getServerResolution(); var x = Math.round((bounds.left - this.tileOrigin.lon) / (res * this.tileSize.w)); var y = Math.round((this.tileOrigin.lat - bounds.top) / (res * this.tileSize.h)); var z = this.getZoomForResolution( res ); var w = this.standardTileSize; var h = this.standardTileSize; if (x == this.tierSizeInTiles[z].w -1 ) { var w = this.tierImageSize[z].w % this.standardTileSize; } if (y == this.tierSizeInTiles[z].h -1 ) { var h = this.tierImageSize[z].h % this.standardTileSize; } return (new OpenLayers.Size(w, h)); } else { return this.tileSize; } }, /** * APIMethod: setMap * When the layer is added to a map, then we can fetch our origin * (if we don't have one.) * * Parameters: * map - {<OpenLayers.Map>} */ setMap: function(map) { OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments); this.tileOrigin = new OpenLayers.LonLat(this.map.maxExtent.left, this.map.maxExtent.top); }, CLASS_NAME: "OpenLayers.Layer.Zoomify" }); /* ====================================================================== 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/Markers.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.Markers * * Inherits from: * - <OpenLayers.Layer> */ OpenLayers.Layer.Markers = OpenLayers.Class(OpenLayers.Layer, { /** * APIProperty: isBaseLayer * {Boolean} Markers layer is never a base layer. */ isBaseLayer: false, /** * APIProperty: markers * {Array(<OpenLayers.Marker>)} internal marker list */ markers: null, /** * Property: drawn * {Boolean} internal state of drawing. This is a workaround for the fact * that the map does not call moveTo with a zoomChanged when the map is * first starting up. This lets us catch the case where we have *never* * drawn the layer, and draw it even if the zoom hasn't changed. */ drawn: false, /** * Constructor: OpenLayers.Layer.Markers * Create a Markers layer. * * Parameters: * name - {String} * options - {Object} Hashtable of extra options to tag onto the layer */ initialize: function(name, options) { OpenLayers.Layer.prototype.initialize.apply(this, arguments); this.markers = []; }, /** * APIMethod: destroy */ destroy: function() { this.clearMarkers(); this.markers = null; OpenLayers.Layer.prototype.destroy.apply(this, arguments); }, /** * APIMethod: setOpacity * Sets the opacity for all the markers. * * Parameters: * opacity - {Float} */ setOpacity: function(opacity) { if (opacity != this.opacity) { this.opacity = opacity; for (var i=0, len=this.markers.length; i<len; i++) { this.markers[i].setOpacity(this.opacity); } } }, /** * Method: moveTo * * Parameters: * bounds - {<OpenLayers.Bounds>} * zoomChanged - {Boolean} * dragging - {Boolean} */ moveTo:function(bounds, zoomChanged, dragging) { OpenLayers.Layer.prototype.moveTo.apply(this, arguments); if (zoomChanged || !this.drawn) { for(var i=0, len=this.markers.length; i<len; i++) { this.drawMarker(this.markers[i]); } this.drawn = true; } }, /** * APIMethod: addMarker * * Parameters: * marker - {<OpenLayers.Marker>} */ addMarker: function(marker) { this.markers.push(marker); if (this.opacity < 1) { marker.setOpacity(this.opacity); } if (this.map && this.map.getExtent()) { marker.map = this.map; this.drawMarker(marker); } }, /** * APIMethod: removeMarker * * Parameters: * marker - {<OpenLayers.Marker>} */ removeMarker: function(marker) { if (this.markers && this.markers.length) { OpenLayers.Util.removeItem(this.markers, marker); marker.erase(); } }, /** * Method: clearMarkers * This method removes all markers from a layer. The markers are not * destroyed by this function, but are removed from the list of markers. */ clearMarkers: function() { if (this.markers != null) { while(this.markers.length > 0) { this.removeMarker(this.markers[0]); } } }, /** * Method: drawMarker * Calculate the pixel location for the marker, create it, and * add it to the layer's div * * Parameters: * marker - {<OpenLayers.Marker>} */ drawMarker: function(marker) { var px = this.map.getLayerPxFromLonLat(marker.lonlat); if (px == null) { marker.display(false); } else { if (!marker.isDrawn()) { var markerImg = marker.draw(px); this.div.appendChild(markerImg); } else if(marker.icon) { marker.icon.moveTo(px); } } }, /** * APIMethod: getDataExtent * Calculates the max extent which includes all of the markers. * * Returns: * {<OpenLayers.Bounds>} */ getDataExtent: function () { var maxExtent = null; if ( this.markers && (this.markers.length > 0)) { var maxExtent = new OpenLayers.Bounds(); for(var i=0, len=this.markers.length; i<len; i++) { var marker = this.markers[i]; maxExtent.extend(marker.lonlat); } } return maxExtent; }, CLASS_NAME: "OpenLayers.Layer.Markers" }); /* ====================================================================== OpenLayers/Layer/Boxes.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/Layer/Markers.js */ /** * Class: OpenLayers.Layer.Boxes * Draw divs as 'boxes' on the layer. * * Inherits from: * - <OpenLayers.Layer.Markers> */ OpenLayers.Layer.Boxes = OpenLayers.Class(OpenLayers.Layer.Markers, { /** * Constructor: OpenLayers.Layer.Boxes * * Parameters: * name - {String} * options - {Object} Hashtable of extra options to tag onto the layer */ /** * Method: drawMarker * Calculate the pixel location for the marker, create it, and * add it to the layer's div * * Parameters: * marker - {<OpenLayers.Marker.Box>} */ drawMarker: function(marker) { var topleft = this.map.getLayerPxFromLonLat({ lon: marker.bounds.left, lat: marker.bounds.top }); var botright = this.map.getLayerPxFromLonLat({ lon: marker.bounds.right, lat: marker.bounds.bottom }); if (botright == null || topleft == null) { marker.display(false); } else { var markerDiv = marker.draw(topleft, { w: Math.max(1, botright.x - topleft.x), h: Math.max(1, botright.y - topleft.y) }); if (!marker.drawn) { this.div.appendChild(markerDiv); marker.drawn = true; } } }, /** * APIMethod: removeMarker * * Parameters: * marker - {<OpenLayers.Marker.Box>} */ removeMarker: function(marker) { OpenLayers.Util.removeItem(this.markers, marker); if ((marker.div != null) && (marker.div.parentNode == this.div) ) { this.div.removeChild(marker.div); } }, CLASS_NAME: "OpenLayers.Layer.Boxes" }); /* ====================================================================== OpenLayers/Layer/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/Layer.js * @requires OpenLayers/Tile/Image.js */ /** * Class: OpenLayers.Layer.Image * Instances of OpenLayers.Layer.Image are used to display data from a web * accessible image as a map layer. Create a new image layer with the * <OpenLayers.Layer.Image> constructor. * * Inherits from: * - <OpenLayers.Layer> */ OpenLayers.Layer.Image = OpenLayers.Class(OpenLayers.Layer, { /** * Property: isBaseLayer * {Boolean} The layer is a base layer. Default is true. Set this property * in the layer options */ isBaseLayer: true, /** * Property: url * {String} URL of the image to use */ url: null, /** * Property: extent * {<OpenLayers.Bounds>} The image bounds in map units. This extent will * also be used as the default maxExtent for the layer. If you wish * to have a maxExtent that is different than the image extent, set the * maxExtent property of the options argument (as with any other layer). */ extent: null, /** * Property: size * {<OpenLayers.Size>} The image size in pixels */ size: null, /** * Property: tile * {<OpenLayers.Tile.Image>} */ tile: null, /** * Property: aspectRatio * {Float} The ratio of height/width represented by a single pixel in the * graphic */ aspectRatio: null, /** * Constructor: OpenLayers.Layer.Image * Create a new image layer * * Parameters: * name - {String} A name for the layer. * url - {String} Relative or absolute path to the image * extent - {<OpenLayers.Bounds>} The extent represented by the image * size - {<OpenLayers.Size>} The size (in pixels) of the image * options - {Object} Hashtable of extra options to tag onto the layer */ initialize: function(name, url, extent, size, options) { this.url = url; this.extent = extent; this.maxExtent = extent; this.size = size; OpenLayers.Layer.prototype.initialize.apply(this, [name, options]); this.aspectRatio = (this.extent.getHeight() / this.size.h) / (this.extent.getWidth() / this.size.w); }, /** * Method: destroy * Destroy this layer */ destroy: function() { if (this.tile) { this.removeTileMonitoringHooks(this.tile); this.tile.destroy(); this.tile = null; } OpenLayers.Layer.prototype.destroy.apply(this, arguments); }, /** * Method: clone * Create a clone of this layer * * Paramters: * obj - {Object} An optional layer (is this ever used?) * * Returns: * {<OpenLayers.Layer.Image>} An exact copy of this layer */ clone: function(obj) { if(obj == null) { obj = new OpenLayers.Layer.Image(this.name, this.url, this.extent, this.size, this.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: setMap * * Parameters: * map - {<OpenLayers.Map>} */ setMap: function(map) { /** * If nothing to do with resolutions has been set, assume a single * resolution determined by ratio*extent/size - if an image has a * pixel aspect ratio different than one (as calculated above), the * image will be stretched in one dimension only. */ if( this.options.maxResolution == null ) { this.options.maxResolution = this.aspectRatio * this.extent.getWidth() / this.size.w; } OpenLayers.Layer.prototype.setMap.apply(this, arguments); }, /** * Method: moveTo * Create the tile for the image or resize it for the new resolution * * Parameters: * bounds - {<OpenLayers.Bounds>} * zoomChanged - {Boolean} * dragging - {Boolean} */ moveTo:function(bounds, zoomChanged, dragging) { OpenLayers.Layer.prototype.moveTo.apply(this, arguments); var firstRendering = (this.tile == null); if(zoomChanged || firstRendering) { //determine new tile size this.setTileSize(); //determine new position (upper left corner of new bounds) var ulPx = this.map.getLayerPxFromLonLat({ lon: this.extent.left, lat: this.extent.top }); if(firstRendering) { //create the new tile this.tile = new OpenLayers.Tile.Image(this, ulPx, this.extent, null, this.tileSize); this.addTileMonitoringHooks(this.tile); } else { //just resize the tile and set it's new position this.tile.size = this.tileSize.clone(); this.tile.position = ulPx.clone(); } this.tile.draw(); } }, /** * Set the tile size based on the map size. */ setTileSize: function() { var tileWidth = this.extent.getWidth() / this.map.getResolution(); var tileHeight = this.extent.getHeight() / this.map.getResolution(); this.tileSize = new OpenLayers.Size(tileWidth, tileHeight); }, /** * 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) { tile.onLoadStart = function() { this.events.triggerEvent("loadstart"); }; tile.events.register("loadstart", this, tile.onLoadStart); tile.onLoadEnd = function() { this.events.triggerEvent("loadend"); }; tile.events.register("loadend", this, tile.onLoadEnd); tile.events.register("unload", this, tile.onLoadEnd); }, /** * Method: removeTileMonitoringHooks * This function takes a tile as input and removes the tile hooks * that were added in <addTileMonitoringHooks>. * * Parameters: * tile - {<OpenLayers.Tile>} */ removeTileMonitoringHooks: function(tile) { tile.unload(); tile.events.un({ "loadstart": tile.onLoadStart, "loadend": tile.onLoadEnd, "unload": tile.onLoadEnd, scope: this }); }, /** * APIMethod: setUrl * * Parameters: * newUrl - {String} */ setUrl: function(newUrl) { this.url = newUrl; this.tile.draw(); }, /** * APIMethod: getURL * The url we return is always the same (the image itself never changes) * so we can ignore the bounds parameter (it will always be the same, * anyways) * * Parameters: * bounds - {<OpenLayers.Bounds>} */ getURL: function(bounds) { return this.url; }, CLASS_NAME: "OpenLayers.Layer.Image" }); /* ====================================================================== OpenLayers/Layer/OSM.js ====================================================================== */ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for * full list of contributors). Published under the 2-clause BSD license. * See license.txt in the OpenLayers distribution or repository for the * full text of the license. */ /** * @requires OpenLayers/Layer/XYZ.js */ /** * Class: OpenLayers.Layer.OSM * This layer allows accessing OpenStreetMap tiles. By default the OpenStreetMap * hosted tile.openstreetmap.org Mapnik tileset is used. If you wish to use * a different layer instead, you need to provide a different * URL to the constructor. Here's an example for using OpenCycleMap: * * (code) * new OpenLayers.Layer.OSM("OpenCycleMap", * ["http://a.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png", * "http://b.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png", * "http://c.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png"]); * (end) * * Inherits from: * - <OpenLayers.Layer.XYZ> */ OpenLayers.Layer.OSM = OpenLayers.Class(OpenLayers.Layer.XYZ, { /** * APIProperty: name * {String} The layer name. Defaults to "OpenStreetMap" if the first * argument to the constructor is null or undefined. */ name: "OpenStreetMap", /** * APIProperty: url * {String} The tileset URL scheme. Defaults to * : http://[a|b|c].tile.openstreetmap.org/${z}/${x}/${y}.png * (the official OSM tileset) if the second argument to the constructor * is null or undefined. To use another tileset you can have something * like this: * (code) * new OpenLayers.Layer.OSM("OpenCycleMap", * ["http://a.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png", * "http://b.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png", * "http://c.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png"]); * (end) */ url: [ 'http://a.tile.openstreetmap.org/${z}/${x}/${y}.png', 'http://b.tile.openstreetmap.org/${z}/${x}/${y}.png', 'http://c.tile.openstreetmap.org/${z}/${x}/${y}.png' ], /** * Property: attribution * {String} The layer attribution. */ attribution: "© <a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors", /** * Property: sphericalMercator * {Boolean} */ sphericalMercator: true, /** * Property: wrapDateLine * {Boolean} */ wrapDateLine: true, /** APIProperty: tileOptions * {Object} optional configuration options for <OpenLayers.Tile> instances * created by this Layer. Default is * * (code) * {crossOriginKeyword: 'anonymous'} * (end) * * When using OSM tilesets other than the default ones, it may be * necessary to set this to * * (code) * {crossOriginKeyword: null} * (end) * * if the server does not send Access-Control-Allow-Origin headers. */ tileOptions: null, /** * Constructor: OpenLayers.Layer.OSM * * Parameters: * name - {String} The layer name. * url - {String} The tileset URL scheme. * options - {Object} Configuration options for the layer. Any inherited * layer option can be set in this object (e.g. * <OpenLayers.Layer.Grid.buffer>). */ initialize: function(name, url, options) { OpenLayers.Layer.XYZ.prototype.initialize.apply(this, arguments); this.tileOptions = OpenLayers.Util.extend({ crossOriginKeyword: 'anonymous' }, this.options && this.options.tileOptions); }, /** * Method: clone */ clone: function(obj) { if (obj == null) { obj = new OpenLayers.Layer.OSM( this.name, this.url, this.getOptions()); } obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]); return obj; }, CLASS_NAME: "OpenLayers.Layer.OSM" }); /* ====================================================================== OpenLayers/Layer/TMS.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.TMS * Create a layer for accessing tiles from services that conform with the * Tile Map Service Specification * (http://wiki.osgeo.org/wiki/Tile_Map_Service_Specification). * * Example: * (code) * var layer = new OpenLayers.Layer.TMS( * "My Layer", // name for display in LayerSwitcher * "http://tilecache.osgeo.org/wms-c/Basic.py/", // service endpoint * {layername: "basic", type: "png"} // required properties * ); * (end) * * Inherits from: * - <OpenLayers.Layer.Grid> */ OpenLayers.Layer.TMS = OpenLayers.Class(OpenLayers.Layer.Grid, { /** * APIProperty: serviceVersion * {String} Service version for tile requests. Default is "1.0.0". */ serviceVersion: "1.0.0", /** * APIProperty: layername * {String} The identifier for the <TileMap> as advertised by the service. * For example, if the service advertises a <TileMap> with * 'href="http://tms.osgeo.org/1.0.0/vmap0"', the <layername> property * would be set to "vmap0". */ layername: null, /** * APIProperty: type * {String} The format extension corresponding to the requested tile image * type. This is advertised in a <TileFormat> element as the * "extension" attribute. For example, if the service advertises a * <TileMap> with <TileFormat width="256" height="256" mime-type="image/jpeg" extension="jpg" />, * the <type> property would be set to "jpg". */ type: null, /** * APIProperty: isBaseLayer * {Boolean} Make this layer a base layer. Default is true. Set false to * use the layer as an overlay. */ isBaseLayer: true, /** * 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 bottom-left * corner of the map's <maxExtent>. Default is ``null``. * * Example: * (code) * var layer = new OpenLayers.Layer.TMS( * "My Layer", * "http://tilecache.osgeo.org/wms-c/Basic.py/", * { * layername: "basic", * type: "png", * // set if different than the bottom left of map.maxExtent * tileOrigin: new OpenLayers.LonLat(-180, -90) * } * ); * (end) */ tileOrigin: null, /** * 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, /** * 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, /** * Constructor: OpenLayers.Layer.TMS * * Parameters: * name - {String} Title to be displayed in a <OpenLayers.Control.LayerSwitcher> * url - {String} Service endpoint (without the version number). E.g. * "http://tms.osgeo.org/". * options - {Object} Additional properties to be set on the layer. The * <layername> and <type> properties must be set here. */ initialize: function(name, url, options) { var newArguments = []; newArguments.push(name, url, {}, options); OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments); }, /** * APIMethod: clone * Create a complete copy of this layer. * * Parameters: * obj - {Object} Should only be provided by subclasses that call this * method. * * Returns: * {<OpenLayers.Layer.TMS>} An exact clone of this <OpenLayers.Layer.TMS> */ clone: function (obj) { if (obj == null) { obj = new OpenLayers.Layer.TMS(this.name, this.url, this.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; }, /** * Method: getURL * * Parameters: * bounds - {<OpenLayers.Bounds>} * * Returns: * {String} A string with the layer's url and parameters and also the * passed-in bounds and appropriate tile size specified as * parameters */ getURL: function (bounds) { bounds = this.adjustBounds(bounds); var res = this.getServerResolution(); var x = Math.round((bounds.left - this.tileOrigin.lon) / (res * this.tileSize.w)); var y = Math.round((bounds.bottom - this.tileOrigin.lat) / (res * this.tileSize.h)); var z = this.getServerZoom(); var path = this.serviceVersion + "/" + this.layername + "/" + z + "/" + x + "/" + y + "." + this.type; var url = this.url; if (OpenLayers.Util.isArray(url)) { url = this.selectUrl(path, url); } return url + path; }, /** * Method: setMap * When the layer is added to a map, then we can fetch our origin * (if we don't have one.) * * Parameters: * map - {<OpenLayers.Map>} */ setMap: function(map) { OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments); if (!this.tileOrigin) { this.tileOrigin = new OpenLayers.LonLat(this.map.maxExtent.left, this.map.maxExtent.bottom); } }, CLASS_NAME: "OpenLayers.Layer.TMS" }); /* ====================================================================== OpenLayers/Layer/MapServer.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.MapServer * Instances of OpenLayers.Layer.MapServer are used to display * data from a MapServer CGI instance. * * Inherits from: * - <OpenLayers.Layer.Grid> */ OpenLayers.Layer.MapServer = OpenLayers.Class(OpenLayers.Layer.Grid, { /** * Constant: DEFAULT_PARAMS * {Object} Hashtable of default parameter key/value pairs */ DEFAULT_PARAMS: { mode: "map", map_imagetype: "png" }, /** * Constructor: OpenLayers.Layer.MapServer * Create a new MapServer layer object * * Parameters: * name - {String} A name for the layer * url - {String} Base url for the MapServer CGI * (e.g. http://www2.dmsolutions.ca/cgi-bin/mapserv) * 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 */ initialize: function(name, url, params, options) { OpenLayers.Layer.Grid.prototype.initialize.apply(this, arguments); this.params = OpenLayers.Util.applyDefaults( this.params, this.DEFAULT_PARAMS ); // unless explicitly set in options, if the layer is transparent, // it will be an overlay if (options == null || options.isBaseLayer == null) { this.isBaseLayer = ((this.params.transparent != "true") && (this.params.transparent != true)); } }, /** * Method: clone * Create a clone of this layer * * Returns: * {<OpenLayers.Layer.MapServer>} An exact clone of this layer */ clone: function (obj) { if (obj == null) { obj = new OpenLayers.Layer.MapServer(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; }, /** * Method: getURL * Return a 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); // Make a list, so that getFullRequestString uses literal "," var extent = [bounds.left, bounds. bottom, bounds.right, bounds.top]; var imageSize = this.getImageSize(); // make lists, so that literal ','s are used var url = this.getFullRequestString( {mapext: extent, imgext: extent, map_size: [imageSize.w, imageSize.h], imgx: imageSize.w / 2, imgy: imageSize.h / 2, imgxy: [imageSize.w, imageSize.h] }); return url; }, /** * Method: getFullRequestString * combine the layer's url with its params and these newParams. * * Parameters: * newParams - {Object} New parameters that should be added to the * request string. * altUrl - {String} (optional) Replace the URL in the full request * string with the provided URL. * * Returns: * {String} A string with the layer's url and parameters embedded in it. */ getFullRequestString:function(newParams, altUrl) { // use layer's url unless altUrl passed in var url = (altUrl == null) ? this.url : altUrl; // 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); // requestString always starts with url var requestString = url; // MapServer needs '+' seperating things like bounds/height/width. // Since typically this is URL encoded, we use a slight hack: we // depend on the list-like functionality of getParameterString to // leave ',' only in the case of list items (since otherwise it is // encoded) then do a regular expression replace on the , characters // to '+' // paramsString = paramsString.replace(/,/g, "+"); if (paramsString != "") { var lastServerChar = url.charAt(url.length - 1); if ((lastServerChar == "&") || (lastServerChar == "?")) { requestString += paramsString; } else { if (url.indexOf('?') == -1) { //serverPath has no ? -- add one requestString += '?' + paramsString; } else { //serverPath contains ?, so must already have paramsString at the end requestString += '&' + paramsString; } } } return requestString; }, CLASS_NAME: "OpenLayers.Layer.MapServer" }); /* ====================================================================== OpenLayers/Layer/FixedZoomLevels.js ====================================================================== */ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for * full list of contributors). Published under the 2-clause BSD license. * See license.txt in the OpenLayers distribution or repository for the * full text of the license. */ /** * @requires OpenLayers/Layer.js */ /** * Class: OpenLayers.Layer.FixedZoomLevels * Some Layers will already have established zoom levels (like google * or ve). Instead of trying to determine them and populate a resolutions[] * Array with those values, we will hijack the resolution functionality * here. * * When you subclass FixedZoomLevels: * * The initResolutions() call gets nullified, meaning no resolutions[] array * is set up. Which would be a big problem getResolution() in Layer, since * it merely takes map.zoom and indexes into resolutions[]... but.... * * The getResolution() call is also overridden. Instead of using the * resolutions[] array, we simply calculate the current resolution based * on the current extent and the current map size. But how will we be able * to calculate the current extent without knowing the resolution...? * * The getExtent() function is also overridden. Instead of calculating extent * based on the center point and the current resolution, we instead * calculate the extent by getting the lonlats at the top-left and * bottom-right by using the getLonLatFromViewPortPx() translation function, * taken from the pixel locations (0,0) and the size of the map. But how * will we be able to do lonlat-px translation without resolution....? * * The getZoomForResolution() method is overridden. Instead of indexing into * the resolutions[] array, we call OpenLayers.Layer.getExent(), passing in * the desired resolution. With this extent, we then call getZoomForExtent() * * * Whenever you implement a layer using OpenLayers.Layer.FixedZoomLevels, * it is your responsibility to provide the following three functions: * * - getLonLatFromViewPortPx * - getViewPortPxFromLonLat * - getZoomForExtent * * ...those three functions should generally be provided by any reasonable * API that you might be working from. * */ OpenLayers.Layer.FixedZoomLevels = OpenLayers.Class({ /********************************************************/ /* */ /* Baselayer Functions */ /* */ /* The following functions must all be implemented */ /* by all base layers */ /* */ /********************************************************/ /** * Constructor: OpenLayers.Layer.FixedZoomLevels * Create a new fixed zoom levels layer. */ initialize: function() { //this class is only just to add the following functions... // nothing to actually do here... but it is probably a good // idea to have layers that use these functions call this // inititalize() anyways, in case at some point we decide we // do want to put some functionality or state in here. }, /** * Method: initResolutions * Populate the resolutions array */ initResolutions: function() { var props = ['minZoomLevel', 'maxZoomLevel', 'numZoomLevels']; for(var i=0, len=props.length; i<len; i++) { var property = props[i]; this[property] = (this.options[property] != null) ? this.options[property] : this.map[property]; } if ( (this.minZoomLevel == null) || (this.minZoomLevel < this.MIN_ZOOM_LEVEL) ){ this.minZoomLevel = this.MIN_ZOOM_LEVEL; } // // At this point, we know what the minimum desired zoom level is, and // we must calculate the total number of zoom levels. // // Because we allow for the setting of either the 'numZoomLevels' // or the 'maxZoomLevel' properties... on either the layer or the // map, we have to define some rules to see which we take into // account first in this calculation. // // The following is the precedence list for these properties: // // (1) numZoomLevels set on layer // (2) maxZoomLevel set on layer // (3) numZoomLevels set on map // (4) maxZoomLevel set on map* // (5) none of the above* // // *Note that options (4) and (5) are only possible if the user // _explicitly_ sets the 'numZoomLevels' property on the map to // null, since it is set by default to 16. // // // Note to future: In 3.0, I think we should remove the default // value of 16 for map.numZoomLevels. Rather, I think that value // should be set as a default on the Layer.WMS class. If someone // creates a 3rd party layer and does not specify any 'minZoomLevel', // 'maxZoomLevel', or 'numZoomLevels', and has not explicitly // specified any of those on the map object either.. then I think // it is fair to say that s/he wants all the zoom levels available. // // By making map.numZoomLevels *null* by default, that will be the // case. As it is, I don't feel comfortable changing that right now // as it would be a glaring API change and actually would probably // break many peoples' codes. // //the number of zoom levels we'd like to have. var desiredZoomLevels; //this is the maximum number of zoom levels the layer will allow, // given the specified starting minimum zoom level. var limitZoomLevels = this.MAX_ZOOM_LEVEL - this.minZoomLevel + 1; if ( ((this.options.numZoomLevels == null) && (this.options.maxZoomLevel != null)) // (2) || ((this.numZoomLevels == null) && (this.maxZoomLevel != null)) // (4) ) { //calculate based on specified maxZoomLevel (on layer or map) desiredZoomLevels = this.maxZoomLevel - this.minZoomLevel + 1; } else { //calculate based on specified numZoomLevels (on layer or map) // this covers cases (1) and (3) desiredZoomLevels = this.numZoomLevels; } if (desiredZoomLevels != null) { //Now that we know what we would *like* the number of zoom levels // to be, based on layer or map options, we have to make sure that // it does not conflict with the actual limit, as specified by // the constants on the layer itself (and calculated into the // 'limitZoomLevels' variable). this.numZoomLevels = Math.min(desiredZoomLevels, limitZoomLevels); } else { // case (5) -- neither 'numZoomLevels' not 'maxZoomLevel' was // set on either the layer or the map. So we just use the // maximum limit as calculated by the layer's constants. this.numZoomLevels = limitZoomLevels; } //now that the 'numZoomLevels' is appropriately, safely set, // we go back and re-calculate the 'maxZoomLevel'. this.maxZoomLevel = this.minZoomLevel + this.numZoomLevels - 1; if (this.RESOLUTIONS != null) { var resolutionsIndex = 0; this.resolutions = []; for(var i= this.minZoomLevel; i <= this.maxZoomLevel; i++) { this.resolutions[resolutionsIndex++] = this.RESOLUTIONS[i]; } this.maxResolution = this.resolutions[0]; this.minResolution = this.resolutions[this.resolutions.length - 1]; } }, /** * APIMethod: getResolution * Get the current map resolution * * Returns: * {Float} Map units per Pixel */ getResolution: function() { if (this.resolutions != null) { return OpenLayers.Layer.prototype.getResolution.apply(this, arguments); } else { var resolution = null; var viewSize = this.map.getSize(); var extent = this.getExtent(); if ((viewSize != null) && (extent != null)) { resolution = Math.max( extent.getWidth() / viewSize.w, extent.getHeight() / viewSize.h ); } return resolution; } }, /** * APIMethod: getExtent * Calculates using px-> lonlat translation functions on tl and br * corners of viewport * * Returns: * {<OpenLayers.Bounds>} A Bounds object which represents the lon/lat * bounds of the current viewPort. */ getExtent: function () { var size = this.map.getSize(); var tl = this.getLonLatFromViewPortPx({ x: 0, y: 0 }); var br = this.getLonLatFromViewPortPx({ x: size.w, y: size.h }); if ((tl != null) && (br != null)) { return new OpenLayers.Bounds(tl.lon, br.lat, br.lon, tl.lat); } else { return null; } }, /** * Method: getZoomForResolution * Get the zoom level for a given resolution * * Parameters: * resolution - {Float} * * Returns: * {Integer} A suitable zoom level for the specified resolution. * If no baselayer is set, returns null. */ getZoomForResolution: function(resolution) { if (this.resolutions != null) { return OpenLayers.Layer.prototype.getZoomForResolution.apply(this, arguments); } else { var extent = OpenLayers.Layer.prototype.getExtent.apply(this, []); return this.getZoomForExtent(extent); } }, /********************************************************/ /* */ /* Translation Functions */ /* */ /* The following functions translate GMaps and OL */ /* formats for Pixel, LonLat, Bounds, and Zoom */ /* */ /********************************************************/ // // TRANSLATION: MapObject Zoom <-> OpenLayers Zoom // /** * Method: getOLZoomFromMapObjectZoom * Get the OL zoom index from the map object zoom level * * Parameters: * moZoom - {Integer} * * Returns: * {Integer} An OpenLayers Zoom level, translated from the passed in zoom * Returns null if null value is passed in */ getOLZoomFromMapObjectZoom: function(moZoom) { var zoom = null; if (moZoom != null) { zoom = moZoom - this.minZoomLevel; if (this.map.baseLayer !== this) { zoom = this.map.baseLayer.getZoomForResolution( this.getResolutionForZoom(zoom) ); } } return zoom; }, /** * Method: getMapObjectZoomFromOLZoom * Get the map object zoom level from the OL zoom level * * Parameters: * olZoom - {Integer} * * Returns: * {Integer} A MapObject level, translated from the passed in olZoom * Returns null if null value is passed in */ getMapObjectZoomFromOLZoom: function(olZoom) { var zoom = null; if (olZoom != null) { zoom = olZoom + this.minZoomLevel; if (this.map.baseLayer !== this) { zoom = this.getZoomForResolution( this.map.baseLayer.getResolutionForZoom(zoom) ); } } return zoom; }, CLASS_NAME: "OpenLayers.Layer.FixedZoomLevels" }); /* ====================================================================== OpenLayers/Layer/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/Layer/KaMap.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.KaMap * * Inherits from: * - <OpenLayers.Layer.Grid> */ OpenLayers.Layer.KaMap = OpenLayers.Class(OpenLayers.Layer.Grid, { /** * APIProperty: isBaseLayer * {Boolean} KaMap Layer is always a base layer */ isBaseLayer: true, /** * Constant: DEFAULT_PARAMS * {Object} parameters set by default. The default parameters set * the format via the 'i' parameter to 'jpeg'. */ DEFAULT_PARAMS: { i: 'jpeg', map: '' }, /** * Constructor: OpenLayers.Layer.KaMap * * Parameters: * name - {String} * url - {String} * params - {Object} Parameters to be sent to the HTTP server in the * query string for the tile. The format can be set via the 'i' * parameter (defaults to jpg) , and the map should be set via * the 'map' parameter. It has been reported that ka-Map may behave * inconsistently if your format parameter does not match the format * parameter configured in your config.php. (See ticket #327 for more * information.) * options - {Object} Additional options for the layer. Any of the * APIProperties listed on this layer, and any layer types it * extends, can be overridden through the options parameter. */ initialize: function(name, url, params, options) { OpenLayers.Layer.Grid.prototype.initialize.apply(this, arguments); this.params = OpenLayers.Util.applyDefaults( this.params, this.DEFAULT_PARAMS ); }, /** * Method: getURL * * Parameters: * bounds - {<OpenLayers.Bounds>} * * Returns: * {String} A string with the layer's url and parameters and also the * passed-in bounds and appropriate tile size specified as * parameters */ getURL: function (bounds) { bounds = this.adjustBounds(bounds); var mapRes = this.map.getResolution(); var scale = Math.round((this.map.getScale() * 10000)) / 10000; var pX = Math.round(bounds.left / mapRes); var pY = -Math.round(bounds.top / mapRes); return this.getFullRequestString( { t: pY, l: pX, s: scale }); }, /** * Method: calculateGridLayout * ka-Map uses the center point of the map as an origin for * its tiles. Override calculateGridLayout to center tiles * correctly for this case. * * Parameters: * bounds - {<OpenLayers.Bound>} * origin - {<OpenLayers.LonLat>} * 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; var tilecol = Math.floor(offsetlon/tilelon) - this.buffer; var offsetlat = bounds.top; var tilerow = Math.floor(offsetlat/tilelat) + this.buffer; return { tilelon: tilelon, tilelat: tilelat, startcol: tilecol, startrow: tilerow }; }, /** * 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 minX = (tileLayout.startcol + col) * tilelon; var minY = (tileLayout.startrow - row) * tilelat; return new OpenLayers.Bounds( minX, minY, minX + tilelon, minY + tilelat ); }, /** * APIMethod: clone * * Parameters: * obj - {Object} * * Returns: * {<OpenLayers.Layer.Kamap>} An exact clone of this OpenLayers.Layer.KaMap */ clone: function (obj) { if (obj == null) { obj = new OpenLayers.Layer.KaMap(this.name, this.url, this.params, this.getOptions()); } //get all additions from superclasses obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); // copy/set any non-init, non-simple values here if (this.tileSize != null) { obj.tileSize = this.tileSize.clone(); } // we do not want to copy reference to grid, so we make a new array obj.grid = []; return obj; }, /** * APIMethod: getTileBounds * Returns The tile bounds for a layer given a pixel location. * * Parameters: * viewPortPx - {<OpenLayers.Pixel>} The location in the viewport. * * Returns: * {<OpenLayers.Bounds>} Bounds of the tile at the given pixel location. */ getTileBounds: function(viewPortPx) { var resolution = this.getResolution(); var tileMapWidth = resolution * this.tileSize.w; var tileMapHeight = resolution * this.tileSize.h; var mapPoint = this.getLonLatFromViewPortPx(viewPortPx); var tileLeft = tileMapWidth * Math.floor(mapPoint.lon / tileMapWidth); var tileBottom = tileMapHeight * Math.floor(mapPoint.lat / tileMapHeight); return new OpenLayers.Bounds(tileLeft, tileBottom, tileLeft + tileMapWidth, tileBottom + tileMapHeight); }, CLASS_NAME: "OpenLayers.Layer.KaMap" }); /* ====================================================================== OpenLayers/Layer/KaMapCache.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 * @requires OpenLayers/Layer/KaMap.js */ /** * Class: OpenLayers.Layer.KaMapCache * * This class is designed to talk directly to a web-accessible ka-Map * cache generated by the precache2.php script. * * To create a a new KaMapCache layer, you must indicate also the "i" parameter * (that will be used to calculate the file extension), and another special * parameter, object names "metaTileSize", with "h" (height) and "w" (width) * properties. * * // Create a new kaMapCache layer. * var kamap_base = new OpenLayers.Layer.KaMapCache( * "Satellite", * "http://www.example.org/web/acessible/cache", * {g: "satellite", map: "world", i: 'png24', metaTileSize: {w: 5, h: 5} } * ); * * // Create an kaMapCache overlay layer (using "isBaseLayer: false"). * // Forces the output to be a "gif", using the "i" parameter. * var kamap_overlay = new OpenLayers.Layer.KaMapCache( * "Streets", * "http://www.example.org/web/acessible/cache", * {g: "streets", map: "world", i: "gif", metaTileSize: {w: 5, h: 5} }, * {isBaseLayer: false} * ); * * The cache URLs must look like: * var/cache/World/50000/Group_Name/def/t-440320/l20480 * * This means that the cache generated via tile.php will *not* work with * this class, and should instead use the KaMap layer. * * More information is available in Ticket #1518. * * Inherits from: * - <OpenLayers.Layer.KaMap> * - <OpenLayers.Layer.Grid> */ OpenLayers.Layer.KaMapCache = OpenLayers.Class(OpenLayers.Layer.KaMap, { /** * Constant: IMAGE_EXTENSIONS * {Object} Simple hash map to convert format to extension. */ IMAGE_EXTENSIONS: { 'jpeg': 'jpg', 'gif' : 'gif', 'png' : 'png', 'png8' : 'png', 'png24' : 'png', 'dithered' : 'png' }, /** * Constant: DEFAULT_FORMAT * {Object} Simple hash map to convert format to extension. */ DEFAULT_FORMAT: 'jpeg', /** * Constructor: OpenLayers.Layer.KaMapCache * * Parameters: * name - {String} * url - {String} * params - {Object} Parameters to be sent to the HTTP server in the * query string for the tile. The format can be set via the 'i' * parameter (defaults to jpg) , and the map should be set via * the 'map' parameter. It has been reported that ka-Map may behave * inconsistently if your format parameter does not match the format * parameter configured in your config.php. (See ticket #327 for more * information.) * options - {Object} Additional options for the layer. Any of the * APIProperties listed on this layer, and any layer types it * extends, can be overridden through the options parameter. */ initialize: function(name, url, params, options) { OpenLayers.Layer.KaMap.prototype.initialize.apply(this, arguments); this.extension = this.IMAGE_EXTENSIONS[this.params.i.toLowerCase() || this.DEFAULT_FORMAT]; }, /** * Method: getURL * * Parameters: * bounds - {<OpenLayers.Bounds>} * * Returns: * {String} A string with the layer's url and parameters and also the * passed-in bounds and appropriate tile size specified as * parameters */ getURL: function (bounds) { bounds = this.adjustBounds(bounds); var mapRes = this.map.getResolution(); var scale = Math.round((this.map.getScale() * 10000)) / 10000; var pX = Math.round(bounds.left / mapRes); var pY = -Math.round(bounds.top / mapRes); var metaX = Math.floor(pX / this.tileSize.w / this.params.metaTileSize.w) * this.tileSize.w * this.params.metaTileSize.w; var metaY = Math.floor(pY / this.tileSize.h / this.params.metaTileSize.h) * this.tileSize.h * this.params.metaTileSize.h; var components = [ "/", this.params.map, "/", scale, "/", this.params.g.replace(/\s/g, '_'), "/def/t", metaY, "/l", metaX, "/t", pY, "l", pX, ".", this.extension ]; var url = this.url; if (OpenLayers.Util.isArray(url)) { url = this.selectUrl(components.join(''), url); } return url + components.join(""); }, CLASS_NAME: "OpenLayers.Layer.KaMapCache" }); /* ====================================================================== OpenLayers/Layer/Text.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/Markers.js * @requires OpenLayers/Format/Text.js * @requires OpenLayers/Request/XMLHttpRequest.js */ /** * Class: OpenLayers.Layer.Text * This layer creates markers given data in a text file. The <location> * property of the layer (specified as a property of the options argument * in the <OpenLayers.Layer.Text> constructor) points to a tab delimited * file with data used to create markers. * * The first row of the data file should be a header line with the column names * of the data. Each column should be delimited by a tab space. The * possible columns are: * - *point* lat,lon of the point where a marker is to be placed * - *lat* Latitude of the point where a marker is to be placed * - *lon* Longitude of the point where a marker is to be placed * - *icon* or *image* URL of marker icon to use. * - *iconSize* Size of Icon to use. * - *iconOffset* Where the top-left corner of the icon is to be placed * relative to the latitude and longitude of the point. * - *title* The text of the 'title' is placed inside an 'h2' marker * inside a popup, which opens when the marker is clicked. * - *description* The text of the 'description' is placed below the h2 * in the popup. this can be plain text or HTML. * * Example text file: * (code) * lat lon title description iconSize iconOffset icon * 10 20 title description 21,25 -10,-25 http://www.openlayers.org/dev/img/marker.png * (end) * * Inherits from: * - <OpenLayers.Layer.Markers> */ OpenLayers.Layer.Text = OpenLayers.Class(OpenLayers.Layer.Markers, { /** * APIProperty: location * {String} URL of text file. Must be specified in the "options" argument * of the constructor. Can not be changed once passed in. */ location:null, /** * Property: features * {Array(<OpenLayers.Feature>)} */ features: null, /** * APIProperty: formatOptions * {Object} Hash of options which should be passed to the format when it is * created. Must be passed in the constructor. */ formatOptions: null, /** * Property: selectedFeature * {<OpenLayers.Feature>} */ selectedFeature: null, /** * Constructor: OpenLayers.Layer.Text * Create a text layer. * * Parameters: * name - {String} * options - {Object} Object with properties to be set on the layer. * Must include <location> property. */ initialize: function(name, options) { OpenLayers.Layer.Markers.prototype.initialize.apply(this, arguments); this.features = []; }, /** * APIMethod: destroy */ destroy: function() { // Warning: Layer.Markers.destroy() must be called prior to calling // clearFeatures() here, otherwise we leak memory. Indeed, if // Layer.Markers.destroy() is called after clearFeatures(), it won't be // able to remove the marker image elements from the layer's div since // the markers will have been destroyed by clearFeatures(). OpenLayers.Layer.Markers.prototype.destroy.apply(this, arguments); this.clearFeatures(); this.features = null; }, /** * Method: loadText * Start the load of the Text data. Don't do this when we first add the layer, * since we may not be visible at any point, and it would therefore be a waste. */ loadText: function() { if (!this.loaded) { if (this.location != null) { var onFail = function(e) { this.events.triggerEvent("loadend"); }; this.events.triggerEvent("loadstart"); OpenLayers.Request.GET({ url: this.location, success: this.parseData, failure: onFail, scope: this }); this.loaded = true; } } }, /** * Method: moveTo * If layer is visible and Text has not been loaded, load Text. * * Parameters: * bounds - {Object} * zoomChanged - {Object} * minor - {Object} */ moveTo:function(bounds, zoomChanged, minor) { OpenLayers.Layer.Markers.prototype.moveTo.apply(this, arguments); if(this.visibility && !this.loaded){ this.loadText(); } }, /** * Method: parseData * * Parameters: * ajaxRequest - {<OpenLayers.Request.XMLHttpRequest>} */ parseData: function(ajaxRequest) { var text = ajaxRequest.responseText; var options = {}; OpenLayers.Util.extend(options, this.formatOptions); if (this.map && !this.projection.equals(this.map.getProjectionObject())) { options.externalProjection = this.projection; options.internalProjection = this.map.getProjectionObject(); } var parser = new OpenLayers.Format.Text(options); var features = parser.read(text); for (var i=0, len=features.length; i<len; i++) { var data = {}; var feature = features[i]; var location; var iconSize, iconOffset; location = new OpenLayers.LonLat(feature.geometry.x, feature.geometry.y); if (feature.style.graphicWidth && feature.style.graphicHeight) { iconSize = new OpenLayers.Size( feature.style.graphicWidth, feature.style.graphicHeight); } // FIXME: At the moment, we only use this if we have an // externalGraphic, because icon has no setOffset API Method. /** * FIXME FIRST!! * The Text format does all sorts of parseFloating * The result of a parseFloat for a bogus string is NaN. That * means the three possible values here are undefined, NaN, or a * number. The previous check was an identity check for null. This * means it was failing for all undefined or NaN. A slightly better * check is for undefined. An even better check is to see if the * value is a number (see #1441). */ if (feature.style.graphicXOffset !== undefined && feature.style.graphicYOffset !== undefined) { iconOffset = new OpenLayers.Pixel( feature.style.graphicXOffset, feature.style.graphicYOffset); } if (feature.style.externalGraphic != null) { data.icon = new OpenLayers.Icon(feature.style.externalGraphic, iconSize, iconOffset); } else { data.icon = OpenLayers.Marker.defaultIcon(); //allows for the case where the image url is not // specified but the size is. use a default icon // but change the size if (iconSize != null) { data.icon.setSize(iconSize); } } if ((feature.attributes.title != null) && (feature.attributes.description != null)) { data['popupContentHTML'] = '<h2>'+feature.attributes.title+'</h2>' + '<p>'+feature.attributes.description+'</p>'; } data['overflow'] = feature.attributes.overflow || "auto"; var markerFeature = new OpenLayers.Feature(this, location, data); this.features.push(markerFeature); var marker = markerFeature.createMarker(); if ((feature.attributes.title != null) && (feature.attributes.description != null)) { marker.events.register('click', markerFeature, this.markerClick); } this.addMarker(marker); } this.events.triggerEvent("loadend"); }, /** * Property: markerClick * * Parameters: * evt - {Event} * * Context: * - {<OpenLayers.Feature>} */ markerClick: function(evt) { var sameMarkerClicked = (this == this.layer.selectedFeature); this.layer.selectedFeature = (!sameMarkerClicked) ? this : null; for(var i=0, len=this.layer.map.popups.length; i<len; i++) { this.layer.map.removePopup(this.layer.map.popups[i]); } if (!sameMarkerClicked) { this.layer.map.addPopup(this.createPopup()); } OpenLayers.Event.stop(evt); }, /** * Method: clearFeatures */ clearFeatures: function() { if (this.features != null) { while(this.features.length > 0) { var feature = this.features[0]; OpenLayers.Util.removeItem(this.features, feature); feature.destroy(); } } }, CLASS_NAME: "OpenLayers.Layer.Text" }); /* ====================================================================== OpenLayers/Layer/ArcGIS93Rest.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.ArcGIS93Rest * Instances of OpenLayers.Layer.ArcGIS93Rest are used to display data from * ESRI ArcGIS Server 9.3 (and up?) Mapping Services using the REST API. * Create a new ArcGIS93Rest layer with the <OpenLayers.Layer.ArcGIS93Rest> * constructor. More detail on the REST API is available at * http://sampleserver1.arcgisonline.com/ArcGIS/SDK/REST/index.html ; * specifically, the URL provided to this layer should be an export service * URL: http://sampleserver1.arcgisonline.com/ArcGIS/SDK/REST/export.html * * Inherits from: * - <OpenLayers.Layer.Grid> */ OpenLayers.Layer.ArcGIS93Rest = OpenLayers.Class(OpenLayers.Layer.Grid, { /** * Constant: DEFAULT_PARAMS * {Object} Hashtable of default parameter key/value pairs */ DEFAULT_PARAMS: { format: "png" }, /** * APIProperty: isBaseLayer * {Boolean} Default is true for ArcGIS93Rest layer */ isBaseLayer: true, /** * Constructor: OpenLayers.Layer.ArcGIS93Rest * Create a new ArcGIS93Rest layer object. * * Example: * (code) * var arcims = new OpenLayers.Layer.ArcGIS93Rest("MyName", * "http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Specialty/ESRI_StateCityHighway_USA/MapServer/export", * { * layers: "0,1,2" * }); * (end) * * Parameters: * name - {String} A name for the layer * url - {String} Base url for the ArcGIS server REST service * options - {Object} An object with key/value pairs representing the * options and option values. * * Valid Options: * format - {String} MIME type of desired image type. * layers - {String} Comma-separated list of layers to display. * srs - {String} Projection ID. */ initialize: function(name, url, params, options) { var newArguments = []; //uppercase params params = OpenLayers.Util.upperCaseObject(params); newArguments.push(name, url, params, options); OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments); OpenLayers.Util.applyDefaults( this.params, OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS) ); //layer is transparent if (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 == "jpg") { this.params.FORMAT = OpenLayers.Util.alphaHack() ? "gif" : "png"; } } }, /** * Method: clone * Create a clone of this layer * * Returns: * {<OpenLayers.Layer.ArcGIS93Rest>} An exact clone of this layer */ clone: function (obj) { if (obj == null) { obj = new OpenLayers.Layer.ArcGIS93Rest(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; }, /** * Method: getURL * Return an image url this layer. * * Parameters: * bounds - {<OpenLayers.Bounds>} A bounds representing the bbox for the * request. * * Returns: * {String} A string with the map image's url. */ getURL: function (bounds) { bounds = this.adjustBounds(bounds); // ArcGIS Server only wants the numeric portion of the projection ID. var projWords = this.projection.getCode().split(":"); var srid = projWords[projWords.length - 1]; var imageSize = this.getImageSize(); var newParams = { 'BBOX': bounds.toBBOX(), 'SIZE': imageSize.w + "," + imageSize.h, // We always want image, the other options were json, image with a whole lotta html around it, etc. 'F': "image", 'BBOXSR': srid, 'IMAGESR': srid }; // Now add the filter parameters. if (this.layerDefs) { var layerDefStrList = []; var layerID; for(layerID in this.layerDefs) { if (this.layerDefs.hasOwnProperty(layerID)) { if (this.layerDefs[layerID]) { layerDefStrList.push(layerID); layerDefStrList.push(":"); layerDefStrList.push(this.layerDefs[layerID]); layerDefStrList.push(";"); } } } if (layerDefStrList.length > 0) { newParams['LAYERDEFS'] = layerDefStrList.join(""); } } var requestString = this.getFullRequestString(newParams); return requestString; }, /** * Method: setLayerFilter * addTile creates a tile, initializes it, and adds it to the layer div. * * Parameters: * id - {String} The id of the layer to which the filter applies. * queryDef - {String} A sql-ish query filter, for more detail see the ESRI * documentation at http://sampleserver1.arcgisonline.com/ArcGIS/SDK/REST/export.html */ setLayerFilter: function ( id, queryDef ) { if (!this.layerDefs) { this.layerDefs = {}; } if (queryDef) { this.layerDefs[id] = queryDef; } else { delete this.layerDefs[id]; } }, /** * Method: clearLayerFilter * Clears layer filters, either from a specific layer, * or all of them. * * Parameters: * id - {String} The id of the layer from which to remove any * filter. If unspecified/blank, all filters * will be removed. */ clearLayerFilter: function ( id ) { if (id) { delete this.layerDefs[id]; } else { delete this.layerDefs; } }, /** * 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); }, CLASS_NAME: "OpenLayers.Layer.ArcGIS93Rest" }); /* ====================================================================== OpenLayers/Layer/MapGuide.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/Request/XMLHttpRequest.js * @requires OpenLayers/Layer/Grid.js */ /** * Class: OpenLayers.Layer.MapGuide * Instances of OpenLayers.Layer.MapGuide are used to display * data from a MapGuide OS instance. * * Inherits from: * - <OpenLayers.Layer.Grid> */ OpenLayers.Layer.MapGuide = OpenLayers.Class(OpenLayers.Layer.Grid, { /** * APIProperty: isBaseLayer * {Boolean} Treat this layer as a base layer. Default is true. **/ isBaseLayer: true, /** * APIProperty: useHttpTile * {Boolean} use a tile cache exposed directly via a webserver rather than the * via mapguide server. This does require extra configuration on the Mapguide Server, * and will only work when singleTile is false. The url for the layer must be set to the * webserver path rather than the Mapguide mapagent. * See http://trac.osgeo.org/mapguide/wiki/CodeSamples/Tiles/ServingTilesViaHttp **/ useHttpTile: false, /** * APIProperty: singleTile * {Boolean} use tile server or request single tile image. **/ singleTile: false, /** * APIProperty: useOverlay * {Boolean} flag to indicate if the layer should be retrieved using * GETMAPIMAGE (default) or using GETDYNAMICOVERLAY requests. **/ useOverlay: false, /** * APIProperty: useAsyncOverlay * {Boolean} indicates if the MapGuide site supports the asynchronous * GETDYNAMICOVERLAY requests which is available in MapGuide Enterprise 2010 * and MapGuide Open Source v2.0.3 or higher. The newer versions of MG * is called asynchronously, allows selections to be drawn separately from * the map and offers styling options. * * With older versions of MapGuide, set useAsyncOverlay=false. Note that in * this case a synchronous AJAX call is issued and the mapname and session * parameters must be used to initialize the layer, not the mapdefinition * parameter. Also note that this will issue a synchronous AJAX request * before the image request can be issued so the users browser may lock * up if the MG Web tier does not respond in a timely fashion. **/ useAsyncOverlay: true, /** * Constant: TILE_PARAMS * {Object} Hashtable of default parameter key/value pairs for tiled layer */ TILE_PARAMS: { operation: 'GETTILEIMAGE', version: '1.2.0' }, /** * Constant: SINGLE_TILE_PARAMS * {Object} Hashtable of default parameter key/value pairs for untiled layer */ SINGLE_TILE_PARAMS: { operation: 'GETMAPIMAGE', format: 'PNG', locale: 'en', clip: '1', version: '1.0.0' }, /** * Constant: OVERLAY_PARAMS * {Object} Hashtable of default parameter key/value pairs for untiled layer */ OVERLAY_PARAMS: { operation: 'GETDYNAMICMAPOVERLAYIMAGE', format: 'PNG', locale: 'en', clip: '1', version: '2.0.0' }, /** * Constant: FOLDER_PARAMS * {Object} Hashtable of parameter key/value pairs which describe * the folder structure for tiles as configured in the mapguide * serverconfig.ini section [TileServiceProperties] */ FOLDER_PARAMS: { tileColumnsPerFolder: 30, tileRowsPerFolder: 30, format: 'png', querystring: null }, /** * Property: defaultSize * {<OpenLayers.Size>} Tile size as produced by MapGuide server **/ defaultSize: new OpenLayers.Size(300,300), /** * Property: tileOriginCorner * {String} MapGuide tile server uses top-left as tile origin **/ tileOriginCorner: "tl", /** * Constructor: OpenLayers.Layer.MapGuide * Create a new Mapguide layer, either tiled or untiled. * * For tiled layers, the 'groupName' and 'mapDefinition' values * must be specified as parameters in the constructor. * * For untiled base layers, specify either combination of 'mapName' and * 'session', or 'mapDefinition' and 'locale'. * * For older versions of MapGuide and overlay layers, set useAsyncOverlay * to false and in this case mapName and session are required parameters * for the constructor. * * NOTE: MapGuide OS uses a DPI value and degrees to meters conversion * factor that are different than the defaults used in OpenLayers, * so these must be adjusted accordingly in your application. * See the MapGuide example for how to set these values for MGOS. * * Parameters: * name - {String} Name of the layer displayed in the interface * url - {String} Location of the MapGuide mapagent executable * (e.g. http://localhost:8008/mapguide/mapagent/mapagent.fcgi) * params - {Object} hashtable of additional parameters to use. Some * parameters may require additional code on the server. The ones that * you may want to use are: * - mapDefinition - {String} The MapGuide resource definition * (e.g. Library://Samples/Gmap/Maps/gmapTiled.MapDefinition) * - locale - Locale setting * (for untiled overlays layers only) * - mapName - {String} Name of the map as stored in the MapGuide session. * (for untiled layers with a session parameter only) * - session - { String} MapGuide session ID * (for untiled overlays layers only) * - basemaplayergroupname - {String} GroupName for tiled MapGuide layers only * - format - Image format to be returned (for untiled overlay layers only) * - showLayers - {String} A comma separated list of GUID's for the * layers to display eg: 'cvc-xcv34,453-345-345sdf'. * - hideLayers - {String} A comma separated list of GUID's for the * layers to hide eg: 'cvc-xcv34,453-345-345sdf'. * - showGroups - {String} A comma separated list of GUID's for the * groups to display eg: 'cvc-xcv34,453-345-345sdf'. * - hideGroups - {String} A comma separated list of GUID's for the * groups to hide eg: 'cvc-xcv34,453-345-345sdf' * - selectionXml - {String} A selection xml string Some server plumbing * is required to read such a value. * options - {Object} Hashtable of extra options to tag onto the layer; * will vary depending if tiled or untiled maps are being requested */ initialize: function(name, url, params, options) { OpenLayers.Layer.Grid.prototype.initialize.apply(this, arguments); // unless explicitly set in options, if the layer is transparent, // it will be an overlay if (options == null || options.isBaseLayer == null) { this.isBaseLayer = ((this.transparent != "true") && (this.transparent != true)); } if (options && options.useOverlay!=null) { this.useOverlay = options.useOverlay; } //initialize for untiled layers if (this.singleTile) { if (this.useOverlay) { OpenLayers.Util.applyDefaults( this.params, this.OVERLAY_PARAMS ); if (!this.useAsyncOverlay) { this.params.version = "1.0.0"; } } else { OpenLayers.Util.applyDefaults( this.params, this.SINGLE_TILE_PARAMS ); } } else { //initialize for tiled layers if (this.useHttpTile) { OpenLayers.Util.applyDefaults( this.params, this.FOLDER_PARAMS ); } else { OpenLayers.Util.applyDefaults( this.params, this.TILE_PARAMS ); } this.setTileSize(this.defaultSize); } }, /** * Method: clone * Create a clone of this layer * * Returns: * {<OpenLayers.Layer.MapGuide>} An exact clone of this layer */ clone: function (obj) { if (obj == null) { obj = new OpenLayers.Layer.MapGuide(this.name, this.url, this.params, this.getOptions()); } //get all additions from superclasses obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); return obj; }, /** * Method: getURL * Return a 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) { var url; var center = bounds.getCenterLonLat(); var mapSize = this.map.getSize(); if (this.singleTile) { //set up the call for GETMAPIMAGE or GETDYNAMICMAPOVERLAY with //dynamic map parameters var params = { setdisplaydpi: OpenLayers.DOTS_PER_INCH, setdisplayheight: mapSize.h*this.ratio, setdisplaywidth: mapSize.w*this.ratio, setviewcenterx: center.lon, setviewcentery: center.lat, setviewscale: this.map.getScale() }; if (this.useOverlay && !this.useAsyncOverlay) { //first we need to call GETVISIBLEMAPEXTENT to set the extent var getVisParams = {}; getVisParams = OpenLayers.Util.extend(getVisParams, params); getVisParams.operation = "GETVISIBLEMAPEXTENT"; getVisParams.version = "1.0.0"; getVisParams.session = this.params.session; getVisParams.mapName = this.params.mapName; getVisParams.format = 'text/xml'; url = this.getFullRequestString( getVisParams ); OpenLayers.Request.GET({url: url, async: false}); } //construct the full URL url = this.getFullRequestString( params ); } else { //tiled version var currentRes = this.map.getResolution(); var colidx = Math.floor((bounds.left-this.maxExtent.left)/currentRes); colidx = Math.round(colidx/this.tileSize.w); var rowidx = Math.floor((this.maxExtent.top-bounds.top)/currentRes); rowidx = Math.round(rowidx/this.tileSize.h); if (this.useHttpTile){ url = this.getImageFilePath( { tilecol: colidx, tilerow: rowidx, scaleindex: this.resolutions.length - this.map.zoom - 1 }); } else { url = this.getFullRequestString( { tilecol: colidx, tilerow: rowidx, scaleindex: this.resolutions.length - this.map.zoom - 1 }); } } return url; }, /** * Method: getFullRequestString * getFullRequestString on MapGuide layers is special, because we * do a regular expression replace on ',' in parameters to '+'. * This is why it is subclassed here. * * Parameters: * altUrl - {String} Alternative base URL to use. * * Returns: * {String} A string with the layer's url appropriately encoded for MapGuide */ getFullRequestString:function(newParams, altUrl) { // use layer's url unless altUrl passed in var url = (altUrl == null) ? this.url : altUrl; // if url is not a string, it should be an array of strings, // in which case we will randomly select one of them in order // to evenly distribute requests to different urls. if (typeof url == "object") { url = url[Math.floor(Math.random()*url.length)]; } // requestString always starts with url var requestString = 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); // 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]; } } var paramsString = OpenLayers.Util.getParameterString(allParams); /* MapGuide needs '+' seperating things like bounds/height/width. Since typically this is URL encoded, we use a slight hack: we depend on the list-like functionality of getParameterString to leave ',' only in the case of list items (since otherwise it is encoded) then do a regular expression replace on the , characters to '+' */ paramsString = paramsString.replace(/,/g, "+"); if (paramsString != "") { var lastServerChar = url.charAt(url.length - 1); if ((lastServerChar == "&") || (lastServerChar == "?")) { requestString += paramsString; } else { if (url.indexOf('?') == -1) { //serverPath has no ? -- add one requestString += '?' + paramsString; } else { //serverPath contains ?, so must already have paramsString at the end requestString += '&' + paramsString; } } } return requestString; }, /** * Method: getImageFilePath * special handler to request mapguide tiles from an http exposed tilecache * * Parameters: * altUrl - {String} Alternative base URL to use. * * Returns: * {String} A string with the url for the tile image */ getImageFilePath:function(newParams, altUrl) { // use layer's url unless altUrl passed in var url = (altUrl == null) ? this.url : altUrl; // if url is not a string, it should be an array of strings, // in which case we will randomly select one of them in order // to evenly distribute requests to different urls. if (typeof url == "object") { url = url[Math.floor(Math.random()*url.length)]; } // requestString always starts with url var requestString = url; var tileRowGroup = ""; var tileColGroup = ""; if (newParams.tilerow < 0) { tileRowGroup = '-'; } if (newParams.tilerow == 0 ) { tileRowGroup += '0'; } else { tileRowGroup += Math.floor(Math.abs(newParams.tilerow/this.params.tileRowsPerFolder)) * this.params.tileRowsPerFolder; } if (newParams.tilecol < 0) { tileColGroup = '-'; } if (newParams.tilecol == 0) { tileColGroup += '0'; } else { tileColGroup += Math.floor(Math.abs(newParams.tilecol/this.params.tileColumnsPerFolder)) * this.params.tileColumnsPerFolder; } var tilePath = '/S' + Math.floor(newParams.scaleindex) + '/' + this.params.basemaplayergroupname + '/R' + tileRowGroup + '/C' + tileColGroup + '/' + (newParams.tilerow % this.params.tileRowsPerFolder) + '_' + (newParams.tilecol % this.params.tileColumnsPerFolder) + '.' + this.params.format; if (this.params.querystring) { tilePath += "?" + this.params.querystring; } requestString += tilePath; return requestString; }, CLASS_NAME: "OpenLayers.Layer.MapGuide" }); /* ====================================================================== OpenLayers/Layer/EventPane.js ====================================================================== */ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for * full list of contributors). Published under the 2-clause BSD license. * See license.txt in the OpenLayers distribution or repository for the * full text of the license. */ /** * @requires OpenLayers/Layer.js * @requires OpenLayers/Util.js */ /** * Class: OpenLayers.Layer.EventPane * Base class for 3rd party layers, providing a DOM element which isolates * the 3rd-party layer from mouse events. * Only used by Google layers. * * Automatically instantiated by the Google constructor, and not usually instantiated directly. * * Create a new event pane layer with the * <OpenLayers.Layer.EventPane> constructor. * * Inherits from: * - <OpenLayers.Layer> */ OpenLayers.Layer.EventPane = OpenLayers.Class(OpenLayers.Layer, { /** * APIProperty: smoothDragPan * {Boolean} smoothDragPan determines whether non-public/internal API * methods are used for better performance while dragging EventPane * layers. When not in sphericalMercator mode, the smoother dragging * doesn't actually move north/south directly with the number of * pixels moved, resulting in a slight offset when you drag your mouse * north south with this option on. If this visual disparity bothers * you, you should turn this option off, or use spherical mercator. * Default is on. */ smoothDragPan: true, /** * Property: isBaseLayer * {Boolean} EventPaned layers are always base layers, by necessity. */ isBaseLayer: true, /** * APIProperty: isFixed * {Boolean} EventPaned layers are fixed by default. */ isFixed: true, /** * Property: pane * {DOMElement} A reference to the element that controls the events. */ pane: null, /** * Property: mapObject * {Object} This is the object which will be used to load the 3rd party library * in the case of the google layer, this will be of type GMap, * in the case of the ve layer, this will be of type VEMap */ mapObject: null, /** * Constructor: OpenLayers.Layer.EventPane * Create a new event pane layer * * Parameters: * name - {String} * options - {Object} Hashtable of extra options to tag onto the layer */ initialize: function(name, options) { OpenLayers.Layer.prototype.initialize.apply(this, arguments); if (this.pane == null) { this.pane = OpenLayers.Util.createDiv(this.div.id + "_EventPane"); } }, /** * APIMethod: destroy * Deconstruct this layer. */ destroy: function() { this.mapObject = null; this.pane = null; OpenLayers.Layer.prototype.destroy.apply(this, arguments); }, /** * Method: setMap * Set the map property for the layer. This is done through an accessor * so that subclasses can override this and take special action once * they have their map variable set. * * Parameters: * map - {<OpenLayers.Map>} */ setMap: function(map) { OpenLayers.Layer.prototype.setMap.apply(this, arguments); this.pane.style.zIndex = parseInt(this.div.style.zIndex) + 1; this.pane.style.display = this.div.style.display; this.pane.style.width="100%"; this.pane.style.height="100%"; if (OpenLayers.BROWSER_NAME == "msie") { this.pane.style.background = "url(" + OpenLayers.Util.getImageLocation("blank.gif") + ")"; } if (this.isFixed) { this.map.viewPortDiv.appendChild(this.pane); } else { this.map.layerContainerDiv.appendChild(this.pane); } // once our layer has been added to the map, we can load it this.loadMapObject(); // if map didn't load, display warning if (this.mapObject == null) { this.loadWarningMessage(); } }, /** * APIMethod: removeMap * On being removed from the map, we'll like to remove the invisible 'pane' * div that we added to it on creation. * * Parameters: * map - {<OpenLayers.Map>} */ removeMap: function(map) { if (this.pane && this.pane.parentNode) { this.pane.parentNode.removeChild(this.pane); } OpenLayers.Layer.prototype.removeMap.apply(this, arguments); }, /** * Method: loadWarningMessage * If we can't load the map lib, then display an error message to the * user and tell them where to go for help. * * This function sets up the layout for the warning message. Each 3rd * party layer must implement its own getWarningHTML() function to * provide the actual warning message. */ loadWarningMessage:function() { this.div.style.backgroundColor = "darkblue"; var viewSize = this.map.getSize(); var msgW = Math.min(viewSize.w, 300); var msgH = Math.min(viewSize.h, 200); var size = new OpenLayers.Size(msgW, msgH); var centerPx = new OpenLayers.Pixel(viewSize.w/2, viewSize.h/2); var topLeft = centerPx.add(-size.w/2, -size.h/2); var div = OpenLayers.Util.createDiv(this.name + "_warning", topLeft, size, null, null, null, "auto"); div.style.padding = "7px"; div.style.backgroundColor = "yellow"; div.innerHTML = this.getWarningHTML(); this.div.appendChild(div); }, /** * Method: getWarningHTML * To be implemented by subclasses. * * Returns: * {String} String with information on why layer is broken, how to get * it working. */ getWarningHTML:function() { //should be implemented by subclasses return ""; }, /** * Method: display * Set the display on the pane * * Parameters: * display - {Boolean} */ display: function(display) { OpenLayers.Layer.prototype.display.apply(this, arguments); this.pane.style.display = this.div.style.display; }, /** * Method: setZIndex * Set the z-index order for the pane. * * Parameters: * zIndex - {int} */ setZIndex: function (zIndex) { OpenLayers.Layer.prototype.setZIndex.apply(this, arguments); this.pane.style.zIndex = parseInt(this.div.style.zIndex) + 1; }, /** * Method: moveByPx * Move the layer based on pixel vector. To be implemented by subclasses. * * Parameters: * dx - {Number} The x coord of the displacement vector. * dy - {Number} The y coord of the displacement vector. */ moveByPx: function(dx, dy) { OpenLayers.Layer.prototype.moveByPx.apply(this, arguments); if (this.dragPanMapObject) { this.dragPanMapObject(dx, -dy); } else { this.moveTo(this.map.getCachedCenter()); } }, /** * Method: moveTo * Handle calls to move the layer. * * Parameters: * bounds - {<OpenLayers.Bounds>} * zoomChanged - {Boolean} * dragging - {Boolean} */ moveTo:function(bounds, zoomChanged, dragging) { OpenLayers.Layer.prototype.moveTo.apply(this, arguments); if (this.mapObject != null) { var newCenter = this.map.getCenter(); var newZoom = this.map.getZoom(); if (newCenter != null) { var moOldCenter = this.getMapObjectCenter(); var oldCenter = this.getOLLonLatFromMapObjectLonLat(moOldCenter); var moOldZoom = this.getMapObjectZoom(); var oldZoom= this.getOLZoomFromMapObjectZoom(moOldZoom); if (!(newCenter.equals(oldCenter)) || newZoom != oldZoom) { if (!zoomChanged && oldCenter && this.dragPanMapObject && this.smoothDragPan) { var oldPx = this.map.getViewPortPxFromLonLat(oldCenter); var newPx = this.map.getViewPortPxFromLonLat(newCenter); this.dragPanMapObject(newPx.x-oldPx.x, oldPx.y-newPx.y); } else { var center = this.getMapObjectLonLatFromOLLonLat(newCenter); var zoom = this.getMapObjectZoomFromOLZoom(newZoom); this.setMapObjectCenter(center, zoom, dragging); } } } } }, /********************************************************/ /* */ /* Baselayer Functions */ /* */ /********************************************************/ /** * Method: getLonLatFromViewPortPx * Get a map location from a pixel location * * Parameters: * viewPortPx - {<OpenLayers.Pixel>} * * Returns: * {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in view * port OpenLayers.Pixel, translated into lon/lat by map lib * If the map lib is not loaded or not centered, returns null */ getLonLatFromViewPortPx: function (viewPortPx) { var lonlat = null; if ( (this.mapObject != null) && (this.getMapObjectCenter() != null) ) { var moPixel = this.getMapObjectPixelFromOLPixel(viewPortPx); var moLonLat = this.getMapObjectLonLatFromMapObjectPixel(moPixel); lonlat = this.getOLLonLatFromMapObjectLonLat(moLonLat); } return lonlat; }, /** * Method: getViewPortPxFromLonLat * Get a pixel location from a map location * * Parameters: * lonlat - {<OpenLayers.LonLat>} * * Returns: * {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in * OpenLayers.LonLat, translated into view port pixels by map lib * If map lib is not loaded or not centered, returns null */ getViewPortPxFromLonLat: function (lonlat) { var viewPortPx = null; if ( (this.mapObject != null) && (this.getMapObjectCenter() != null) ) { var moLonLat = this.getMapObjectLonLatFromOLLonLat(lonlat); var moPixel = this.getMapObjectPixelFromMapObjectLonLat(moLonLat); viewPortPx = this.getOLPixelFromMapObjectPixel(moPixel); } return viewPortPx; }, /********************************************************/ /* */ /* Translation Functions */ /* */ /* The following functions translate Map Object and */ /* OL formats for Pixel, LonLat */ /* */ /********************************************************/ // // TRANSLATION: MapObject LatLng <-> OpenLayers.LonLat // /** * Method: getOLLonLatFromMapObjectLonLat * Get an OL style map location from a 3rd party style map location * * Parameters * moLonLat - {Object} * * Returns: * {<OpenLayers.LonLat>} An OpenLayers.LonLat, translated from the passed in * MapObject LonLat * Returns null if null value is passed in */ getOLLonLatFromMapObjectLonLat: function(moLonLat) { var olLonLat = null; if (moLonLat != null) { var lon = this.getLongitudeFromMapObjectLonLat(moLonLat); var lat = this.getLatitudeFromMapObjectLonLat(moLonLat); olLonLat = new OpenLayers.LonLat(lon, lat); } return olLonLat; }, /** * Method: getMapObjectLonLatFromOLLonLat * Get a 3rd party map location from an OL map location. * * Parameters: * olLonLat - {<OpenLayers.LonLat>} * * Returns: * {Object} A MapObject LonLat, translated from the passed in * OpenLayers.LonLat * Returns null if null value is passed in */ getMapObjectLonLatFromOLLonLat: function(olLonLat) { var moLatLng = null; if (olLonLat != null) { moLatLng = this.getMapObjectLonLatFromLonLat(olLonLat.lon, olLonLat.lat); } return moLatLng; }, // // TRANSLATION: MapObject Pixel <-> OpenLayers.Pixel // /** * Method: getOLPixelFromMapObjectPixel * Get an OL pixel location from a 3rd party pixel location. * * Parameters: * moPixel - {Object} * * Returns: * {<OpenLayers.Pixel>} An OpenLayers.Pixel, translated from the passed in * MapObject Pixel * Returns null if null value is passed in */ getOLPixelFromMapObjectPixel: function(moPixel) { var olPixel = null; if (moPixel != null) { var x = this.getXFromMapObjectPixel(moPixel); var y = this.getYFromMapObjectPixel(moPixel); olPixel = new OpenLayers.Pixel(x, y); } return olPixel; }, /** * Method: getMapObjectPixelFromOLPixel * Get a 3rd party pixel location from an OL pixel location * * Parameters: * olPixel - {<OpenLayers.Pixel>} * * Returns: * {Object} A MapObject Pixel, translated from the passed in * OpenLayers.Pixel * Returns null if null value is passed in */ getMapObjectPixelFromOLPixel: function(olPixel) { var moPixel = null; if (olPixel != null) { moPixel = this.getMapObjectPixelFromXY(olPixel.x, olPixel.y); } return moPixel; }, CLASS_NAME: "OpenLayers.Layer.EventPane" }); /* ====================================================================== OpenLayers/Layer/SphericalMercator.js ====================================================================== */ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for * full list of contributors). Published under the 2-clause BSD license. * See license.txt in the OpenLayers distribution or repository for the * full text of the license. */ /** * @requires OpenLayers/Layer.js * @requires OpenLayers/Projection.js */ /** * Class: OpenLayers.Layer.SphericalMercator * A mixin for layers that wraps up the pieces neccesary to have a coordinate * conversion for working with commercial APIs which use a spherical * mercator projection. Using this layer as a base layer, additional * layers can be used as overlays if they are in the same projection. * * A layer is given properties of this object by setting the sphericalMercator * property to true. * * More projection information: * - http://spatialreference.org/ref/user/google-projection/ * * Proj4 Text: * +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 * +k=1.0 +units=m +nadgrids=@null +no_defs * * WKT: * 900913=PROJCS["WGS84 / Simple Mercator", GEOGCS["WGS 84", * DATUM["WGS_1984", SPHEROID["WGS_1984", 6378137.0, 298.257223563]], * PRIMEM["Greenwich", 0.0], UNIT["degree", 0.017453292519943295], * AXIS["Longitude", EAST], AXIS["Latitude", NORTH]], * PROJECTION["Mercator_1SP_Google"], * PARAMETER["latitude_of_origin", 0.0], PARAMETER["central_meridian", 0.0], * PARAMETER["scale_factor", 1.0], PARAMETER["false_easting", 0.0], * PARAMETER["false_northing", 0.0], UNIT["m", 1.0], AXIS["x", EAST], * AXIS["y", NORTH], AUTHORITY["EPSG","900913"]] */ OpenLayers.Layer.SphericalMercator = { /** * Method: getExtent * Get the map's extent. * * Returns: * {<OpenLayers.Bounds>} The map extent. */ getExtent: function() { var extent = null; if (this.sphericalMercator) { extent = this.map.calculateBounds(); } else { extent = OpenLayers.Layer.FixedZoomLevels.prototype.getExtent.apply(this); } return extent; }, /** * Method: getLonLatFromViewPortPx * Get a map location from a pixel location * * Parameters: * viewPortPx - {<OpenLayers.Pixel>} * * Returns: * {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in view * port OpenLayers.Pixel, translated into lon/lat by map lib * If the map lib is not loaded or not centered, returns null */ getLonLatFromViewPortPx: function (viewPortPx) { return OpenLayers.Layer.prototype.getLonLatFromViewPortPx.apply(this, arguments); }, /** * Method: getViewPortPxFromLonLat * Get a pixel location from a map location * * Parameters: * lonlat - {<OpenLayers.LonLat>} * * Returns: * {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in * OpenLayers.LonLat, translated into view port pixels by map lib * If map lib is not loaded or not centered, returns null */ getViewPortPxFromLonLat: function (lonlat) { return OpenLayers.Layer.prototype.getViewPortPxFromLonLat.apply(this, arguments); }, /** * Method: initMercatorParameters * Set up the mercator parameters on the layer: resolutions, * projection, units. */ initMercatorParameters: function() { // set up properties for Mercator - assume EPSG:900913 this.RESOLUTIONS = []; var maxResolution = 156543.03390625; for(var zoom=0; zoom<=this.MAX_ZOOM_LEVEL; ++zoom) { this.RESOLUTIONS[zoom] = maxResolution / Math.pow(2, zoom); } this.units = "m"; this.projection = this.projection || "EPSG:900913"; }, /** * APIMethod: forwardMercator * Given a lon,lat in EPSG:4326, return a point in Spherical Mercator. * * Parameters: * lon - {float} * lat - {float} * * Returns: * {<OpenLayers.LonLat>} The coordinates transformed to Mercator. */ forwardMercator: (function() { var gg = new OpenLayers.Projection("EPSG:4326"); var sm = new OpenLayers.Projection("EPSG:900913"); return function(lon, lat) { var point = OpenLayers.Projection.transform({x: lon, y: lat}, gg, sm); return new OpenLayers.LonLat(point.x, point.y); }; })(), /** * APIMethod: inverseMercator * Given a x,y in Spherical Mercator, return a point in EPSG:4326. * * Parameters: * x - {float} A map x in Spherical Mercator. * y - {float} A map y in Spherical Mercator. * * Returns: * {<OpenLayers.LonLat>} The coordinates transformed to EPSG:4326. */ inverseMercator: (function() { var gg = new OpenLayers.Projection("EPSG:4326"); var sm = new OpenLayers.Projection("EPSG:900913"); return function(x, y) { var point = OpenLayers.Projection.transform({x: x, y: y}, sm, gg); return new OpenLayers.LonLat(point.x, point.y); }; })() }; /* ====================================================================== OpenLayers/Layer/Google.js ====================================================================== */ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for * full list of contributors). Published under the 2-clause BSD license. * See license.txt in the OpenLayers distribution or repository for the * full text of the license. */ /** * @requires OpenLayers/Layer/SphericalMercator.js * @requires OpenLayers/Layer/EventPane.js * @requires OpenLayers/Layer/FixedZoomLevels.js * @requires OpenLayers/Lang.js */ /** * Class: OpenLayers.Layer.Google * * Provides a wrapper for Google's Maps API * Normally the Terms of Use for this API do not allow wrapping, but Google * have provided written consent to OpenLayers for this - see email in * http://osgeo-org.1560.n6.nabble.com/Google-Maps-API-Terms-of-Use-changes-tp4910013p4911981.html * * Inherits from: * - <OpenLayers.Layer.SphericalMercator> * - <OpenLayers.Layer.EventPane> * - <OpenLayers.Layer.FixedZoomLevels> */ OpenLayers.Layer.Google = OpenLayers.Class( OpenLayers.Layer.EventPane, OpenLayers.Layer.FixedZoomLevels, { /** * Constant: MIN_ZOOM_LEVEL * {Integer} 0 */ MIN_ZOOM_LEVEL: 0, /** * Constant: MAX_ZOOM_LEVEL * {Integer} 21 */ MAX_ZOOM_LEVEL: 21, /** * Constant: RESOLUTIONS * {Array(Float)} Hardcode these resolutions so that they are more closely * tied with the standard wms projection */ RESOLUTIONS: [ 1.40625, 0.703125, 0.3515625, 0.17578125, 0.087890625, 0.0439453125, 0.02197265625, 0.010986328125, 0.0054931640625, 0.00274658203125, 0.001373291015625, 0.0006866455078125, 0.00034332275390625, 0.000171661376953125, 0.0000858306884765625, 0.00004291534423828125, 0.00002145767211914062, 0.00001072883605957031, 0.00000536441802978515, 0.00000268220901489257, 0.0000013411045074462891, 0.00000067055225372314453 ], /** * APIProperty: type * {GMapType} */ type: null, /** * APIProperty: wrapDateLine * {Boolean} Allow user to pan forever east/west. Default is true. * Setting this to false only restricts panning if * <sphericalMercator> is true. */ wrapDateLine: true, /** * APIProperty: sphericalMercator * {Boolean} Should the map act as a mercator-projected map? This will * cause all interactions with the map to be in the actual map * projection, which allows support for vector drawing, overlaying * other maps, etc. */ sphericalMercator: false, /** * Property: version * {Number} The version of the Google Maps API */ version: null, /** * Constructor: OpenLayers.Layer.Google * * Parameters: * name - {String} A name for the layer. * options - {Object} An optional object whose properties will be set * on the layer. */ initialize: function(name, options) { options = options || {}; if(!options.version) { options.version = typeof GMap2 === "function" ? "2" : "3"; } var mixin = OpenLayers.Layer.Google["v" + options.version.replace(/\./g, "_")]; if (mixin) { OpenLayers.Util.applyDefaults(options, mixin); } else { throw "Unsupported Google Maps API version: " + options.version; } OpenLayers.Util.applyDefaults(options, mixin.DEFAULTS); if (options.maxExtent) { options.maxExtent = options.maxExtent.clone(); } OpenLayers.Layer.EventPane.prototype.initialize.apply(this, [name, options]); OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this, [name, options]); if (this.sphericalMercator) { OpenLayers.Util.extend(this, OpenLayers.Layer.SphericalMercator); this.initMercatorParameters(); } }, /** * Method: clone * Create a clone of this layer * * Returns: * {<OpenLayers.Layer.Google>} An exact clone of this layer */ clone: function() { /** * This method isn't intended to be called by a subclass and it * doesn't call the same method on the superclass. We don't call * the super's clone because we don't want properties that are set * on this layer after initialize (i.e. this.mapObject etc.). */ return new OpenLayers.Layer.Google( this.name, this.getOptions() ); }, /** * APIMethod: setVisibility * Set the visibility flag for the layer and hide/show & redraw * accordingly. Fire event unless otherwise specified * * Note that visibility is no longer simply whether or not the layer's * style.display is set to "block". Now we store a 'visibility' state * property on the layer class, this allows us to remember whether or * not we *desire* for a layer to be visible. In the case where the * map's resolution is out of the layer's range, this desire may be * subverted. * * Parameters: * visible - {Boolean} Display the layer (if in range) */ setVisibility: function(visible) { // sharing a map container, opacity has to be set per layer var opacity = this.opacity == null ? 1 : this.opacity; OpenLayers.Layer.EventPane.prototype.setVisibility.apply(this, arguments); this.setOpacity(opacity); }, /** * APIMethod: display * Hide or show the Layer * * Parameters: * visible - {Boolean} */ display: function(visible) { if (!this._dragging) { this.setGMapVisibility(visible); } OpenLayers.Layer.EventPane.prototype.display.apply(this, arguments); }, /** * Method: moveTo * * Parameters: * bounds - {<OpenLayers.Bounds>} * zoomChanged - {Boolean} Tells when zoom has changed, as layers have to * do some init work in that case. * dragging - {Boolean} */ moveTo: function(bounds, zoomChanged, dragging) { this._dragging = dragging; OpenLayers.Layer.EventPane.prototype.moveTo.apply(this, arguments); delete this._dragging; }, /** * APIMethod: setOpacity * Sets the opacity for the entire layer (all images) * * Parameters: * opacity - {Float} */ setOpacity: function(opacity) { if (opacity !== this.opacity) { if (this.map != null) { this.map.events.triggerEvent("changelayer", { layer: this, property: "opacity" }); } this.opacity = opacity; } // Though this layer's opacity may not change, we're sharing a container // and need to update the opacity for the entire container. if (this.getVisibility()) { var container = this.getMapContainer(); OpenLayers.Util.modifyDOMElement( container, null, null, null, null, null, null, opacity ); } }, /** * APIMethod: destroy * Clean up this layer. */ destroy: function() { /** * We have to override this method because the event pane destroy * deletes the mapObject reference before removing this layer from * the map. */ if (this.map) { this.setGMapVisibility(false); var cache = OpenLayers.Layer.Google.cache[this.map.id]; if (cache && cache.count <= 1) { this.removeGMapElements(); } } OpenLayers.Layer.EventPane.prototype.destroy.apply(this, arguments); }, /** * Method: removeGMapElements * Remove all elements added to the dom. This should only be called if * this is the last of the Google layers for the given map. */ removeGMapElements: function() { var cache = OpenLayers.Layer.Google.cache[this.map.id]; if (cache) { // remove shared elements from dom var container = this.mapObject && this.getMapContainer(); if (container && container.parentNode) { container.parentNode.removeChild(container); } var termsOfUse = cache.termsOfUse; if (termsOfUse && termsOfUse.parentNode) { termsOfUse.parentNode.removeChild(termsOfUse); } var poweredBy = cache.poweredBy; if (poweredBy && poweredBy.parentNode) { poweredBy.parentNode.removeChild(poweredBy); } if (this.mapObject && window.google && google.maps && google.maps.event && google.maps.event.clearListeners) { google.maps.event.clearListeners(this.mapObject, 'tilesloaded'); } } }, /** * APIMethod: removeMap * On being removed from the map, also remove termsOfUse and poweredBy divs * * Parameters: * map - {<OpenLayers.Map>} */ removeMap: function(map) { // hide layer before removing if (this.visibility && this.mapObject) { this.setGMapVisibility(false); } // check to see if last Google layer in this map var cache = OpenLayers.Layer.Google.cache[map.id]; if (cache) { if (cache.count <= 1) { this.removeGMapElements(); delete OpenLayers.Layer.Google.cache[map.id]; } else { // decrement the layer count --cache.count; } } // remove references to gmap elements delete this.termsOfUse; delete this.poweredBy; delete this.mapObject; delete this.dragObject; OpenLayers.Layer.EventPane.prototype.removeMap.apply(this, arguments); }, // // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds // /** * APIMethod: getOLBoundsFromMapObjectBounds * * Parameters: * moBounds - {Object} * * Returns: * {<OpenLayers.Bounds>} An <OpenLayers.Bounds>, translated from the * passed-in MapObject Bounds. * Returns null if null value is passed in. */ getOLBoundsFromMapObjectBounds: function(moBounds) { var olBounds = null; if (moBounds != null) { var sw = moBounds.getSouthWest(); var ne = moBounds.getNorthEast(); if (this.sphericalMercator) { sw = this.forwardMercator(sw.lng(), sw.lat()); ne = this.forwardMercator(ne.lng(), ne.lat()); } else { sw = new OpenLayers.LonLat(sw.lng(), sw.lat()); ne = new OpenLayers.LonLat(ne.lng(), ne.lat()); } olBounds = new OpenLayers.Bounds(sw.lon, sw.lat, ne.lon, ne.lat ); } return olBounds; }, /** * APIMethod: getWarningHTML * * Returns: * {String} String with information on why layer is broken, how to get * it working. */ getWarningHTML:function() { return OpenLayers.i18n("googleWarning"); }, /************************************ * * * MapObject Interface Controls * * * ************************************/ // Get&Set Center, Zoom /** * APIMethod: getMapObjectCenter * * Returns: * {Object} The mapObject's current center in Map Object format */ getMapObjectCenter: function() { return this.mapObject.getCenter(); }, /** * APIMethod: getMapObjectZoom * * Returns: * {Integer} The mapObject's current zoom, in Map Object format */ getMapObjectZoom: function() { return this.mapObject.getZoom(); }, /************************************ * * * MapObject Primitives * * * ************************************/ // LonLat /** * APIMethod: getLongitudeFromMapObjectLonLat * * Parameters: * moLonLat - {Object} MapObject LonLat format * * Returns: * {Float} Longitude of the given MapObject LonLat */ getLongitudeFromMapObjectLonLat: function(moLonLat) { return this.sphericalMercator ? this.forwardMercator(moLonLat.lng(), moLonLat.lat()).lon : moLonLat.lng(); }, /** * APIMethod: getLatitudeFromMapObjectLonLat * * Parameters: * moLonLat - {Object} MapObject LonLat format * * Returns: * {Float} Latitude of the given MapObject LonLat */ getLatitudeFromMapObjectLonLat: function(moLonLat) { var lat = this.sphericalMercator ? this.forwardMercator(moLonLat.lng(), moLonLat.lat()).lat : moLonLat.lat(); return lat; }, // Pixel /** * APIMethod: getXFromMapObjectPixel * * Parameters: * moPixel - {Object} MapObject Pixel format * * Returns: * {Integer} X value of the MapObject Pixel */ getXFromMapObjectPixel: function(moPixel) { return moPixel.x; }, /** * APIMethod: getYFromMapObjectPixel * * Parameters: * moPixel - {Object} MapObject Pixel format * * Returns: * {Integer} Y value of the MapObject Pixel */ getYFromMapObjectPixel: function(moPixel) { return moPixel.y; }, CLASS_NAME: "OpenLayers.Layer.Google" }); /** * Property: OpenLayers.Layer.Google.cache * {Object} Cache for elements that should only be created once per map. */ OpenLayers.Layer.Google.cache = {}; /** * Constant: OpenLayers.Layer.Google.v2 * * Mixin providing functionality specific to the Google Maps API v2. * * This API has been deprecated by Google. * Developers are encouraged to migrate to v3 of the API; support for this * is provided by <OpenLayers.Layer.Google.v3> */ OpenLayers.Layer.Google.v2 = { /** * Property: termsOfUse * {DOMElement} Div for Google's copyright and terms of use link */ termsOfUse: null, /** * Property: poweredBy * {DOMElement} Div for Google's powered by logo and link */ poweredBy: null, /** * Property: dragObject * {GDraggableObject} Since 2.93, Google has exposed the ability to get * the maps GDraggableObject. We can now use this for smooth panning */ dragObject: null, /** * Method: loadMapObject * Load the GMap and register appropriate event listeners. If we can't * load GMap2, then display a warning message. */ loadMapObject:function() { if (!this.type) { this.type = G_NORMAL_MAP; } var mapObject, termsOfUse, poweredBy; var cache = OpenLayers.Layer.Google.cache[this.map.id]; if (cache) { // there are already Google layers added to this map mapObject = cache.mapObject; termsOfUse = cache.termsOfUse; poweredBy = cache.poweredBy; // increment the layer count ++cache.count; } else { // this is the first Google layer for this map var container = this.map.viewPortDiv; var div = document.createElement("div"); div.id = this.map.id + "_GMap2Container"; div.style.position = "absolute"; div.style.width = "100%"; div.style.height = "100%"; container.appendChild(div); // create GMap and shuffle elements try { mapObject = new GMap2(div); // move the ToS and branding stuff up to the container div termsOfUse = div.lastChild; container.appendChild(termsOfUse); termsOfUse.style.zIndex = "1100"; termsOfUse.style.right = ""; termsOfUse.style.bottom = ""; termsOfUse.className = "olLayerGoogleCopyright"; poweredBy = div.lastChild; container.appendChild(poweredBy); poweredBy.style.zIndex = "1100"; poweredBy.style.right = ""; poweredBy.style.bottom = ""; poweredBy.className = "olLayerGooglePoweredBy gmnoprint"; } catch (e) { throw(e); } // cache elements for use by any other google layers added to // this same map OpenLayers.Layer.Google.cache[this.map.id] = { mapObject: mapObject, termsOfUse: termsOfUse, poweredBy: poweredBy, count: 1 }; } this.mapObject = mapObject; this.termsOfUse = termsOfUse; this.poweredBy = poweredBy; // ensure this layer type is one of the mapObject types if (OpenLayers.Util.indexOf(this.mapObject.getMapTypes(), this.type) === -1) { this.mapObject.addMapType(this.type); } //since v 2.93 getDragObject is now available. if(typeof mapObject.getDragObject == "function") { this.dragObject = mapObject.getDragObject(); } else { this.dragPanMapObject = null; } if(this.isBaseLayer === false) { this.setGMapVisibility(this.div.style.display !== "none"); } }, /** * APIMethod: onMapResize */ onMapResize: function() { // workaround for resizing of invisible or not yet fully loaded layers // where GMap2.checkResize() does not work. We need to load the GMap // for the old div size, then checkResize(), and then call // layer.moveTo() to trigger GMap.setCenter() (which will finish // the GMap initialization). if(this.visibility && this.mapObject.isLoaded()) { this.mapObject.checkResize(); } else { if(!this._resized) { var layer = this; var handle = GEvent.addListener(this.mapObject, "load", function() { GEvent.removeListener(handle); delete layer._resized; layer.mapObject.checkResize(); layer.moveTo(layer.map.getCenter(), layer.map.getZoom()); }); } this._resized = true; } }, /** * Method: setGMapVisibility * Display the GMap container and associated elements. * * Parameters: * visible - {Boolean} Display the GMap elements. */ setGMapVisibility: function(visible) { var cache = OpenLayers.Layer.Google.cache[this.map.id]; if (cache) { var container = this.mapObject.getContainer(); if (visible === true) { this.mapObject.setMapType(this.type); container.style.display = ""; this.termsOfUse.style.left = ""; this.termsOfUse.style.display = ""; this.poweredBy.style.display = ""; cache.displayed = this.id; } else { if (cache.displayed === this.id) { delete cache.displayed; } if (!cache.displayed) { container.style.display = "none"; this.termsOfUse.style.display = "none"; // move ToU far to the left in addition to setting display // to "none", because at the end of the GMap2 load // sequence, display: none will be unset and ToU would be // visible after loading a map with a google layer that is // initially hidden. this.termsOfUse.style.left = "-9999px"; this.poweredBy.style.display = "none"; } } } }, /** * Method: getMapContainer * * Returns: * {DOMElement} the GMap container's div */ getMapContainer: function() { return this.mapObject.getContainer(); }, // // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds // /** * APIMethod: getMapObjectBoundsFromOLBounds * * Parameters: * olBounds - {<OpenLayers.Bounds>} * * Returns: * {Object} A MapObject Bounds, translated from olBounds * Returns null if null value is passed in */ getMapObjectBoundsFromOLBounds: function(olBounds) { var moBounds = null; if (olBounds != null) { var sw = this.sphericalMercator ? this.inverseMercator(olBounds.bottom, olBounds.left) : new OpenLayers.LonLat(olBounds.bottom, olBounds.left); var ne = this.sphericalMercator ? this.inverseMercator(olBounds.top, olBounds.right) : new OpenLayers.LonLat(olBounds.top, olBounds.right); moBounds = new GLatLngBounds(new GLatLng(sw.lat, sw.lon), new GLatLng(ne.lat, ne.lon)); } return moBounds; }, /************************************ * * * MapObject Interface Controls * * * ************************************/ // Get&Set Center, Zoom /** * APIMethod: setMapObjectCenter * Set the mapObject to the specified center and zoom * * Parameters: * center - {Object} MapObject LonLat format * zoom - {int} MapObject zoom format */ setMapObjectCenter: function(center, zoom) { this.mapObject.setCenter(center, zoom); }, /** * APIMethod: dragPanMapObject * * Parameters: * dX - {Integer} * dY - {Integer} */ dragPanMapObject: function(dX, dY) { this.dragObject.moveBy(new GSize(-dX, dY)); }, // LonLat - Pixel Translation /** * APIMethod: getMapObjectLonLatFromMapObjectPixel * * Parameters: * moPixel - {Object} MapObject Pixel format * * Returns: * {Object} MapObject LonLat translated from MapObject Pixel */ getMapObjectLonLatFromMapObjectPixel: function(moPixel) { return this.mapObject.fromContainerPixelToLatLng(moPixel); }, /** * APIMethod: getMapObjectPixelFromMapObjectLonLat * * Parameters: * moLonLat - {Object} MapObject LonLat format * * Returns: * {Object} MapObject Pixel transtlated from MapObject LonLat */ getMapObjectPixelFromMapObjectLonLat: function(moLonLat) { return this.mapObject.fromLatLngToContainerPixel(moLonLat); }, // Bounds /** * APIMethod: getMapObjectZoomFromMapObjectBounds * * Parameters: * moBounds - {Object} MapObject Bounds format * * Returns: * {Object} MapObject Zoom for specified MapObject Bounds */ getMapObjectZoomFromMapObjectBounds: function(moBounds) { return this.mapObject.getBoundsZoomLevel(moBounds); }, /************************************ * * * MapObject Primitives * * * ************************************/ // LonLat /** * APIMethod: getMapObjectLonLatFromLonLat * * Parameters: * lon - {Float} * lat - {Float} * * Returns: * {Object} MapObject LonLat built from lon and lat params */ getMapObjectLonLatFromLonLat: function(lon, lat) { var gLatLng; if(this.sphericalMercator) { var lonlat = this.inverseMercator(lon, lat); gLatLng = new GLatLng(lonlat.lat, lonlat.lon); } else { gLatLng = new GLatLng(lat, lon); } return gLatLng; }, // Pixel /** * APIMethod: getMapObjectPixelFromXY * * Parameters: * x - {Integer} * y - {Integer} * * Returns: * {Object} MapObject Pixel from x and y parameters */ getMapObjectPixelFromXY: function(x, y) { return new GPoint(x, y); } }; /* ====================================================================== OpenLayers/Layer/GeoRSS.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/Markers.js * @requires OpenLayers/Request/XMLHttpRequest.js */ /** * Class: OpenLayers.Layer.GeoRSS * Add GeoRSS Point features to your map. * * Inherits from: * - <OpenLayers.Layer.Markers> */ OpenLayers.Layer.GeoRSS = OpenLayers.Class(OpenLayers.Layer.Markers, { /** * Property: location * {String} store url of text file */ location: null, /** * Property: features * {Array(<OpenLayers.Feature>)} */ features: null, /** * APIProperty: formatOptions * {Object} Hash of options which should be passed to the format when it is * created. Must be passed in the constructor. */ formatOptions: null, /** * Property: selectedFeature * {<OpenLayers.Feature>} */ selectedFeature: null, /** * APIProperty: icon * {<OpenLayers.Icon>}. This determines the Icon to be used on the map * for this GeoRSS layer. */ icon: null, /** * APIProperty: popupSize * {<OpenLayers.Size>} This determines the size of GeoRSS popups. If * not provided, defaults to 250px by 120px. */ popupSize: null, /** * APIProperty: useFeedTitle * {Boolean} Set layer.name to the first <title> element in the feed. Default is true. */ useFeedTitle: true, /** * Constructor: OpenLayers.Layer.GeoRSS * Create a GeoRSS Layer. * * Parameters: * name - {String} * location - {String} * options - {Object} */ initialize: function(name, location, options) { OpenLayers.Layer.Markers.prototype.initialize.apply(this, [name, options]); this.location = location; this.features = []; }, /** * Method: destroy */ destroy: function() { // Warning: Layer.Markers.destroy() must be called prior to calling // clearFeatures() here, otherwise we leak memory. Indeed, if // Layer.Markers.destroy() is called after clearFeatures(), it won't be // able to remove the marker image elements from the layer's div since // the markers will have been destroyed by clearFeatures(). OpenLayers.Layer.Markers.prototype.destroy.apply(this, arguments); this.clearFeatures(); this.features = null; }, /** * Method: loadRSS * Start the load of the RSS data. Don't do this when we first add the layer, * since we may not be visible at any point, and it would therefore be a waste. */ loadRSS: function() { if (!this.loaded) { this.events.triggerEvent("loadstart"); OpenLayers.Request.GET({ url: this.location, success: this.parseData, scope: this }); this.loaded = true; } }, /** * Method: moveTo * If layer is visible and RSS has not been loaded, load RSS. * * Parameters: * bounds - {Object} * zoomChanged - {Object} * minor - {Object} */ moveTo:function(bounds, zoomChanged, minor) { OpenLayers.Layer.Markers.prototype.moveTo.apply(this, arguments); if(this.visibility && !this.loaded){ this.loadRSS(); } }, /** * Method: parseData * Parse the data returned from the Events call. * * Parameters: * ajaxRequest - {<OpenLayers.Request.XMLHttpRequest>} */ parseData: function(ajaxRequest) { var doc = ajaxRequest.responseXML; if (!doc || !doc.documentElement) { doc = OpenLayers.Format.XML.prototype.read(ajaxRequest.responseText); } if (this.useFeedTitle) { var name = null; try { name = doc.getElementsByTagNameNS('*', 'title')[0].firstChild.nodeValue; } catch (e) { try { name = doc.getElementsByTagName('title')[0].firstChild.nodeValue; } catch (e) {} } if (name) { this.setName(name); } } var options = {}; OpenLayers.Util.extend(options, this.formatOptions); if (this.map && !this.projection.equals(this.map.getProjectionObject())) { options.externalProjection = this.projection; options.internalProjection = this.map.getProjectionObject(); } var format = new OpenLayers.Format.GeoRSS(options); var features = format.read(doc); for (var i=0, len=features.length; i<len; i++) { var data = {}; var feature = features[i]; // we don't support features with no geometry in the GeoRSS // layer at this time. if (!feature.geometry) { continue; } var title = feature.attributes.title ? feature.attributes.title : "Untitled"; var description = feature.attributes.description ? feature.attributes.description : "No description."; var link = feature.attributes.link ? feature.attributes.link : ""; var location = feature.geometry.getBounds().getCenterLonLat(); data.icon = this.icon == null ? OpenLayers.Marker.defaultIcon() : this.icon.clone(); data.popupSize = this.popupSize ? this.popupSize.clone() : new OpenLayers.Size(250, 120); if (title || description) { // we have supplemental data, store them. data.title = title; data.description = description; var contentHTML = '<div class="olLayerGeoRSSClose">[x]</div>'; contentHTML += '<div class="olLayerGeoRSSTitle">'; if (link) { contentHTML += '<a class="link" href="'+link+'" target="_blank">'; } contentHTML += title; if (link) { contentHTML += '</a>'; } contentHTML += '</div>'; contentHTML += '<div style="" class="olLayerGeoRSSDescription">'; contentHTML += description; contentHTML += '</div>'; data['popupContentHTML'] = contentHTML; } var feature = new OpenLayers.Feature(this, location, data); this.features.push(feature); var marker = feature.createMarker(); marker.events.register('click', feature, this.markerClick); this.addMarker(marker); } this.events.triggerEvent("loadend"); }, /** * Method: markerClick * * Parameters: * evt - {Event} */ markerClick: function(evt) { var sameMarkerClicked = (this == this.layer.selectedFeature); this.layer.selectedFeature = (!sameMarkerClicked) ? this : null; for(var i=0, len=this.layer.map.popups.length; i<len; i++) { this.layer.map.removePopup(this.layer.map.popups[i]); } if (!sameMarkerClicked) { var popup = this.createPopup(); OpenLayers.Event.observe(popup.div, "click", OpenLayers.Function.bind(function() { for(var i=0, len=this.layer.map.popups.length; i<len; i++) { this.layer.map.removePopup(this.layer.map.popups[i]); } }, this) ); this.layer.map.addPopup(popup); } OpenLayers.Event.stop(evt); }, /** * Method: clearFeatures * Destroy all features in this layer. */ clearFeatures: function() { if (this.features != null) { while(this.features.length > 0) { var feature = this.features[0]; OpenLayers.Util.removeItem(this.features, feature); feature.destroy(); } } }, CLASS_NAME: "OpenLayers.Layer.GeoRSS" }); /* ====================================================================== OpenLayers/Layer/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/PointGrid.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 * @requires OpenLayers/Geometry/Polygon.js */ /** * Class: OpenLayers.Layer.PointGrid * A point grid layer dynamically generates a regularly spaced grid of point * features. This is a specialty layer for cases where an application needs * a regular grid of points. It can be used, for example, in an editing * environment to snap to a grid. * * Create a new vector layer with the <OpenLayers.Layer.PointGrid> constructor. * (code) * // create a grid with points spaced at 10 map units * var points = new OpenLayers.Layer.PointGrid({dx: 10, dy: 10}); * * // create a grid with different x/y spacing rotated 15 degrees clockwise. * var points = new OpenLayers.Layer.PointGrid({dx: 5, dy: 10, rotation: 15}); * (end) * * Inherits from: * - <OpenLayers.Layer.Vector> */ OpenLayers.Layer.PointGrid = OpenLayers.Class(OpenLayers.Layer.Vector, { /** * APIProperty: dx * {Number} Point grid spacing in the x-axis direction (map units). * Read-only. Use the <setSpacing> method to modify this value. */ dx: null, /** * APIProperty: dy * {Number} Point grid spacing in the y-axis direction (map units). * Read-only. Use the <setSpacing> method to modify this value. */ dy: null, /** * APIProperty: ratio * {Number} Ratio of the desired grid size to the map viewport size. * Default is 1.5. Larger ratios mean the grid is recalculated less often * while panning. The <maxFeatures> setting has precedence when determining * grid size. Read-only. Use the <setRatio> method to modify this value. */ ratio: 1.5, /** * APIProperty: maxFeatures * {Number} The maximum number of points to generate in the grid. Default * is 250. Read-only. Use the <setMaxFeatures> method to modify this value. */ maxFeatures: 250, /** * APIProperty: rotation * {Number} Grid rotation (in degrees clockwise from the positive x-axis). * Default is 0. Read-only. Use the <setRotation> method to modify this * value. */ rotation: 0, /** * APIProperty: origin * {<OpenLayers.LonLat>} Grid origin. The grid lattice will be aligned with * the origin. If not set at construction, the center of the map's maximum * extent is used. Read-only. Use the <setOrigin> method to modify this * value. */ origin: null, /** * Property: gridBounds * {<OpenLayers.Bounds>} Internally cached grid bounds (with optional * rotation applied). */ gridBounds: null, /** * Constructor: OpenLayers.Layer.PointGrid * Creates a new point grid layer. * * Parameters: * config - {Object} An object containing all configuration properties for * the layer. The <dx> and <dy> properties are required to be set at * construction. Any other layer properties may be set in this object. */ initialize: function(config) { config = config || {}; OpenLayers.Layer.Vector.prototype.initialize.apply(this, [config.name, config]); }, /** * Method: setMap * The layer has been added to the map. * * Parameters: * map - {<OpenLayers.Map>} */ setMap: function(map) { OpenLayers.Layer.Vector.prototype.setMap.apply(this, arguments); map.events.register("moveend", this, this.onMoveEnd); }, /** * Method: removeMap * The layer has been removed from the map. * * Parameters: * map - {<OpenLayers.Map>} */ removeMap: function(map) { map.events.unregister("moveend", this, this.onMoveEnd); OpenLayers.Layer.Vector.prototype.removeMap.apply(this, arguments); }, /** * APIMethod: setRatio * Set the grid <ratio> property and update the grid. Can only be called * after the layer has been added to a map with a center/extent. * * Parameters: * ratio - {Number} */ setRatio: function(ratio) { this.ratio = ratio; this.updateGrid(true); }, /** * APIMethod: setMaxFeatures * Set the grid <maxFeatures> property and update the grid. Can only be * called after the layer has been added to a map with a center/extent. * * Parameters: * maxFeatures - {Number} */ setMaxFeatures: function(maxFeatures) { this.maxFeatures = maxFeatures; this.updateGrid(true); }, /** * APIMethod: setSpacing * Set the grid <dx> and <dy> properties and update the grid. If only one * argument is provided, it will be set as <dx> and <dy>. Can only be * called after the layer has been added to a map with a center/extent. * * Parameters: * dx - {Number} * dy - {Number} */ setSpacing: function(dx, dy) { this.dx = dx; this.dy = dy || dx; this.updateGrid(true); }, /** * APIMethod: setOrigin * Set the grid <origin> property and update the grid. Can only be called * after the layer has been added to a map with a center/extent. * * Parameters: * origin - {<OpenLayers.LonLat>} */ setOrigin: function(origin) { this.origin = origin; this.updateGrid(true); }, /** * APIMethod: getOrigin * Get the grid <origin> property. * * Returns: * {<OpenLayers.LonLat>} The grid origin. */ getOrigin: function() { if (!this.origin) { this.origin = this.map.getExtent().getCenterLonLat(); } return this.origin; }, /** * APIMethod: setRotation * Set the grid <rotation> property and update the grid. Rotation values * are in degrees clockwise from the positive x-axis (negative values * for counter-clockwise rotation). Can only be called after the layer * has been added to a map with a center/extent. * * Parameters: * rotation - {Number} Degrees clockwise from the positive x-axis. */ setRotation: function(rotation) { this.rotation = rotation; this.updateGrid(true); }, /** * Method: onMoveEnd * Listener for map "moveend" events. */ onMoveEnd: function() { this.updateGrid(); }, /** * Method: getViewBounds * Gets the (potentially rotated) view bounds for grid calculations. * * Returns: * {<OpenLayers.Bounds>} */ getViewBounds: function() { var bounds = this.map.getExtent(); if (this.rotation) { var origin = this.getOrigin(); var rotationOrigin = new OpenLayers.Geometry.Point(origin.lon, origin.lat); var rect = bounds.toGeometry(); rect.rotate(-this.rotation, rotationOrigin); bounds = rect.getBounds(); } return bounds; }, /** * Method: updateGrid * Update the grid. * * Parameters: * force - {Boolean} Update the grid even if the previous bounds are still * valid. */ updateGrid: function(force) { if (force || this.invalidBounds()) { var viewBounds = this.getViewBounds(); var origin = this.getOrigin(); var rotationOrigin = new OpenLayers.Geometry.Point(origin.lon, origin.lat); var viewBoundsWidth = viewBounds.getWidth(); var viewBoundsHeight = viewBounds.getHeight(); var aspectRatio = viewBoundsWidth / viewBoundsHeight; var maxHeight = Math.sqrt(this.dx * this.dy * this.maxFeatures / aspectRatio); var maxWidth = maxHeight * aspectRatio; var gridWidth = Math.min(viewBoundsWidth * this.ratio, maxWidth); var gridHeight = Math.min(viewBoundsHeight * this.ratio, maxHeight); var center = viewBounds.getCenterLonLat(); this.gridBounds = new OpenLayers.Bounds( center.lon - (gridWidth / 2), center.lat - (gridHeight / 2), center.lon + (gridWidth / 2), center.lat + (gridHeight / 2) ); var rows = Math.floor(gridHeight / this.dy); var cols = Math.floor(gridWidth / this.dx); var gridLeft = origin.lon + (this.dx * Math.ceil((this.gridBounds.left - origin.lon) / this.dx)); var gridBottom = origin.lat + (this.dy * Math.ceil((this.gridBounds.bottom - origin.lat) / this.dy)); var features = new Array(rows * cols); var x, y, point; for (var i=0; i<cols; ++i) { x = gridLeft + (i * this.dx); for (var j=0; j<rows; ++j) { y = gridBottom + (j * this.dy); point = new OpenLayers.Geometry.Point(x, y); if (this.rotation) { point.rotate(this.rotation, rotationOrigin); } features[(i*rows)+j] = new OpenLayers.Feature.Vector(point); } } this.destroyFeatures(this.features, {silent: true}); this.addFeatures(features, {silent: true}); } }, /** * Method: invalidBounds * Determine whether the previously generated point grid is invalid. * This occurs when the map bounds extends beyond the previously * generated grid bounds. * * Returns: * {Boolean} */ invalidBounds: function() { return !this.gridBounds || !this.gridBounds.containsBounds(this.getViewBounds()); }, CLASS_NAME: "OpenLayers.Layer.PointGrid" }); /* ====================================================================== OpenLayers/Layer/PointTrack.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.PointTrack * Vector layer to display ordered point features as a line, creating one * LineString feature for each pair of two points. * * Inherits from: * - <OpenLayers.Layer.Vector> */ OpenLayers.Layer.PointTrack = OpenLayers.Class(OpenLayers.Layer.Vector, { /** * APIProperty: dataFrom * {<OpenLayers.Layer.PointTrack.TARGET_NODE>} or * {<OpenLayers.Layer.PointTrack.SOURCE_NODE>} optional. If the lines * should get the data/attributes from one of the two points it is * composed of, which one should it be? */ dataFrom: null, /** * APIProperty: styleFrom * {<OpenLayers.Layer.PointTrack.TARGET_NODE>} or * {<OpenLayers.Layer.PointTrack.SOURCE_NODE>} optional. If the lines * should get the style from one of the two points it is composed of, * which one should it be? */ styleFrom: null, /** * Constructor: OpenLayers.PointTrack * Constructor for a new OpenLayers.PointTrack instance. * * Parameters: * name - {String} name of the layer * options - {Object} Optional object with properties to tag onto the * instance. */ /** * APIMethod: addNodes * Adds point features that will be used to create lines from, using point * pairs. The first point of a pair will be the source node, the second * will be the target node. * * Parameters: * pointFeatures - {Array(<OpenLayers.Feature>)} * options - {Object} * * Supported options: * silent - {Boolean} true to suppress (before)feature(s)added events */ addNodes: function(pointFeatures, options) { if (pointFeatures.length < 2) { throw new Error("At least two point features have to be added to " + "create a line from"); } var lines = new Array(pointFeatures.length-1); var pointFeature, startPoint, endPoint; for(var i=0, len=pointFeatures.length; i<len; i++) { pointFeature = pointFeatures[i]; endPoint = pointFeature.geometry; if (!endPoint) { var lonlat = pointFeature.lonlat; endPoint = new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat); } else if(endPoint.CLASS_NAME != "OpenLayers.Geometry.Point") { throw new TypeError("Only features with point geometries are supported."); } if(i > 0) { var attributes = (this.dataFrom != null) ? (pointFeatures[i+this.dataFrom].data || pointFeatures[i+this.dataFrom].attributes) : null; var style = (this.styleFrom != null) ? (pointFeatures[i+this.styleFrom].style) : null; var line = new OpenLayers.Geometry.LineString([startPoint, endPoint]); lines[i-1] = new OpenLayers.Feature.Vector(line, attributes, style); } startPoint = endPoint; } this.addFeatures(lines, options); }, CLASS_NAME: "OpenLayers.Layer.PointTrack" }); /** * Constant: OpenLayers.Layer.PointTrack.SOURCE_NODE * {Number} value for <OpenLayers.Layer.PointTrack.dataFrom> and * <OpenLayers.Layer.PointTrack.styleFrom> */ OpenLayers.Layer.PointTrack.SOURCE_NODE = -1; /** * Constant: OpenLayers.Layer.PointTrack.TARGET_NODE * {Number} value for <OpenLayers.Layer.PointTrack.dataFrom> and * <OpenLayers.Layer.PointTrack.styleFrom> */ OpenLayers.Layer.PointTrack.TARGET_NODE = 0; /** * Constant: OpenLayers.Layer.PointTrack.dataFrom * {Object} with the following keys - *deprecated* * - SOURCE_NODE: take data/attributes from the source node of the line * - TARGET_NODE: take data/attributes from the target node of the line */ OpenLayers.Layer.PointTrack.dataFrom = {'SOURCE_NODE': -1, 'TARGET_NODE': 0}; /* ====================================================================== OpenLayers/Layer/TileCache.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.TileCache * A read only TileCache layer. Used to requests tiles cached by TileCache in * a web accessible cache. This means that you have to pre-populate your * cache before this layer can be used. It is meant only to read tiles * created by TileCache, and not to make calls to TileCache for tile * creation. Create a new instance with the * <OpenLayers.Layer.TileCache> constructor. * * Inherits from: * - <OpenLayers.Layer.Grid> */ OpenLayers.Layer.TileCache = OpenLayers.Class(OpenLayers.Layer.Grid, { /** * APIProperty: isBaseLayer * {Boolean} Treat this layer as a base layer. Default is true. */ isBaseLayer: true, /** * APIProperty: format * {String} Mime type of the images returned. Default is image/png. */ format: 'image/png', /** * 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. (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.TileCache * Create a new read only TileCache layer. * * Parameters: * name - {String} Name of the layer displayed in the interface * url - {String} Location of the web accessible cache (not the location of * your tilecache script!) * layername - {String} Layer name as defined in the TileCache * configuration * options - {Object} Optional object with properties to be set on the * layer. Note that you should speficy your resolutions to match * your TileCache configuration. This can be done by setting * the resolutions array directly (here or on the map), by setting * maxResolution and numZoomLevels, or by using scale based properties. */ initialize: function(name, url, layername, options) { this.layername = layername; OpenLayers.Layer.Grid.prototype.initialize.apply(this, [name, url, {}, options]); this.extension = this.format.split('/')[1].toLowerCase(); this.extension = (this.extension == 'jpg') ? 'jpeg' : this.extension; }, /** * APIMethod: clone * obj - {Object} * * Returns: * {<OpenLayers.Layer.TileCache>} An exact clone of this * <OpenLayers.Layer.TileCache> */ clone: function (obj) { if (obj == null) { obj = new OpenLayers.Layer.TileCache(this.name, this.url, this.layername, this.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; }, /** * Method: getURL * * Parameters: * bounds - {<OpenLayers.Bounds>} * * Returns: * {String} A string with the layer's url and parameters and also the * passed-in bounds and appropriate tile size specified as parameters. */ getURL: function(bounds) { var res = this.getServerResolution(); var bbox = this.maxExtent; var size = this.tileSize; var tileX = Math.round((bounds.left - bbox.left) / (res * size.w)); var tileY = Math.round((bounds.bottom - bbox.bottom) / (res * size.h)); var tileZ = this.serverResolutions != null ? OpenLayers.Util.indexOf(this.serverResolutions, res) : this.map.getZoom(); var components = [ this.layername, OpenLayers.Number.zeroPad(tileZ, 2), OpenLayers.Number.zeroPad(parseInt(tileX / 1000000), 3), OpenLayers.Number.zeroPad((parseInt(tileX / 1000) % 1000), 3), OpenLayers.Number.zeroPad((parseInt(tileX) % 1000), 3), OpenLayers.Number.zeroPad(parseInt(tileY / 1000000), 3), OpenLayers.Number.zeroPad((parseInt(tileY / 1000) % 1000), 3), OpenLayers.Number.zeroPad((parseInt(tileY) % 1000), 3) + '.' + this.extension ]; var path = components.join('/'); var url = this.url; if (OpenLayers.Util.isArray(url)) { url = this.selectUrl(path, url); } url = (url.charAt(url.length - 1) == '/') ? url : url + '/'; return url + path; }, CLASS_NAME: "OpenLayers.Layer.TileCache" }); /* ====================================================================== OpenLayers/Layer/ArcGISCache.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.ArcGISCache * Layer for accessing cached map tiles from an ArcGIS Server style mapcache. * Tile must already be cached for this layer to access it. This does not require * ArcGIS Server itself. * * A few attempts have been made at this kind of layer before. See * http://trac.osgeo.org/openlayers/ticket/1967 * and * http://trac.osgeo.org/openlayers/browser/sandbox/tschaub/arcgiscache/lib/OpenLayers/Layer/ArcGISCache.js * * Typically the problem encountered is that the tiles seem to "jump around". * This is due to the fact that the actual max extent for the tiles on AGS layers * changes at each zoom level due to the way these caches are constructed. * We have attempted to use the resolutions, tile size, and tile origin * from the cache meta data to make the appropriate changes to the max extent * of the tile to compensate for this behavior. This must be done as zoom levels change * and before tiles are requested, which is why methods from base classes are overridden. * * For reference, you can access mapcache meta data in two ways. For accessing a * mapcache through ArcGIS Server, you can simply go to the landing page for the * layer. (ie. http://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer) * For accessing it directly through HTTP, there should always be a conf.xml file * in the root directory. * (ie. http://serverx.esri.com/arcgiscache/DG_County_roads_yesA_backgroundDark/Layers/conf.xml) * *Inherits from: * - <OpenLayers.Layer.XYZ> */ OpenLayers.Layer.ArcGISCache = OpenLayers.Class(OpenLayers.Layer.XYZ, { /** * APIProperty: url * {String | Array} The base URL for the layer cache. You can also * provide a list of URL strings for the layer if your cache is * available from multiple origins. This must be set before the layer * is drawn. */ url: null, /** * APIProperty: tileOrigin * {<OpenLayers.LonLat>} The location of the tile origin for the cache. * An ArcGIS cache has it's origin at the upper-left (lowest x value * and highest y value of the coordinate system). The units for the * tile origin should be the same as the units for the cached data. */ tileOrigin: null, /** * APIProperty: tileSize * {<OpenLayers.Size>} This size of each tile. Defaults to 256 by 256 pixels. */ tileSize: new OpenLayers.Size(256, 256), /** * APIProperty: useAGS * {Boolean} Indicates if we are going to be accessing the ArcGIS Server (AGS) * cache via an AGS MapServer or directly through HTTP. When accessing via * AGS the path structure uses a standard z/y/x structure. But AGS actually * stores the tile images on disk using a hex based folder structure that looks * like "http://example.com/mylayer/L00/R00000000/C00000000.png". Learn more * about this here: * http://blogs.esri.com/Support/blogs/mappingcenter/archive/2010/08/20/Checking-Your-Local-Cache-Folders.aspx * Defaults to true; */ useArcGISServer: true, /** * APIProperty: type * {String} Image type for the layer. This becomes the filename extension * in tile requests. Default is "png" (generating a url like * "http://example.com/mylayer/L00/R00000000/C00000000.png"). */ type: 'png', /** * APIProperty: useScales * {Boolean} Optional override to indicate that the layer should use 'scale' information * returned from the server capabilities object instead of 'resolution' information. * This can be important if your tile server uses an unusual DPI for the tiles. */ useScales: false, /** * APIProperty: overrideDPI * {Boolean} Optional override to change the OpenLayers.DOTS_PER_INCH setting based * on the tile information in the server capabilities object. This can be useful * if your server has a non-standard DPI setting on its tiles, and you're only using * tiles with that DPI. This value is used while OpenLayers is calculating resolution * using scales, and is not necessary if you have resolution information. (This is * typically the case) Regardless, this setting can be useful, but is dangerous * because it will impact other layers while calculating resolution. Only use this * if you know what you are doing. (See OpenLayers.Util.getResolutionFromScale) */ overrideDPI: false, /** * Constructor: OpenLayers.Layer.ArcGISCache * Creates a new instance of this class * * Parameters: * name - {String} * url - {String} * options - {Object} extra layer options */ initialize: function(name, url, options) { OpenLayers.Layer.XYZ.prototype.initialize.apply(this, arguments); if (this.resolutions) { this.serverResolutions = this.resolutions; this.maxExtent = this.getMaxExtentForResolution(this.resolutions[0]); } // this block steps through translating the values from the server layer JSON // capabilities object into values that we can use. This is also a helpful // reference when configuring this layer directly. if (this.layerInfo) { // alias the object var info = this.layerInfo; // build our extents var startingTileExtent = new OpenLayers.Bounds( info.fullExtent.xmin, info.fullExtent.ymin, info.fullExtent.xmax, info.fullExtent.ymax ); // set our projection based on the given spatial reference. // esri uses slightly different IDs, so this may not be comprehensive this.projection = 'EPSG:' + info.spatialReference.wkid; this.sphericalMercator = (info.spatialReference.wkid == 102100); // convert esri units into openlayers units (basic feet or meters only) this.units = (info.units == "esriFeet") ? 'ft' : 'm'; // optional extended section based on whether or not the server returned // specific tile information if (!!info.tileInfo) { // either set the tiles based on rows/columns, or specific width/height this.tileSize = new OpenLayers.Size( info.tileInfo.width || info.tileInfo.cols, info.tileInfo.height || info.tileInfo.rows ); // this must be set when manually configuring this layer this.tileOrigin = new OpenLayers.LonLat( info.tileInfo.origin.x, info.tileInfo.origin.y ); var upperLeft = new OpenLayers.Geometry.Point( startingTileExtent.left, startingTileExtent.top ); var bottomRight = new OpenLayers.Geometry.Point( startingTileExtent.right, startingTileExtent.bottom ); if (this.useScales) { this.scales = []; } else { this.resolutions = []; } this.lods = []; for(var key in info.tileInfo.lods) { if (info.tileInfo.lods.hasOwnProperty(key)) { var lod = info.tileInfo.lods[key]; if (this.useScales) { this.scales.push(lod.scale); } else { this.resolutions.push(lod.resolution); } var start = this.getContainingTileCoords(upperLeft, lod.resolution); lod.startTileCol = start.x; lod.startTileRow = start.y; var end = this.getContainingTileCoords(bottomRight, lod.resolution); lod.endTileCol = end.x; lod.endTileRow = end.y; this.lods.push(lod); } } this.maxExtent = this.calculateMaxExtentWithLOD(this.lods[0]); this.serverResolutions = this.resolutions; if (this.overrideDPI && info.tileInfo.dpi) { // see comment above for 'overrideDPI' OpenLayers.DOTS_PER_INCH = info.tileInfo.dpi; } } } }, /** * Method: getContainingTileCoords * Calculates the x/y pixel corresponding to the position of the tile * that contains the given point and for the for the given resolution. * * Parameters: * point - {<OpenLayers.Geometry.Point>} * res - {Float} The resolution for which to compute the extent. * * Returns: * {<OpenLayers.Pixel>} The x/y pixel corresponding to the position * of the upper left tile for the given resolution. */ getContainingTileCoords: function(point, res) { return new OpenLayers.Pixel( Math.max(Math.floor((point.x - this.tileOrigin.lon) / (this.tileSize.w * res)),0), Math.max(Math.floor((this.tileOrigin.lat - point.y) / (this.tileSize.h * res)),0) ); }, /** * Method: calculateMaxExtentWithLOD * Given a Level of Detail object from the server, this function * calculates the actual max extent * * Parameters: * lod - {Object} a Level of Detail Object from the server capabilities object representing a particular zoom level * * Returns: * {<OpenLayers.Bounds>} The actual extent of the tiles for the given zoom level */ calculateMaxExtentWithLOD: function(lod) { // the max extent we're provided with just overlaps some tiles // our real extent is the bounds of all the tiles we touch var numTileCols = (lod.endTileCol - lod.startTileCol) + 1; var numTileRows = (lod.endTileRow - lod.startTileRow) + 1; var minX = this.tileOrigin.lon + (lod.startTileCol * this.tileSize.w * lod.resolution); var maxX = minX + (numTileCols * this.tileSize.w * lod.resolution); var maxY = this.tileOrigin.lat - (lod.startTileRow * this.tileSize.h * lod.resolution); var minY = maxY - (numTileRows * this.tileSize.h * lod.resolution); return new OpenLayers.Bounds(minX, minY, maxX, maxY); }, /** * Method: calculateMaxExtentWithExtent * Given a 'suggested' max extent from the server, this function uses * information about the actual tile sizes to determine the actual * extent of the layer. * * Parameters: * extent - {<OpenLayers.Bounds>} The 'suggested' extent for the layer * res - {Float} The resolution for which to compute the extent. * * Returns: * {<OpenLayers.Bounds>} The actual extent of the tiles for the given zoom level */ calculateMaxExtentWithExtent: function(extent, res) { var upperLeft = new OpenLayers.Geometry.Point(extent.left, extent.top); var bottomRight = new OpenLayers.Geometry.Point(extent.right, extent.bottom); var start = this.getContainingTileCoords(upperLeft, res); var end = this.getContainingTileCoords(bottomRight, res); var lod = { resolution: res, startTileCol: start.x, startTileRow: start.y, endTileCol: end.x, endTileRow: end.y }; return this.calculateMaxExtentWithLOD(lod); }, /** * Method: getUpperLeftTileCoord * Calculates the x/y pixel corresponding to the position * of the upper left tile for the given resolution. * * Parameters: * res - {Float} The resolution for which to compute the extent. * * Returns: * {<OpenLayers.Pixel>} The x/y pixel corresponding to the position * of the upper left tile for the given resolution. */ getUpperLeftTileCoord: function(res) { var upperLeft = new OpenLayers.Geometry.Point( this.maxExtent.left, this.maxExtent.top); return this.getContainingTileCoords(upperLeft, res); }, /** * Method: getLowerRightTileCoord * Calculates the x/y pixel corresponding to the position * of the lower right tile for the given resolution. * * Parameters: * res - {Float} The resolution for which to compute the extent. * * Returns: * {<OpenLayers.Pixel>} The x/y pixel corresponding to the position * of the lower right tile for the given resolution. */ getLowerRightTileCoord: function(res) { var bottomRight = new OpenLayers.Geometry.Point( this.maxExtent.right, this.maxExtent.bottom); return this.getContainingTileCoords(bottomRight, res); }, /** * Method: getMaxExtentForResolution * Since the max extent of a set of tiles can change from zoom level * to zoom level, we need to be able to calculate that max extent * for a given resolution. * * Parameters: * res - {Float} The resolution for which to compute the extent. * * Returns: * {<OpenLayers.Bounds>} The extent for this resolution */ getMaxExtentForResolution: function(res) { var start = this.getUpperLeftTileCoord(res); var end = this.getLowerRightTileCoord(res); var numTileCols = (end.x - start.x) + 1; var numTileRows = (end.y - start.y) + 1; var minX = this.tileOrigin.lon + (start.x * this.tileSize.w * res); var maxX = minX + (numTileCols * this.tileSize.w * res); var maxY = this.tileOrigin.lat - (start.y * this.tileSize.h * res); var minY = maxY - (numTileRows * this.tileSize.h * res); return new OpenLayers.Bounds(minX, minY, maxX, maxY); }, /** * APIMethod: clone * Returns an exact clone of this OpenLayers.Layer.ArcGISCache * * Parameters: * [obj] - {Object} optional object to assign the cloned instance to. * * Returns: * {<OpenLayers.Layer.ArcGISCache>} clone of this instance */ clone: function (obj) { if (obj == null) { obj = new OpenLayers.Layer.ArcGISCache(this.name, this.url, this.options); } return OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]); }, /** * Method: initGriddedTiles * * Parameters: * bounds - {<OpenLayers.Bounds>} */ initGriddedTiles: function(bounds) { delete this._tileOrigin; OpenLayers.Layer.XYZ.prototype.initGriddedTiles.apply(this, arguments); }, /** * Method: getMaxExtent * Get this layer's maximum extent. * * Returns: * {<OpenLayers.Bounds>} */ getMaxExtent: function() { var resolution = this.map.getResolution(); return this.maxExtent = this.getMaxExtentForResolution(resolution); }, /** * Method: getTileOrigin * Determine the origin for aligning the grid of tiles. * The origin will be derived from the layer's <maxExtent> property. * * Returns: * {<OpenLayers.LonLat>} The tile origin. */ getTileOrigin: function() { if (!this._tileOrigin) { var extent = this.getMaxExtent(); this._tileOrigin = new OpenLayers.LonLat(extent.left, extent.bottom); } return this._tileOrigin; }, /** * Method: getURL * Determine the URL for a tile given the tile bounds. This is should support * urls that access tiles through an ArcGIS Server MapServer or directly through * the hex folder structure using HTTP. Just be sure to set the useArcGISServer * property appropriately! This is basically the same as * 'OpenLayers.Layer.TMS.getURL', but with the addition of hex addressing, * and tile rounding. * * Parameters: * bounds - {<OpenLayers.Bounds>} * * Returns: * {String} The URL for a tile based on given bounds. */ getURL: function (bounds) { var res = this.getResolution(); // tile center var originTileX = (this.tileOrigin.lon + (res * this.tileSize.w/2)); var originTileY = (this.tileOrigin.lat - (res * this.tileSize.h/2)); var center = bounds.getCenterLonLat(); var point = { x: center.lon, y: center.lat }; var x = (Math.round(Math.abs((center.lon - originTileX) / (res * this.tileSize.w)))); var y = (Math.round(Math.abs((originTileY - center.lat) / (res * this.tileSize.h)))); var z = this.map.getZoom(); // this prevents us from getting pink tiles (non-existant tiles) if (this.lods) { var lod = this.lods[this.map.getZoom()]; if ((x < lod.startTileCol || x > lod.endTileCol) || (y < lod.startTileRow || y > lod.endTileRow)) { return null; } } else { var start = this.getUpperLeftTileCoord(res); var end = this.getLowerRightTileCoord(res); if ((x < start.x || x >= end.x) || (y < start.y || y >= end.y)) { return null; } } // Construct the url string var url = this.url; var s = '' + x + y + z; if (OpenLayers.Util.isArray(url)) { url = this.selectUrl(s, url); } // Accessing tiles through ArcGIS Server uses a different path // structure than direct access via the folder structure. if (this.useArcGISServer) { // AGS MapServers have pretty url access to tiles url = url + '/tile/${z}/${y}/${x}'; } else { // The tile images are stored using hex values on disk. x = 'C' + OpenLayers.Number.zeroPad(x, 8, 16); y = 'R' + OpenLayers.Number.zeroPad(y, 8, 16); z = 'L' + OpenLayers.Number.zeroPad(z, 2, 10); url = url + '/${z}/${y}/${x}.' + this.type; } // Write the values into our formatted url url = OpenLayers.String.format(url, {'x': x, 'y': y, 'z': z}); return OpenLayers.Util.urlAppend( url, OpenLayers.Util.getParameterString(this.params) ); }, CLASS_NAME: 'OpenLayers.Layer.ArcGISCache' }); /* ====================================================================== OpenLayers/Layer/UTFGrid.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 * @requires OpenLayers/Tile/UTFGrid.js */ /** * Class: OpenLayers.Layer.UTFGrid * This Layer reads from UTFGrid tiled data sources. Since UTFGrids are * essentially JSON-based ASCII art with attached attributes, they are not * visibly rendered. In order to use them in the map, you must add a * <OpenLayers.Control.UTFGrid> control as well. * * Example: * * (start code) * var world_utfgrid = new OpenLayers.Layer.UTFGrid({ * url: "/tiles/world_utfgrid/${z}/${x}/${y}.json", * utfgridResolution: 4, * displayInLayerSwitcher: false * ); * map.addLayer(world_utfgrid); * * var control = new OpenLayers.Control.UTFGrid({ * layers: [world_utfgrid], * handlerMode: 'move', * callback: function(dataLookup) { * // do something with returned data * } * }) * (end code) * * * Inherits from: * - <OpenLayers.Layer.XYZ> */ OpenLayers.Layer.UTFGrid = OpenLayers.Class(OpenLayers.Layer.XYZ, { /** * APIProperty: isBaseLayer * Default is false, as UTFGrids are designed to be a transparent overlay layer. */ isBaseLayer: false, /** * APIProperty: projection * {<OpenLayers.Projection>} * Source projection for the UTFGrids. Default is "EPSG:900913". */ projection: new OpenLayers.Projection("EPSG:900913"), /** * Property: useJSONP * {Boolean} * Should we use a JSONP script approach instead of a standard AJAX call? * * Set to true for using utfgrids from another server. * Avoids same-domain policy restrictions. * Note that this only works if the server accepts * the callback GET parameter and dynamically * wraps the returned json in a function call. * * Default is false */ useJSONP: false, /** * APIProperty: url * {String} * URL tempate for UTFGrid tiles. Include x, y, and z parameters. * E.g. "/tiles/${z}/${x}/${y}.json" */ /** * APIProperty: utfgridResolution * {Number} * Ratio of the pixel width to the width of a UTFGrid data point. If an * entry in the grid represents a 4x4 block of pixels, the * utfgridResolution would be 4. Default is 2 (specified in * <OpenLayers.Tile.UTFGrid>). */ /** * Property: tileClass * {<OpenLayers.Tile>} The tile class to use for this layer. * Defaults is <OpenLayers.Tile.UTFGrid>. */ tileClass: OpenLayers.Tile.UTFGrid, /** * Constructor: OpenLayers.Layer.UTFGrid * Create a new UTFGrid layer. * * Parameters: * config - {Object} Configuration properties for the layer. * * Required configuration properties: * url - {String} The url template for UTFGrid tiles. See the <url> property. */ initialize: function(options) { OpenLayers.Layer.Grid.prototype.initialize.apply( this, [options.name, options.url, {}, options] ); this.tileOptions = OpenLayers.Util.extend({ utfgridResolution: this.utfgridResolution }, this.tileOptions); }, /** * Method: createBackBuffer * The UTFGrid cannot create a back buffer, so this method is overriden. */ createBackBuffer: function() {}, /** * APIMethod: clone * Create a clone of this layer * * Parameters: * obj - {Object} Only used by a subclass of this layer. * * Returns: * {<OpenLayers.Layer.UTFGrid>} An exact clone of this OpenLayers.Layer.UTFGrid */ clone: function (obj) { if (obj == null) { obj = new OpenLayers.Layer.UTFGrid(this.getOptions()); } // get all additions from superclasses obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); return obj; }, /** * APIProperty: getFeatureInfo * Get details about a feature associated with a map location. The object * returned will have id and data properties. If the given location * doesn't correspond to a feature, null will be returned. * * Parameters: * location - {<OpenLayers.LonLat>} map location * * Returns: * {Object} Object representing the feature id and UTFGrid data * corresponding to the given map location. Returns null if the given * location doesn't hit a feature. */ getFeatureInfo: function(location) { var info = null; var tileInfo = this.getTileData(location); if (tileInfo && tileInfo.tile) { info = tileInfo.tile.getFeatureInfo(tileInfo.i, tileInfo.j); } return info; }, /** * APIMethod: getFeatureId * Get the identifier for the feature associated with a map location. * * Parameters: * location - {<OpenLayers.LonLat>} map location * * Returns: * {String} The feature identifier corresponding to the given map location. * Returns null if the location doesn't hit a feature. */ getFeatureId: function(location) { var id = null; var info = this.getTileData(location); if (info.tile) { id = info.tile.getFeatureId(info.i, info.j); } return id; }, CLASS_NAME: "OpenLayers.Layer.UTFGrid" }); /* ====================================================================== 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/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/Layer/Google/v3.js ====================================================================== */ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for * full list of contributors). Published under the 2-clause BSD license. * See license.txt in the OpenLayers distribution or repository for the * full text of the license. */ /** * @requires OpenLayers/Layer/Google.js */ /** * Constant: OpenLayers.Layer.Google.v3 * * Mixin providing functionality specific to the Google Maps API v3. * * To use this layer, you must include the GMaps v3 API in your html. * * Note that this layer configures the google.maps.map object with the * "disableDefaultUI" option set to true. Using UI controls that the Google * Maps API provides is not supported by the OpenLayers API. */ OpenLayers.Layer.Google.v3 = { /** * Constant: DEFAULTS * {Object} It is not recommended to change the properties set here. Note * that Google.v3 layers only work when sphericalMercator is set to true. * * (code) * { * sphericalMercator: true, * projection: "EPSG:900913" * } * (end) */ DEFAULTS: { sphericalMercator: true, projection: "EPSG:900913" }, /** * APIProperty: animationEnabled * {Boolean} If set to true, the transition between zoom levels will be * animated (if supported by the GMaps API for the device used). Set to * false to match the zooming experience of other layer types. Default * is true. Note that the GMaps API does not give us control over zoom * animation, so if set to false, when zooming, this will make the * layer temporarily invisible, wait until GMaps reports the map being * idle, and make it visible again. The result will be a blank layer * for a few moments while zooming. */ animationEnabled: true, /** * Method: loadMapObject * Load the GMap and register appropriate event listeners. */ loadMapObject: function() { if (!this.type) { this.type = google.maps.MapTypeId.ROADMAP; } var mapObject; var cache = OpenLayers.Layer.Google.cache[this.map.id]; if (cache) { // there are already Google layers added to this map mapObject = cache.mapObject; // increment the layer count ++cache.count; } else { // this is the first Google layer for this map // create GMap var center = this.map.getCenter(); var container = document.createElement('div'); container.className = "olForeignContainer"; container.style.width = '100%'; container.style.height = '100%'; mapObject = new google.maps.Map(container, { center: center ? new google.maps.LatLng(center.lat, center.lon) : new google.maps.LatLng(0, 0), zoom: this.map.getZoom() || 0, mapTypeId: this.type, disableDefaultUI: true, keyboardShortcuts: false, draggable: false, disableDoubleClickZoom: true, scrollwheel: false, streetViewControl: false }); var googleControl = document.createElement('div'); googleControl.style.width = '100%'; googleControl.style.height = '100%'; mapObject.controls[google.maps.ControlPosition.TOP_LEFT].push(googleControl); // cache elements for use by any other google layers added to // this same map cache = { googleControl: googleControl, mapObject: mapObject, count: 1 }; OpenLayers.Layer.Google.cache[this.map.id] = cache; } this.mapObject = mapObject; this.setGMapVisibility(this.visibility); }, /** * APIMethod: onMapResize */ onMapResize: function() { if (this.visibility) { google.maps.event.trigger(this.mapObject, "resize"); } }, /** * Method: setGMapVisibility * Display the GMap container and associated elements. * * Parameters: * visible - {Boolean} Display the GMap elements. */ setGMapVisibility: function(visible) { var cache = OpenLayers.Layer.Google.cache[this.map.id]; var map = this.map; if (cache) { var type = this.type; var layers = map.layers; var layer; for (var i=layers.length-1; i>=0; --i) { layer = layers[i]; if (layer instanceof OpenLayers.Layer.Google && layer.visibility === true && layer.inRange === true) { type = layer.type; visible = true; break; } } var container = this.mapObject.getDiv(); if (visible === true) { if (container.parentNode !== map.div) { if (!cache.rendered) { var me = this; google.maps.event.addListenerOnce(this.mapObject, 'tilesloaded', function() { cache.rendered = true; me.setGMapVisibility(me.getVisibility()); me.moveTo(me.map.getCenter()); }); } else { map.div.appendChild(container); cache.googleControl.appendChild(map.viewPortDiv); google.maps.event.trigger(this.mapObject, 'resize'); } } this.mapObject.setMapTypeId(type); } else if (cache.googleControl.hasChildNodes()) { map.div.appendChild(map.viewPortDiv); map.div.removeChild(container); } } }, /** * Method: getMapContainer * * Returns: * {DOMElement} the GMap container's div */ getMapContainer: function() { return this.mapObject.getDiv(); }, // // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds // /** * APIMethod: getMapObjectBoundsFromOLBounds * * Parameters: * olBounds - {<OpenLayers.Bounds>} * * Returns: * {Object} A MapObject Bounds, translated from olBounds * Returns null if null value is passed in */ getMapObjectBoundsFromOLBounds: function(olBounds) { var moBounds = null; if (olBounds != null) { var sw = this.sphericalMercator ? this.inverseMercator(olBounds.bottom, olBounds.left) : new OpenLayers.LonLat(olBounds.bottom, olBounds.left); var ne = this.sphericalMercator ? this.inverseMercator(olBounds.top, olBounds.right) : new OpenLayers.LonLat(olBounds.top, olBounds.right); moBounds = new google.maps.LatLngBounds( new google.maps.LatLng(sw.lat, sw.lon), new google.maps.LatLng(ne.lat, ne.lon) ); } return moBounds; }, /************************************ * * * MapObject Interface Controls * * * ************************************/ // LonLat - Pixel Translation /** * APIMethod: getMapObjectLonLatFromMapObjectPixel * * Parameters: * moPixel - {Object} MapObject Pixel format * * Returns: * {Object} MapObject LonLat translated from MapObject Pixel */ getMapObjectLonLatFromMapObjectPixel: function(moPixel) { var size = this.map.getSize(); var lon = this.getLongitudeFromMapObjectLonLat(this.mapObject.center); var lat = this.getLatitudeFromMapObjectLonLat(this.mapObject.center); var res = this.map.getResolution(); var delta_x = moPixel.x - (size.w / 2); var delta_y = moPixel.y - (size.h / 2); var lonlat = new OpenLayers.LonLat( lon + delta_x * res, lat - delta_y * res ); if (this.wrapDateLine) { lonlat = lonlat.wrapDateLine(this.maxExtent); } return this.getMapObjectLonLatFromLonLat(lonlat.lon, lonlat.lat); }, /** * APIMethod: getMapObjectPixelFromMapObjectLonLat * * Parameters: * moLonLat - {Object} MapObject LonLat format * * Returns: * {Object} MapObject Pixel transtlated from MapObject LonLat */ getMapObjectPixelFromMapObjectLonLat: function(moLonLat) { var lon = this.getLongitudeFromMapObjectLonLat(moLonLat); var lat = this.getLatitudeFromMapObjectLonLat(moLonLat); var res = this.map.getResolution(); var extent = this.map.getExtent(); return this.getMapObjectPixelFromXY((1/res * (lon - extent.left)), (1/res * (extent.top - lat))); }, /** * APIMethod: setMapObjectCenter * Set the mapObject to the specified center and zoom * * Parameters: * center - {Object} MapObject LonLat format * zoom - {int} MapObject zoom format */ setMapObjectCenter: function(center, zoom) { if (this.animationEnabled === false && zoom != this.mapObject.zoom) { var mapContainer = this.getMapContainer(); google.maps.event.addListenerOnce( this.mapObject, "idle", function() { mapContainer.style.visibility = ""; } ); mapContainer.style.visibility = "hidden"; } this.mapObject.setOptions({ center: center, zoom: zoom }); }, // Bounds /** * APIMethod: getMapObjectZoomFromMapObjectBounds * * Parameters: * moBounds - {Object} MapObject Bounds format * * Returns: * {Object} MapObject Zoom for specified MapObject Bounds */ getMapObjectZoomFromMapObjectBounds: function(moBounds) { return this.mapObject.getBoundsZoomLevel(moBounds); }, /************************************ * * * MapObject Primitives * * * ************************************/ // LonLat /** * APIMethod: getMapObjectLonLatFromLonLat * * Parameters: * lon - {Float} * lat - {Float} * * Returns: * {Object} MapObject LonLat built from lon and lat params */ getMapObjectLonLatFromLonLat: function(lon, lat) { var gLatLng; if(this.sphericalMercator) { var lonlat = this.inverseMercator(lon, lat); gLatLng = new google.maps.LatLng(lonlat.lat, lonlat.lon); } else { gLatLng = new google.maps.LatLng(lat, lon); } return gLatLng; }, // Pixel /** * APIMethod: getMapObjectPixelFromXY * * Parameters: * x - {Integer} * y - {Integer} * * Returns: * {Object} MapObject Pixel from x and y parameters */ getMapObjectPixelFromXY: function(x, y) { return new google.maps.Point(x, y); } }; /* ====================================================================== OpenLayers/Protocol/CSW.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.CSW * Used to create a versioned CSW protocol. Default version is 2.0.2. */ OpenLayers.Protocol.CSW = function(options) { options = OpenLayers.Util.applyDefaults( options, OpenLayers.Protocol.CSW.DEFAULTS ); var cls = OpenLayers.Protocol.CSW["v"+options.version.replace(/\./g, "_")]; if(!cls) { throw "Unsupported CSW version: " + options.version; } return new cls(options); }; /** * Constant: OpenLayers.Protocol.CSW.DEFAULTS */ OpenLayers.Protocol.CSW.DEFAULTS = { "version": "2.0.2" }; /* ====================================================================== OpenLayers/Protocol/SOS.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 */ /** * Function: OpenLayers.Protocol.SOS * Used to create a versioned SOS protocol. Default version is 1.0.0. * * Returns: * {<OpenLayers.Protocol>} An SOS protocol for the given version. */ OpenLayers.Protocol.SOS = function(options) { options = OpenLayers.Util.applyDefaults( options, OpenLayers.Protocol.SOS.DEFAULTS ); var cls = OpenLayers.Protocol.SOS["v"+options.version.replace(/\./g, "_")]; if(!cls) { throw "Unsupported SOS version: " + options.version; } return new cls(options); }; /** * Constant: OpenLayers.Protocol.SOS.DEFAULTS */ OpenLayers.Protocol.SOS.DEFAULTS = { "version": "1.0.0" }; /* ====================================================================== OpenLayers/Protocol/Script.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/Feature/Vector.js * @requires OpenLayers/Format/GeoJSON.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.Script * A basic Script protocol for vector layers. Create a new instance with the * <OpenLayers.Protocol.Script> constructor. A script protocol is used to * get around the same origin policy. It works with services that return * JSONP - that is, JSON wrapped in a client-specified callback. The * protocol handles fetching and parsing of feature data and sends parsed * features to the <callback> configured with the protocol. The protocol * expects features serialized as GeoJSON by default, but can be configured * to work with other formats by setting the <format> property. * * Inherits from: * - <OpenLayers.Protocol> */ OpenLayers.Protocol.Script = OpenLayers.Class(OpenLayers.Protocol, { /** * APIProperty: url * {String} Service URL. The service is expected to return serialized * features wrapped in a named callback (where the callback name is * generated by this protocol). * Read-only, set through the options passed to the constructor. */ url: null, /** * APIProperty: params * {Object} Query string parameters to be appended to the URL. * Read-only, set through the options passed to the constructor. * Example: {maxFeatures: 50} */ params: null, /** * APIProperty: callback * {Object} Function to be called when the <read> operation completes. */ callback: null, /** * APIProperty: callbackTemplate * {String} Template for creating a unique callback function name * for the registry. Should include ${id}. The ${id} variable will be * replaced with a string identifier prefixed with a "c" (e.g. c1, c2). * Default is "OpenLayers.Protocol.Script.registry.${id}". */ callbackTemplate: "OpenLayers.Protocol.Script.registry.${id}", /** * APIProperty: callbackKey * {String} The name of the query string parameter that the service * recognizes as the callback identifier. Default is "callback". * This key is used to generate the URL for the script. For example * setting <callbackKey> to "myCallback" would result in a URL like * http://example.com/?myCallback=... */ callbackKey: "callback", /** * APIProperty: callbackPrefix * {String} Where a service requires that the callback query string * parameter value is prefixed by some string, this value may be set. * For example, setting <callbackPrefix> to "foo:" would result in a * URL like http://example.com/?callback=foo:... Default is "". */ callbackPrefix: "", /** * APIProperty: scope * {Object} Optional ``this`` object for the callback. Read-only, set * through the options passed to the constructor. */ scope: null, /** * APIProperty: format * {<OpenLayers.Format>} Format for parsing features. Default is an * <OpenLayers.Format.GeoJSON> format. If an alternative is provided, * the format's read method must take an object and return an array * of features. */ format: null, /** * Property: pendingRequests * {Object} References all pending requests. Property names are script * identifiers and property values are script elements. */ pendingRequests: null, /** * APIProperty: srsInBBOX * {Boolean} Include the SRS identifier in BBOX query string parameter. * Setting this property has no effect if a custom filterToParams method * is provided. 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.Script * A class for giving layers generic Script protocol. * * Parameters: * options - {Object} Optional object whose properties will be set on the * instance. * * Valid options include: * url - {String} * params - {Object} * callback - {Function} * scope - {Object} */ initialize: function(options) { options = options || {}; this.params = {}; this.pendingRequests = {}; OpenLayers.Protocol.prototype.initialize.apply(this, arguments); if (!this.format) { this.format = new OpenLayers.Format.GeoJSON(); } if (!this.filterToParams && OpenLayers.Format.QueryStringFilter) { var format = new OpenLayers.Format.QueryStringFilter({ srsInBBOX: this.srsInBBOX }); this.filterToParams = function(filter, params) { return format.write(filter, params); }; } }, /** * 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. * filter - {<OpenLayers.Filter>} Filter to get serialized as a * query string. * * Returns: * {<OpenLayers.Protocol.Response>} A response object, whose "priv" property * references the injected script. 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 = OpenLayers.Util.applyDefaults(options, this.options); options.params = OpenLayers.Util.applyDefaults( options.params, this.options.params ); if (options.filter && this.filterToParams) { options.params = this.filterToParams( options.filter, options.params ); } var response = new OpenLayers.Protocol.Response({requestType: "read"}); var request = this.createRequest( options.url, options.params, OpenLayers.Function.bind(function(data) { response.data = data; this.handleRead(response, options); }, this) ); response.priv = request; return response; }, /** * 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, any filter will not be serialized. * * Parameters: * filter - {<OpenLayers.Filter>} filter to convert. * params - {Object} The parameters object. * * Returns: * {Object} The resulting parameters object. */ /** * Method: createRequest * Issues a request for features by creating injecting a script in the * document head. * * Parameters: * url - {String} Service URL. * params - {Object} Query string parameters. * callback - {Function} Callback to be called with resulting data. * * Returns: * {HTMLScriptElement} The script pending execution. */ createRequest: function(url, params, callback) { var id = OpenLayers.Protocol.Script.register(callback); var name = OpenLayers.String.format(this.callbackTemplate, {id: id}); params = OpenLayers.Util.extend({}, params); params[this.callbackKey] = this.callbackPrefix + name; url = OpenLayers.Util.urlAppend( url, OpenLayers.Util.getParameterString(params) ); var script = document.createElement("script"); script.type = "text/javascript"; script.src = url; script.id = "OpenLayers_Protocol_Script_" + id; this.pendingRequests[script.id] = script; var head = document.getElementsByTagName("head")[0]; head.appendChild(script); return script; }, /** * Method: destroyRequest * Remove a script node associated with a response from the document. Also * unregisters the callback and removes the script from the * <pendingRequests> object. * * Parameters: * script - {HTMLScriptElement} */ destroyRequest: function(script) { OpenLayers.Protocol.Script.unregister(script.id.split("_").pop()); delete this.pendingRequests[script.id]; if (script.parentNode) { script.parentNode.removeChild(script); } }, /** * Method: handleRead * Individual callbacks are created for read, create and update, should * a subclass need to override each one separately. * * 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) { this.handleResponse(response, options); }, /** * Method: handleResponse * Called by CRUD specific handlers. * * Parameters: * response - {<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(response, options) { if (options.callback) { if (response.data) { response.features = this.parseFeatures(response.data); response.code = OpenLayers.Protocol.Response.SUCCESS; } else { response.code = OpenLayers.Protocol.Response.FAILURE; } this.destroyRequest(response.priv); options.callback.call(options.scope, response); } }, /** * Method: parseFeatures * Read Script response body and return features. * * Parameters: * data - {Object} The data sent to the callback function by the server. * * Returns: * {Array({<OpenLayers.Feature.Vector>})} or * {<OpenLayers.Feature.Vector>} Array of features or a single feature. */ parseFeatures: function(data) { return this.format.read(data); }, /** * APIMethod: abort * Abort an ongoing request. If no response is provided, all pending * requests will be aborted. * * Parameters: * response - {<OpenLayers.Protocol.Response>} The response object returned * from a <read> request. */ abort: function(response) { if (response) { this.destroyRequest(response.priv); } else { for (var key in this.pendingRequests) { this.destroyRequest(this.pendingRequests[key]); } } }, /** * APIMethod: destroy * Clean up the protocol. */ destroy: function() { this.abort(); delete this.params; delete this.format; OpenLayers.Protocol.prototype.destroy.apply(this); }, CLASS_NAME: "OpenLayers.Protocol.Script" }); (function() { var o = OpenLayers.Protocol.Script; var counter = 0; o.registry = {}; /** * Function: OpenLayers.Protocol.Script.register * Register a callback for a newly created script. * * Parameters: * callback - {Function} The callback to be executed when the newly added * script loads. This callback will be called with a single argument * that is the JSON returned by the service. * * Returns: * {Number} An identifier for retrieving the registered callback. */ o.register = function(callback) { var id = "c"+(++counter); o.registry[id] = function() { callback.apply(this, arguments); }; return id; }; /** * Function: OpenLayers.Protocol.Script.unregister * Unregister a callback previously registered with the register function. * * Parameters: * id - {Number} The identifer returned by the register function. */ o.unregister = function(id) { delete o.registry[id]; }; })(); /* ====================================================================== 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/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/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/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/Protocol/WFS/v1_1_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_1_0.js */ /** * Class: OpenLayers.Protocol.WFS.v1_1_0 * A WFS v1.1.0 protocol for vector layers. Create a new instance with the * <OpenLayers.Protocol.WFS.v1_1_0> constructor. * * Differences from the v1.0.0 protocol: * - uses Filter Encoding 1.1.0 instead of 1.0.0 * - uses GML 3 instead of 2 if no format is provided * * Inherits from: * - <OpenLayers.Protocol.WFS.v1> */ OpenLayers.Protocol.WFS.v1_1_0 = OpenLayers.Class(OpenLayers.Protocol.WFS.v1, { /** * Property: version * {String} WFS version number. */ version: "1.1.0", /** * Constructor: OpenLayers.Protocol.WFS.v1_1_0 * A class for giving layers WFS v1.1.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'. * outputFormat - {String} Optional output format to use for WFS GetFeature * requests. This can be any format advertized by the WFS's * GetCapabilities response. If set, an appropriate readFormat also * has to be provided, unless outputFormat is GML3, GML2 or JSON. * readFormat - {<OpenLayers.Format>} An appropriate format parser if * outputFormat is none of GML3, GML2 or JSON. */ initialize: function(options) { OpenLayers.Protocol.WFS.v1.prototype.initialize.apply(this, arguments); if (this.outputFormat && !this.readFormat) { if (this.outputFormat.toLowerCase() == "gml2") { this.readFormat = new OpenLayers.Format.GML.v2({ featureType: this.featureType, featureNS: this.featureNS, geometryName: this.geometryName }); } else if (this.outputFormat.toLowerCase() == "json") { this.readFormat = new OpenLayers.Format.GeoJSON(); } } }, CLASS_NAME: "OpenLayers.Protocol.WFS.v1_1_0" }); /* ====================================================================== OpenLayers/Protocol/SOS/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/SOS.js * @requires OpenLayers/Format/SOSGetFeatureOfInterest.js */ /** * Class: OpenLayers.Protocol.SOS.v1_0_0 * An SOS v1.0.0 Protocol for vector layers. Create a new instance with the * <OpenLayers.Protocol.SOS.v1_0_0> constructor. * * Inherits from: * - <OpenLayers.Protocol> */ OpenLayers.Protocol.SOS.v1_0_0 = OpenLayers.Class(OpenLayers.Protocol, { /** * APIProperty: fois * {Array(String)} Array of features of interest (foi) */ fois: null, /** * 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, /** * Constructor: OpenLayers.Protocol.SOS * A class for giving layers an SOS 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). * fois - {Array} The features of interest (required). */ initialize: function(options) { OpenLayers.Protocol.prototype.initialize.apply(this, [options]); if(!options.format) { this.format = new OpenLayers.Format.SOSGetFeatureOfInterest( this.formatOptions); } }, /** * 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 sensor positions. This is done by * issuing one GetFeatureOfInterest request. */ read: function(options) { options = OpenLayers.Util.extend({}, options); OpenLayers.Util.applyDefaults(options, this.options || {}); var response = new OpenLayers.Protocol.Response({requestType: "read"}); var format = this.format; var data = OpenLayers.Format.XML.prototype.write.apply(format, [format.writeNode("sos:GetFeatureOfInterest", {fois: this.fois})] ); response.priv = OpenLayers.Request.POST({ url: options.url, callback: this.createCallback(this.handleRead, response, options), data: data }); return response; }, /** * 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) { if(options.callback) { var request = response.priv; if(request.status >= 200 && request.status < 300) { // success response.features = this.parseFeatures(request); response.code = OpenLayers.Protocol.Response.SUCCESS; } else { // failure response.code = OpenLayers.Protocol.Response.FAILURE; } options.callback.call(options.scope, response); } }, /** * Method: parseFeatures * Read HTTP response body and return features * * Parameters: * request - {XMLHttpRequest} The request object * * Returns: * {Array({<OpenLayers.Feature.Vector>})} Array of features */ 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); }, CLASS_NAME: "OpenLayers.Protocol.SOS.v1_0_0" }); /* ====================================================================== OpenLayers/Protocol/CSW/v2_0_2.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/CSW.js * @requires OpenLayers/Format/CSWGetRecords/v2_0_2.js */ /** * Class: OpenLayers.Protocol.CSW.v2_0_2 * CS-W (Catalogue services for the Web) version 2.0.2 protocol. * * Inherits from: * - <OpenLayers.Protocol> */ OpenLayers.Protocol.CSW.v2_0_2 = OpenLayers.Class(OpenLayers.Protocol, { /** * 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, /** * Constructor: OpenLayers.Protocol.CSW.v2_0_2 * A class for CSW version 2.0.2 protocol management. * * Parameters: * options - {Object} Optional object whose properties will be set on the * instance. */ initialize: function(options) { OpenLayers.Protocol.prototype.initialize.apply(this, [options]); if(!options.format) { this.format = new OpenLayers.Format.CSWGetRecords.v2_0_2(OpenLayers.Util.extend({ }, this.formatOptions)); } }, /** * 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); }, /** * Method: read * Construct a request for reading new records from the Catalogue. */ read: function(options) { options = OpenLayers.Util.extend({}, options); OpenLayers.Util.applyDefaults(options, this.options || {}); var response = new OpenLayers.Protocol.Response({requestType: "read"}); var data = this.format.write(options.params || 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; }, /** * Method: handleRead * Deal with response from the read request. * * Parameters: * response - {<OpenLayers.Protocol.Response>} The response object to pass * to the user callback. * This response is given a code property, and optionally a data property. * The latter represents the CSW records as returned by the call to * the CSW format read method. * options - {Object} The user options passed to the read call. */ handleRead: function(response, options) { if(options.callback) { var request = response.priv; if(request.status >= 200 && request.status < 300) { // success response.data = this.parseData(request); response.code = OpenLayers.Protocol.Response.SUCCESS; } else { // failure response.code = OpenLayers.Protocol.Response.FAILURE; } options.callback.call(options.scope, response); } }, /** * Method: parseData * Read HTTP response body and return records * * Parameters: * request - {XMLHttpRequest} The request object * * Returns: * {Object} The CSW records as returned by the call to the format read method. */ parseData: 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); }, CLASS_NAME: "OpenLayers.Protocol.CSW.v2_0_2" }); /* ====================================================================== 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/Control/ScaleLine.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.ScaleLine * The ScaleLine displays a small line indicator representing the current * map scale on the map. By default it is drawn in the lower left corner of * the map. * * Inherits from: * - <OpenLayers.Control> * * Is a very close copy of: * - <OpenLayers.Control.Scale> */ OpenLayers.Control.ScaleLine = OpenLayers.Class(OpenLayers.Control, { /** * Property: maxWidth * {Integer} Maximum width of the scale line in pixels. Default is 100. */ maxWidth: 100, /** * Property: topOutUnits * {String} Units for zoomed out on top bar. Default is km. */ topOutUnits: "km", /** * Property: topInUnits * {String} Units for zoomed in on top bar. Default is m. */ topInUnits: "m", /** * Property: bottomOutUnits * {String} Units for zoomed out on bottom bar. Default is mi. */ bottomOutUnits: "mi", /** * Property: bottomInUnits * {String} Units for zoomed in on bottom bar. Default is ft. */ bottomInUnits: "ft", /** * Property: eTop * {DOMElement} */ eTop: null, /** * Property: eBottom * {DOMElement} */ eBottom:null, /** * APIProperty: geodesic * {Boolean} Use geodesic measurement. Default is false. The recommended * setting for maps in EPSG:4326 is false, and true EPSG:900913. If set to * true, the scale will be calculated based on the horizontal size of the * pixel in the center of the map viewport. */ geodesic: false, /** * Constructor: OpenLayers.Control.ScaleLine * Create a new scale line control. * * Parameters: * options - {Object} An optional object whose properties will be used * to extend the control. */ /** * Method: draw * * Returns: * {DOMElement} */ draw: function() { OpenLayers.Control.prototype.draw.apply(this, arguments); if (!this.eTop) { // stick in the top bar this.eTop = document.createElement("div"); this.eTop.className = this.displayClass + "Top"; var theLen = this.topInUnits.length; this.div.appendChild(this.eTop); if((this.topOutUnits == "") || (this.topInUnits == "")) { this.eTop.style.visibility = "hidden"; } else { this.eTop.style.visibility = "visible"; } // and the bottom bar this.eBottom = document.createElement("div"); this.eBottom.className = this.displayClass + "Bottom"; this.div.appendChild(this.eBottom); if((this.bottomOutUnits == "") || (this.bottomInUnits == "")) { this.eBottom.style.visibility = "hidden"; } else { this.eBottom.style.visibility = "visible"; } } this.map.events.register('moveend', this, this.update); this.update(); return this.div; }, /** * Method: getBarLen * Given a number, round it down to the nearest 1,2,5 times a power of 10. * That seems a fairly useful set of number groups to use. * * Parameters: * maxLen - {float} the number we're rounding down from * * Returns: * {Float} the rounded number (less than or equal to maxLen) */ getBarLen: function(maxLen) { // nearest power of 10 lower than maxLen var digits = parseInt(Math.log(maxLen) / Math.log(10)); var pow10 = Math.pow(10, digits); // ok, find first character var firstChar = parseInt(maxLen / pow10); // right, put it into the correct bracket var barLen; if(firstChar > 5) { barLen = 5; } else if(firstChar > 2) { barLen = 2; } else { barLen = 1; } // scale it up the correct power of 10 return barLen * pow10; }, /** * Method: update * Update the size of the bars, and the labels they contain. */ update: function() { var res = this.map.getResolution(); if (!res) { return; } var curMapUnits = this.map.getUnits(); var inches = OpenLayers.INCHES_PER_UNIT; // convert maxWidth to map units var maxSizeData = this.maxWidth * res * inches[curMapUnits]; var geodesicRatio = 1; if(this.geodesic === true) { var maxSizeGeodesic = (this.map.getGeodesicPixelSize().w || 0.000001) * this.maxWidth; var maxSizeKilometers = maxSizeData / inches["km"]; geodesicRatio = maxSizeGeodesic / maxSizeKilometers; maxSizeData *= geodesicRatio; } // decide whether to use large or small scale units var topUnits; var bottomUnits; if(maxSizeData > 100000) { topUnits = this.topOutUnits; bottomUnits = this.bottomOutUnits; } else { topUnits = this.topInUnits; bottomUnits = this.bottomInUnits; } // and to map units units var topMax = maxSizeData / inches[topUnits]; var bottomMax = maxSizeData / inches[bottomUnits]; // now trim this down to useful block length var topRounded = this.getBarLen(topMax); var bottomRounded = this.getBarLen(bottomMax); // and back to display units topMax = topRounded / inches[curMapUnits] * inches[topUnits]; bottomMax = bottomRounded / inches[curMapUnits] * inches[bottomUnits]; // and to pixel units var topPx = topMax / res / geodesicRatio; var bottomPx = bottomMax / res / geodesicRatio; // now set the pixel widths // and the values inside them if (this.eBottom.style.visibility == "visible"){ this.eBottom.style.width = Math.round(bottomPx) + "px"; this.eBottom.innerHTML = bottomRounded + " " + bottomUnits ; } if (this.eTop.style.visibility == "visible"){ this.eTop.style.width = Math.round(topPx) + "px"; this.eTop.innerHTML = topRounded + " " + topUnits; } }, CLASS_NAME: "OpenLayers.Control.ScaleLine" }); /* ====================================================================== OpenLayers/Control/ZoomBox.js ====================================================================== */ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for * full list of contributors). Published under the 2-clause BSD license. * See license.txt in the OpenLayers distribution or repository for the * full text of the license. */ /** * @requires OpenLayers/Control.js * @requires OpenLayers/Handler/Box.js */ /** * Class: OpenLayers.Control.ZoomBox * The ZoomBox control enables zooming directly to a given extent, by drawing * a box on the map. The box is drawn by holding down shift, whilst dragging * the mouse. * * Inherits from: * - <OpenLayers.Control> */ OpenLayers.Control.ZoomBox = OpenLayers.Class(OpenLayers.Control, { /** * Property: type * {OpenLayers.Control.TYPE} */ type: OpenLayers.Control.TYPE_TOOL, /** * Property: out * {Boolean} Should the control be used for zooming out? */ out: false, /** * APIProperty: keyMask * {Integer} Zoom only occurs if the keyMask matches the combination of * keys down. Use bitwise operators and one or more of the * <OpenLayers.Handler> constants to construct a keyMask. Leave null if * not used mask. Default is null. */ keyMask: null, /** * APIProperty: alwaysZoom * {Boolean} Always zoom in/out when box drawn, even if the zoom level does * not change. */ alwaysZoom: false, /** * APIProperty: zoomOnClick * {Boolean} Should we zoom when no box was dragged, i.e. the user only * clicked? Default is true. */ zoomOnClick: true, /** * Method: draw */ draw: function() { this.handler = new OpenLayers.Handler.Box( this, {done: this.zoomBox}, {keyMask: this.keyMask} ); }, /** * Method: zoomBox * * Parameters: * position - {<OpenLayers.Bounds>} or {<OpenLayers.Pixel>} */ zoomBox: function (position) { if (position instanceof OpenLayers.Bounds) { var bounds, targetCenterPx = position.getCenterPixel(); if (!this.out) { var minXY = this.map.getLonLatFromPixel({ x: position.left, y: position.bottom }); var maxXY = this.map.getLonLatFromPixel({ x: position.right, y: position.top }); bounds = new OpenLayers.Bounds(minXY.lon, minXY.lat, maxXY.lon, maxXY.lat); } else { var pixWidth = position.right - position.left; var pixHeight = position.bottom - position.top; var zoomFactor = Math.min((this.map.size.h / pixHeight), (this.map.size.w / pixWidth)); var extent = this.map.getExtent(); var center = this.map.getLonLatFromPixel(targetCenterPx); var xmin = center.lon - (extent.getWidth()/2)*zoomFactor; var xmax = center.lon + (extent.getWidth()/2)*zoomFactor; var ymin = center.lat - (extent.getHeight()/2)*zoomFactor; var ymax = center.lat + (extent.getHeight()/2)*zoomFactor; bounds = new OpenLayers.Bounds(xmin, ymin, xmax, ymax); } // always zoom in/out var lastZoom = this.map.getZoom(), size = this.map.getSize(), centerPx = {x: size.w / 2, y: size.h / 2}, zoom = this.map.getZoomForExtent(bounds), oldRes = this.map.getResolution(), newRes = this.map.getResolutionForZoom(zoom); if (oldRes == newRes) { this.map.setCenter(this.map.getLonLatFromPixel(targetCenterPx)); } else { var zoomOriginPx = { x: (oldRes * targetCenterPx.x - newRes * centerPx.x) / (oldRes - newRes), y: (oldRes * targetCenterPx.y - newRes * centerPx.y) / (oldRes - newRes) }; this.map.zoomTo(zoom, zoomOriginPx); } if (lastZoom == this.map.getZoom() && this.alwaysZoom == true){ this.map.zoomTo(lastZoom + (this.out ? -1 : 1)); } } else if (this.zoomOnClick) { // it's a pixel if (!this.out) { this.map.zoomTo(this.map.getZoom() + 1, position); } else { this.map.zoomTo(this.map.getZoom() - 1, position); } } }, CLASS_NAME: "OpenLayers.Control.ZoomBox" }); /* ====================================================================== OpenLayers/Control/Snapping.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/Layer/Vector.js */ /** * Class: OpenLayers.Control.Snapping * Acts as a snapping agent while editing vector features. * * Inherits from: * - <OpenLayers.Control> */ OpenLayers.Control.Snapping = 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>): * beforesnap - Triggered before a snap occurs. Listeners receive an * event object with *point*, *x*, *y*, *distance*, *layer*, and * *snapType* properties. The point property will be original point * geometry considered for snapping. The x and y properties represent * coordinates the point will receive. The distance is the distance * of the snap. The layer is the target layer. The snapType property * will be one of "node", "vertex", or "edge". Return false to stop * snapping from occurring. * snap - Triggered when a snap occurs. Listeners receive an event with * *point*, *snapType*, *layer*, and *distance* properties. The point * will be the location snapped to. The snapType will be one of "node", * "vertex", or "edge". The layer will be the target layer. The * distance will be the distance of the snap in map units. * unsnap - Triggered when a vertex is unsnapped. Listeners receive an * event with a *point* property. */ /** * CONSTANT: DEFAULTS * Default target properties. */ DEFAULTS: { tolerance: 10, node: true, edge: true, vertex: true }, /** * Property: greedy * {Boolean} Snap to closest feature in first layer with an eligible * feature. Default is true. */ greedy: true, /** * Property: precedence * {Array} List representing precedence of different snapping types. * Default is "node", "vertex", "edge". */ precedence: ["node", "vertex", "edge"], /** * Property: resolution * {Float} The map resolution for the previously considered snap. */ resolution: null, /** * Property: geoToleranceCache * {Object} A cache of geo-tolerances. Tolerance values (in map units) are * calculated when the map resolution changes. */ geoToleranceCache: null, /** * Property: layer * {<OpenLayers.Layer.Vector>} The current editable layer. Set at * construction or after construction with <setLayer>. */ layer: null, /** * Property: feature * {<OpenLayers.Feature.Vector>} The current editable feature. */ feature: null, /** * Property: point * {<OpenLayers.Geometry.Point>} The currently snapped vertex. */ point: null, /** * Constructor: OpenLayers.Control.Snapping * Creates a new snapping control. A control is constructed with an editable * layer and a set of configuration objects for target layers. While the * control is active, dragging vertices while drawing new features or * modifying existing features on the editable layer will engage * snapping to features on the target layers. Whether a vertex snaps to * a feature on a target layer depends on the target layer configuration. * * Parameters: * options - {Object} An object containing all configuration properties for * the control. * * Valid options: * layer - {<OpenLayers.Layer.Vector>} The editable layer. Features from this * layer that are digitized or modified may have vertices snapped to * features from any of the target layers. * targets - {Array(Object | OpenLayers.Layer.Vector)} A list of objects for * configuring target layers. See valid properties of the target * objects below. If the items in the targets list are vector layers * (instead of configuration objects), the defaults from the <defaults> * property will apply. The editable layer itself may be a target * layer, allowing newly created or edited features to be snapped to * existing features from the same layer. If no targets are provided * the layer given in the constructor (as <layer>) will become the * initial target. * defaults - {Object} An object with default properties to be applied * to all target objects. * greedy - {Boolean} Snap to closest feature in first target layer that * applies. Default is true. If false, all features in all target * layers will be checked and the closest feature in all target layers * will be chosen. The greedy property determines if the order of the * target layers is significant. By default, the order of the target * layers is significant where layers earlier in the target layer list * have precedence over layers later in the list. Within a single * layer, the closest feature is always chosen for snapping. This * property only determines whether the search for a closer feature * continues after an eligible feature is found in a target layer. * * Valid target properties: * layer - {<OpenLayers.Layer.Vector>} A target layer. Features from this * layer will be eligible to act as snapping target for the editable * layer. * tolerance - {Float} The distance (in pixels) at which snapping may occur. * Default is 10. * node - {Boolean} Snap to nodes (first or last point in a geometry) in * target layer. Default is true. * nodeTolerance - {Float} Optional distance at which snapping may occur * for nodes specifically. If none is provided, <tolerance> will be * used. * vertex - {Boolean} Snap to vertices in target layer. Default is true. * vertexTolerance - {Float} Optional distance at which snapping may occur * for vertices specifically. If none is provided, <tolerance> will be * used. * edge - {Boolean} Snap to edges in target layer. Default is true. * edgeTolerance - {Float} Optional distance at which snapping may occur * for edges specifically. If none is provided, <tolerance> will be * used. * filter - {<OpenLayers.Filter>} Optional filter to evaluate to determine if * feature is eligible for snapping. If filter evaluates to true for a * target feature a vertex may be snapped to the feature. * minResolution - {Number} If a minResolution is provided, snapping to this * target will only be considered if the map resolution is greater than * or equal to this value (the minResolution is inclusive). Default is * no minimum resolution limit. * maxResolution - {Number} If a maxResolution is provided, snapping to this * target will only be considered if the map resolution is strictly * less than this value (the maxResolution is exclusive). Default is * no maximum resolution limit. */ initialize: function(options) { OpenLayers.Control.prototype.initialize.apply(this, [options]); this.options = options || {}; // TODO: this could be done by the super // set the editable layer if provided if(this.options.layer) { this.setLayer(this.options.layer); } // configure target layers var defaults = OpenLayers.Util.extend({}, this.options.defaults); this.defaults = OpenLayers.Util.applyDefaults(defaults, this.DEFAULTS); this.setTargets(this.options.targets); if(this.targets.length === 0 && this.layer) { this.addTargetLayer(this.layer); } this.geoToleranceCache = {}; }, /** * APIMethod: setLayer * Set the editable layer. Call the setLayer method if the editable layer * changes and the same control should be used on a new editable layer. * If the control is already active, it will be active after the new * layer is set. * * Parameters: * layer - {<OpenLayers.Layer.Vector>} The new editable layer. */ setLayer: function(layer) { if(this.active) { this.deactivate(); this.layer = layer; this.activate(); } else { this.layer = layer; } }, /** * Method: setTargets * Set the targets for the snapping agent. * * Parameters: * targets - {Array} An array of target configs or target layers. */ setTargets: function(targets) { this.targets = []; if(targets && targets.length) { var target; for(var i=0, len=targets.length; i<len; ++i) { target = targets[i]; if(target instanceof OpenLayers.Layer.Vector) { this.addTargetLayer(target); } else { this.addTarget(target); } } } }, /** * Method: addTargetLayer * Add a target layer with the default target config. * * Parameters: * layer - {<OpenLayers.Layer.Vector>} A target layer. */ addTargetLayer: function(layer) { this.addTarget({layer: layer}); }, /** * Method: addTarget * Add a configured target layer. * * Parameters: * target - {Object} A target config. */ addTarget: function(target) { target = OpenLayers.Util.applyDefaults(target, this.defaults); target.nodeTolerance = target.nodeTolerance || target.tolerance; target.vertexTolerance = target.vertexTolerance || target.tolerance; target.edgeTolerance = target.edgeTolerance || target.tolerance; this.targets.push(target); }, /** * Method: removeTargetLayer * Remove a target layer. * * Parameters: * layer - {<OpenLayers.Layer.Vector>} The target layer to remove. */ removeTargetLayer: function(layer) { var target; for(var i=this.targets.length-1; i>=0; --i) { target = this.targets[i]; if(target.layer === layer) { this.removeTarget(target); } } }, /** * Method: removeTarget * Remove a target. * * Parameters: * target - {Object} A target config. * * Returns: * {Array} The targets array. */ removeTarget: function(target) { return OpenLayers.Util.removeItem(this.targets, target); }, /** * APIMethod: activate * Activate the control. Activating the control registers listeners for * editing related events so that during feature creation and * modification, moving vertices will trigger snapping. */ activate: function() { var activated = OpenLayers.Control.prototype.activate.call(this); if(activated) { if(this.layer && this.layer.events) { this.layer.events.on({ sketchstarted: this.onSketchModified, sketchmodified: this.onSketchModified, vertexmodified: this.onVertexModified, scope: this }); } } return activated; }, /** * APIMethod: deactivate * Deactivate the control. Deactivating the control unregisters listeners * so feature editing may proceed without engaging the snapping agent. */ deactivate: function() { var deactivated = OpenLayers.Control.prototype.deactivate.call(this); if(deactivated) { if(this.layer && this.layer.events) { this.layer.events.un({ sketchstarted: this.onSketchModified, sketchmodified: this.onSketchModified, vertexmodified: this.onVertexModified, scope: this }); } } this.feature = null; this.point = null; return deactivated; }, /** * Method: onSketchModified * Registered as a listener for the sketchmodified event on the editable * layer. * * Parameters: * event - {Object} The sketch modified event. */ onSketchModified: function(event) { this.feature = event.feature; this.considerSnapping(event.vertex, event.vertex); }, /** * Method: onVertexModified * Registered as a listener for the vertexmodified event on the editable * layer. * * Parameters: * event - {Object} The vertex modified event. */ onVertexModified: function(event) { this.feature = event.feature; var loc = this.layer.map.getLonLatFromViewPortPx(event.pixel); this.considerSnapping( event.vertex, new OpenLayers.Geometry.Point(loc.lon, loc.lat) ); }, /** * Method: considerSnapping * * Parameters: * point - {<OpenLayers.Geometry.Point>} The vertex to be snapped (or * unsnapped). * loc - {<OpenLayers.Geometry.Point>} The location of the mouse in map * coords. */ considerSnapping: function(point, loc) { var best = { rank: Number.POSITIVE_INFINITY, dist: Number.POSITIVE_INFINITY, x: null, y: null }; var snapped = false; var result, target; for(var i=0, len=this.targets.length; i<len; ++i) { target = this.targets[i]; result = this.testTarget(target, loc); if(result) { if(this.greedy) { best = result; best.target = target; snapped = true; break; } else { if((result.rank < best.rank) || (result.rank === best.rank && result.dist < best.dist)) { best = result; best.target = target; snapped = true; } } } } if(snapped) { var proceed = this.events.triggerEvent("beforesnap", { point: point, x: best.x, y: best.y, distance: best.dist, layer: best.target.layer, snapType: this.precedence[best.rank] }); if(proceed !== false) { point.x = best.x; point.y = best.y; this.point = point; this.events.triggerEvent("snap", { point: point, snapType: this.precedence[best.rank], layer: best.target.layer, distance: best.dist }); } else { snapped = false; } } if(this.point && !snapped) { point.x = loc.x; point.y = loc.y; this.point = null; this.events.triggerEvent("unsnap", {point: point}); } }, /** * Method: testTarget * * Parameters: * target - {Object} Object with target layer configuration. * loc - {<OpenLayers.Geometry.Point>} The location of the mouse in map * coords. * * Returns: * {Object} A result object with rank, dist, x, and y properties. * Returns null if candidate is not eligible for snapping. */ testTarget: function(target, loc) { var resolution = this.layer.map.getResolution(); if ("minResolution" in target) { if (resolution < target.minResolution) { return null; } } if ("maxResolution" in target) { if (resolution >= target.maxResolution) { return null; } } var tolerance = { node: this.getGeoTolerance(target.nodeTolerance, resolution), vertex: this.getGeoTolerance(target.vertexTolerance, resolution), edge: this.getGeoTolerance(target.edgeTolerance, resolution) }; // this could be cached if we don't support setting tolerance values directly var maxTolerance = Math.max( tolerance.node, tolerance.vertex, tolerance.edge ); var result = { rank: Number.POSITIVE_INFINITY, dist: Number.POSITIVE_INFINITY }; var eligible = false; var features = target.layer.features; var feature, type, vertices, vertex, closest, dist, found; var numTypes = this.precedence.length; var ll = new OpenLayers.LonLat(loc.x, loc.y); for(var i=0, len=features.length; i<len; ++i) { feature = features[i]; if(feature !== this.feature && !feature._sketch && feature.state !== OpenLayers.State.DELETE && (!target.filter || target.filter.evaluate(feature))) { if(feature.atPoint(ll, maxTolerance, maxTolerance)) { for(var j=0, stop=Math.min(result.rank+1, numTypes); j<stop; ++j) { type = this.precedence[j]; if(target[type]) { if(type === "edge") { closest = feature.geometry.distanceTo(loc, {details: true}); dist = closest.distance; if(dist <= tolerance[type] && dist < result.dist) { result = { rank: j, dist: dist, x: closest.x0, y: closest.y0 // closest coords on feature }; eligible = true; // don't look for lower precedence types for this feature break; } } else { // look for nodes or vertices vertices = feature.geometry.getVertices(type === "node"); found = false; for(var k=0, klen=vertices.length; k<klen; ++k) { vertex = vertices[k]; dist = vertex.distanceTo(loc); if(dist <= tolerance[type] && (j < result.rank || (j === result.rank && dist < result.dist))) { result = { rank: j, dist: dist, x: vertex.x, y: vertex.y }; eligible = true; found = true; } } if(found) { // don't look for lower precedence types for this feature break; } } } } } } } return eligible ? result : null; }, /** * Method: getGeoTolerance * Calculate a tolerance in map units given a tolerance in pixels. This * takes advantage of the <geoToleranceCache> when the map resolution * has not changed. * * Parameters: * tolerance - {Number} A tolerance value in pixels. * resolution - {Number} Map resolution. * * Returns: * {Number} A tolerance value in map units. */ getGeoTolerance: function(tolerance, resolution) { if(resolution !== this.resolution) { this.resolution = resolution; this.geoToleranceCache = {}; } var geoTolerance = this.geoToleranceCache[tolerance]; if(geoTolerance === undefined) { geoTolerance = tolerance * resolution; this.geoToleranceCache[tolerance] = geoTolerance; } return geoTolerance; }, /** * Method: destroy * Clean up the control. */ destroy: function() { if(this.active) { this.deactivate(); // TODO: this should be handled by the super } delete this.layer; delete this.targets; OpenLayers.Control.prototype.destroy.call(this); }, CLASS_NAME: "OpenLayers.Control.Snapping" }); /* ====================================================================== OpenLayers/Control/GetFeature.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/Click.js * @requires OpenLayers/Handler/Box.js * @requires OpenLayers/Handler/Hover.js * @requires OpenLayers/Filter/Spatial.js */ /** * Class: OpenLayers.Control.GetFeature * Gets vector features for locations underneath the mouse cursor. Can be * configured to act on click, hover or dragged boxes. Uses an * <OpenLayers.Protocol> that supports spatial filters to retrieve * features from a server and fires events that notify applications of the * selected features. * * Inherits from: * - <OpenLayers.Control> */ OpenLayers.Control.GetFeature = OpenLayers.Class(OpenLayers.Control, { /** * APIProperty: protocol * {<OpenLayers.Protocol>} Required. The protocol used for fetching * features. */ protocol: null, /** * APIProperty: multipleKey * {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets * the <multiple> property to true. Default is null. */ multipleKey: null, /** * APIProperty: toggleKey * {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets * the <toggle> property to true. Default is null. */ toggleKey: null, /** * Property: modifiers * {Object} The event modifiers to use, according to the current event * being handled by this control's handlers */ modifiers: null, /** * APIProperty: multiple * {Boolean} Allow selection of multiple geometries. Default is false. */ multiple: false, /** * APIProperty: click * {Boolean} Use a click handler for selecting/unselecting features. If * both <click> and <box> are set to true, the click handler takes * precedence over the box handler if a box with zero extent was * selected. Default is true. */ click: true, /** * APIProperty: single * {Boolean} Tells whether select by click should select a single * feature. If set to false, all matching features are selected. * If set to true, only the best matching feature is selected. * This option has an effect only of the <click> option is set * to true. Default is true. */ single: true, /** * APIProperty: clickout * {Boolean} Unselect features when clicking outside any feature. * Applies only if <click> is true. Default is true. */ clickout: true, /** * APIProperty: toggle * {Boolean} Unselect a selected feature on click. Applies only if * <click> is true. Default is false. */ toggle: false, /** * APIProperty: clickTolerance * {Integer} Tolerance for the filter query in pixels. This has the * same effect as the tolerance parameter on WMS GetFeatureInfo * requests. Will be ignored for box selections. Applies only if * <click> or <hover> is true. Default is 5. Note that this not * only affects requests on click, but also on hover. */ clickTolerance: 5, /** * APIProperty: hover * {Boolean} Send feature requests on mouse moves. Default is false. */ hover: false, /** * APIProperty: box * {Boolean} Allow feature selection by drawing a box. If set to * true set <click> to false to disable the click handler and * rely on the box handler only, even for "zero extent" boxes. * See the description of the <click> option for additional * information. Default is false. */ box: false, /** * APIProperty: maxFeatures * {Integer} Maximum number of features to return from a query in single mode * if supported by the <protocol>. This set of features is then used to * determine the best match client-side. Default is 10. */ maxFeatures: 10, /** * Property: features * {Object} Hash of {<OpenLayers.Feature.Vector>}, keyed by fid, holding * the currently selected features */ features: null, /** * Proeprty: hoverFeature * {<OpenLayers.Feature.Vector>} The feature currently selected by the * hover handler */ hoverFeature: null, /** * APIProperty: handlerOptions * {Object} Additional options for the handlers used by this control. This * is a hash with the keys "click", "box" and "hover". */ /** * Property: handlers * {Object} Object with references to multiple <OpenLayers.Handler> * instances. */ handlers: null, /** * Property: hoverResponse * {<OpenLayers.Protocol.Response>} The response object associated with * the currently running hover request (if any). */ hoverResponse: null, /** * Property: filterType * {<String>} The type of filter to use when sending off a request. * Possible values: * OpenLayers.Filter.Spatial.<BBOX|INTERSECTS|WITHIN|CONTAINS> * Defaults to: OpenLayers.Filter.Spatial.BBOX */ filterType: OpenLayers.Filter.Spatial.BBOX, /** * 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>): * beforefeatureselected - Triggered when <click> is true before a * feature is selected. The event object has a feature property with * the feature about to select * featureselected - Triggered when <click> is true and a feature is * selected. The event object has a feature property with the * selected feature * beforefeaturesselected - Triggered when <click> is true before a * set of features is selected. The event object is an array of * feature properties with the features about to be selected. * Return false after receiving this event to discontinue processing * of all featureselected events and the featuresselected event. * featuresselected - Triggered when <click> is true and a set of * features is selected. The event object is an array of feature * properties of the selected features * featureunselected - Triggered when <click> is true and a feature is * unselected. The event object has a feature property with the * unselected feature * clickout - Triggered when when <click> is true and no feature was * selected. * hoverfeature - Triggered when <hover> is true and the mouse has * stopped over a feature * outfeature - Triggered when <hover> is true and the mouse moves * moved away from a hover-selected feature */ /** * Constructor: OpenLayers.Control.GetFeature * Create a new control for fetching remote features. * * Parameters: * options - {Object} A configuration object which at least has to contain * a <protocol> property (if not, it has to be set before a request is * made) */ initialize: function(options) { options.handlerOptions = options.handlerOptions || {}; OpenLayers.Control.prototype.initialize.apply(this, [options]); this.features = {}; this.handlers = {}; if(this.click) { this.handlers.click = new OpenLayers.Handler.Click(this, {click: this.selectClick}, this.handlerOptions.click || {}); } if(this.box) { this.handlers.box = new OpenLayers.Handler.Box( this, {done: this.selectBox}, OpenLayers.Util.extend(this.handlerOptions.box, { boxDivClassName: "olHandlerBoxSelectFeature" }) ); } if(this.hover) { this.handlers.hover = new OpenLayers.Handler.Hover( this, {'move': this.cancelHover, 'pause': this.selectHover}, OpenLayers.Util.extend(this.handlerOptions.hover, { 'delay': 250, 'pixelTolerance': 2 }) ); } }, /** * Method: activate * Activates the control. * * Returns: * {Boolean} The control was effectively activated. */ activate: function () { if (!this.active) { for(var i in this.handlers) { this.handlers[i].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) { for(var i in this.handlers) { this.handlers[i].deactivate(); } } return OpenLayers.Control.prototype.deactivate.apply( this, arguments ); }, /** * Method: selectClick * Called on click * * Parameters: * evt - {<OpenLayers.Event>} */ selectClick: function(evt) { var bounds = this.pixelToBounds(evt.xy); this.setModifiers(evt); this.request(bounds, {single: this.single}); }, /** * Method: selectBox * Callback from the handlers.box set up when <box> selection is on * * Parameters: * position - {<OpenLayers.Bounds>|Object} An OpenLayers.Bounds or * an object with a 'left', 'bottom', 'right' and 'top' properties. */ selectBox: function(position) { var bounds; 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 }); bounds = new OpenLayers.Bounds( minXY.lon, minXY.lat, maxXY.lon, maxXY.lat ); } else { if(this.click) { // box without extent - let the click handler take care of it return; } bounds = this.pixelToBounds(position); } this.setModifiers(this.handlers.box.dragHandler.evt); this.request(bounds); }, /** * Method: selectHover * Callback from the handlers.hover set up when <hover> selection is on * * Parameters: * evt - {Object} event object with an xy property */ selectHover: function(evt) { var bounds = this.pixelToBounds(evt.xy); this.request(bounds, {single: true, hover: true}); }, /** * Method: cancelHover * Callback from the handlers.hover set up when <hover> selection is on */ cancelHover: function() { if (this.hoverResponse) { this.protocol.abort(this.hoverResponse); this.hoverResponse = null; OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait"); } }, /** * Method: request * Sends a GetFeature request to the WFS * * Parameters: * bounds - {<OpenLayers.Bounds>} bounds for the request's BBOX filter * options - {Object} additional options for this method. * * Supported options include: * single - {Boolean} A single feature should be returned. * Note that this will be ignored if the protocol does not * return the geometries of the features. * hover - {Boolean} Do the request for the hover handler. */ request: function(bounds, options) { options = options || {}; var filter = new OpenLayers.Filter.Spatial({ type: this.filterType, value: bounds }); // Set the cursor to "wait" to tell the user we're working. OpenLayers.Element.addClass(this.map.viewPortDiv, "olCursorWait"); var response = this.protocol.read({ maxFeatures: options.single == true ? this.maxFeatures : undefined, filter: filter, callback: function(result) { if(result.success()) { if(result.features.length) { if(options.single == true) { this.selectBestFeature(result.features, bounds.getCenterLonLat(), options); } else { this.select(result.features); } } else if(options.hover) { this.hoverSelect(); } else { this.events.triggerEvent("clickout"); if(this.clickout) { this.unselectAll(); } } } // Reset the cursor. OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait"); }, scope: this }); if(options.hover == true) { this.hoverResponse = response; } }, /** * Method: selectBestFeature * Selects the feature from an array of features that is the best match * for the click position. * * Parameters: * features - {Array(<OpenLayers.Feature.Vector>)} * clickPosition - {<OpenLayers.LonLat>} * options - {Object} additional options for this method * * Supported options include: * hover - {Boolean} Do the selection for the hover handler. */ selectBestFeature: function(features, clickPosition, options) { options = options || {}; if(features.length) { var point = new OpenLayers.Geometry.Point(clickPosition.lon, clickPosition.lat); var feature, resultFeature, dist; var minDist = Number.MAX_VALUE; for(var i=0; i<features.length; ++i) { feature = features[i]; if(feature.geometry) { dist = point.distanceTo(feature.geometry, {edge: false}); if(dist < minDist) { minDist = dist; resultFeature = feature; if(minDist == 0) { break; } } } } if(options.hover == true) { this.hoverSelect(resultFeature); } else { this.select(resultFeature || features); } } }, /** * Method: setModifiers * Sets the multiple and toggle modifiers according to the current event * * Parameters: * evt - {<OpenLayers.Event>} */ setModifiers: function(evt) { this.modifiers = { multiple: this.multiple || (this.multipleKey && evt[this.multipleKey]), toggle: this.toggle || (this.toggleKey && evt[this.toggleKey]) }; }, /** * Method: select * Add feature to the hash of selected features and trigger the * featureselected and featuresselected events. * * Parameters: * features - {<OpenLayers.Feature.Vector>} or an array of features */ select: function(features) { if(!this.modifiers.multiple && !this.modifiers.toggle) { this.unselectAll(); } if(!(OpenLayers.Util.isArray(features))) { features = [features]; } var cont = this.events.triggerEvent("beforefeaturesselected", { features: features }); if(cont !== false) { var selectedFeatures = []; var feature; for(var i=0, len=features.length; i<len; ++i) { feature = features[i]; if(this.features[feature.fid || feature.id]) { if(this.modifiers.toggle) { this.unselect(this.features[feature.fid || feature.id]); } } else { cont = this.events.triggerEvent("beforefeatureselected", { feature: feature }); if(cont !== false) { this.features[feature.fid || feature.id] = feature; selectedFeatures.push(feature); this.events.triggerEvent("featureselected", {feature: feature}); } } } this.events.triggerEvent("featuresselected", { features: selectedFeatures }); } }, /** * Method: hoverSelect * Sets/unsets the <hoverFeature> * * Parameters: * feature - {<OpenLayers.Feature.Vector>} the feature to hover-select. * If none is provided, the current <hoverFeature> will be nulled and * the outfeature event will be triggered. */ hoverSelect: function(feature) { var fid = feature ? feature.fid || feature.id : null; var hfid = this.hoverFeature ? this.hoverFeature.fid || this.hoverFeature.id : null; if(hfid && hfid != fid) { this.events.triggerEvent("outfeature", {feature: this.hoverFeature}); this.hoverFeature = null; } if(fid && fid != hfid) { this.events.triggerEvent("hoverfeature", {feature: feature}); this.hoverFeature = feature; } }, /** * Method: unselect * Remove feature from the hash of selected features and trigger the * featureunselected event. * * Parameters: * feature - {<OpenLayers.Feature.Vector>} */ unselect: function(feature) { delete this.features[feature.fid || feature.id]; this.events.triggerEvent("featureunselected", {feature: feature}); }, /** * Method: unselectAll * Unselect all selected features. */ unselectAll: function() { // we'll want an option to supress notification here for(var fid in this.features) { this.unselect(this.features[fid]); } }, /** * Method: setMap * Set the map property for the control. * * Parameters: * map - {<OpenLayers.Map>} */ setMap: function(map) { for(var i in this.handlers) { this.handlers[i].setMap(map); } OpenLayers.Control.prototype.setMap.apply(this, arguments); }, /** * Method: pixelToBounds * Takes a pixel as argument and creates bounds after adding the * <clickTolerance>. * * Parameters: * pixel - {<OpenLayers.Pixel>} */ pixelToBounds: function(pixel) { var llPx = pixel.add(-this.clickTolerance/2, this.clickTolerance/2); var urPx = pixel.add(this.clickTolerance/2, -this.clickTolerance/2); var ll = this.map.getLonLatFromPixel(llPx); var ur = this.map.getLonLatFromPixel(urPx); return new OpenLayers.Bounds(ll.lon, ll.lat, ur.lon, ur.lat); }, CLASS_NAME: "OpenLayers.Control.GetFeature" }); /* ====================================================================== OpenLayers/Control/Scale.js ====================================================================== */ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for * full list of contributors). Published under the 2-clause BSD license. * See license.txt in the OpenLayers distribution or repository for the * full text of the license. */ /** * @requires OpenLayers/Control.js * @requires OpenLayers/Lang.js */ /** * Class: OpenLayers.Control.Scale * The Scale control displays the current map scale as a ratio (e.g. Scale = * 1:1M). By default it is displayed in the lower right corner of the map. * * Inherits from: * - <OpenLayers.Control> */ OpenLayers.Control.Scale = OpenLayers.Class(OpenLayers.Control, { /** * Property: element * {DOMElement} */ element: null, /** * APIProperty: geodesic * {Boolean} Use geodesic measurement. Default is false. The recommended * setting for maps in EPSG:4326 is false, and true EPSG:900913. If set to * true, the scale will be calculated based on the horizontal size of the * pixel in the center of the map viewport. */ geodesic: false, /** * Constructor: OpenLayers.Control.Scale * * Parameters: * element - {DOMElement} * options - {Object} */ initialize: function(element, options) { OpenLayers.Control.prototype.initialize.apply(this, [options]); this.element = OpenLayers.Util.getElement(element); }, /** * Method: draw * * Returns: * {DOMElement} */ draw: function() { OpenLayers.Control.prototype.draw.apply(this, arguments); if (!this.element) { this.element = document.createElement("div"); this.div.appendChild(this.element); } this.map.events.register( 'moveend', this, this.updateScale); this.updateScale(); return this.div; }, /** * Method: updateScale */ updateScale: function() { var scale; if(this.geodesic === true) { var units = this.map.getUnits(); if(!units) { return; } var inches = OpenLayers.INCHES_PER_UNIT; scale = (this.map.getGeodesicPixelSize().w || 0.000001) * inches["km"] * OpenLayers.DOTS_PER_INCH; } else { scale = this.map.getScale(); } if (!scale) { return; } if (scale >= 9500 && scale <= 950000) { scale = Math.round(scale / 1000) + "K"; } else if (scale >= 950000) { scale = Math.round(scale / 1000000) + "M"; } else { scale = Math.round(scale); } this.element.innerHTML = OpenLayers.i18n("Scale = 1 : ${scaleDenom}", {'scaleDenom':scale}); }, CLASS_NAME: "OpenLayers.Control.Scale" }); /* ====================================================================== 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/Control/Button.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.Button * The Button control is a very simple push-button, for use with * <OpenLayers.Control.Panel>. * When clicked, the function trigger() is executed. * * Inherits from: * - <OpenLayers.Control> * * Use: * (code) * var button = new OpenLayers.Control.Button({ * displayClass: "MyButton", trigger: myFunction * }); * panel.addControls([button]); * (end) * * Will create a button with CSS class MyButtonItemInactive, that * will call the function MyFunction() when clicked. */ OpenLayers.Control.Button = OpenLayers.Class(OpenLayers.Control, { /** * Property: type * {Integer} OpenLayers.Control.TYPE_BUTTON. */ type: OpenLayers.Control.TYPE_BUTTON, /** * Method: trigger * Called by a control panel when the button is clicked. */ trigger: function() {}, CLASS_NAME: "OpenLayers.Control.Button" }); /* ====================================================================== OpenLayers/Control/ZoomIn.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/Button.js */ /** * Class: OpenLayers.Control.ZoomIn * The ZoomIn control is a button to increase the zoom level of a map. * * Inherits from: * - <OpenLayers.Control> */ OpenLayers.Control.ZoomIn = OpenLayers.Class(OpenLayers.Control.Button, { /** * Method: trigger */ trigger: function(){ if (this.map) { this.map.zoomIn(); } }, CLASS_NAME: "OpenLayers.Control.ZoomIn" }); /* ====================================================================== OpenLayers/Control/ZoomOut.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/Button.js */ /** * Class: OpenLayers.Control.ZoomOut * The ZoomOut control is a button to decrease the zoom level of a map. * * Inherits from: * - <OpenLayers.Control> */ OpenLayers.Control.ZoomOut = OpenLayers.Class(OpenLayers.Control.Button, { /** * Method: trigger */ trigger: function(){ if (this.map) { this.map.zoomOut(); } }, CLASS_NAME: "OpenLayers.Control.ZoomOut" }); /* ====================================================================== OpenLayers/Control/ZoomToMaxExtent.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/Button.js */ /** * Class: OpenLayers.Control.ZoomToMaxExtent * The ZoomToMaxExtent control is a button that zooms out to the maximum * extent of the map. It is designed to be used with a * <OpenLayers.Control.Panel>. * * Inherits from: * - <OpenLayers.Control> */ OpenLayers.Control.ZoomToMaxExtent = OpenLayers.Class(OpenLayers.Control.Button, { /** * Method: trigger * * Called whenever this control is being rendered inside of a panel and a * click occurs on this controls element. Actually zooms to the maximum * extent of this controls map. */ trigger: function() { if (this.map) { this.map.zoomToMaxExtent(); } }, CLASS_NAME: "OpenLayers.Control.ZoomToMaxExtent" }); /* ====================================================================== OpenLayers/Control/ZoomPanel.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/Panel.js * @requires OpenLayers/Control/ZoomIn.js * @requires OpenLayers/Control/ZoomOut.js * @requires OpenLayers/Control/ZoomToMaxExtent.js */ /** * Class: OpenLayers.Control.ZoomPanel * The ZoomPanel control is a compact collecton of 3 zoom controls: a * <OpenLayers.Control.ZoomIn>, a <OpenLayers.Control.ZoomToMaxExtent>, and a * <OpenLayers.Control.ZoomOut>. By default it is drawn in the upper left * corner of the map. * * Note: * If you wish to use this class with the default images and you want * it to look nice in ie6, you should add the following, conditionally * added css stylesheet to your HTML file: * * (code) * <!--[if lte IE 6]> * <link rel="stylesheet" href="../theme/default/ie6-style.css" type="text/css" /> * <![endif]--> * (end) * * Inherits from: * - <OpenLayers.Control.Panel> */ OpenLayers.Control.ZoomPanel = OpenLayers.Class(OpenLayers.Control.Panel, { /** * Constructor: OpenLayers.Control.ZoomPanel * Add the three zooming controls. * * Parameters: * options - {Object} An optional object whose properties will be used * to extend the control. */ initialize: function(options) { OpenLayers.Control.Panel.prototype.initialize.apply(this, [options]); this.addControls([ new OpenLayers.Control.ZoomIn(), new OpenLayers.Control.ZoomToMaxExtent(), new OpenLayers.Control.ZoomOut() ]); }, CLASS_NAME: "OpenLayers.Control.ZoomPanel" }); /* ====================================================================== OpenLayers/Control/OverviewMap.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/BaseTypes.js * @requires OpenLayers/Events/buttonclick.js * @requires OpenLayers/Map.js * @requires OpenLayers/Handler/Click.js * @requires OpenLayers/Handler/Drag.js */ /** * Class: OpenLayers.Control.OverviewMap * The OverMap control creates a small overview map, useful to display the * extent of a zoomed map and your main map and provide additional * navigation options to the User. By default the overview map is drawn in * the lower right corner of the main map. Create a new overview map with the * <OpenLayers.Control.OverviewMap> constructor. * * Inherits from: * - <OpenLayers.Control> */ OpenLayers.Control.OverviewMap = OpenLayers.Class(OpenLayers.Control, { /** * Property: element * {DOMElement} The DOM element that contains the overview map */ element: null, /** * APIProperty: ovmap * {<OpenLayers.Map>} A reference to the overview map itself. */ ovmap: null, /** * APIProperty: size * {<OpenLayers.Size>} The overvew map size in pixels. Note that this is * the size of the map itself - the element that contains the map (default * class name olControlOverviewMapElement) may have padding or other style * attributes added via CSS. */ size: {w: 180, h: 90}, /** * APIProperty: layers * {Array(<OpenLayers.Layer>)} Ordered list of layers in the overview map. * If none are sent at construction, the base layer for the main map is used. */ layers: null, /** * APIProperty: minRectSize * {Integer} The minimum width or height (in pixels) of the extent * rectangle on the overview map. When the extent rectangle reaches * this size, it will be replaced depending on the value of the * <minRectDisplayClass> property. Default is 15 pixels. */ minRectSize: 15, /** * APIProperty: minRectDisplayClass * {String} Replacement style class name for the extent rectangle when * <minRectSize> is reached. This string will be suffixed on to the * displayClass. Default is "RectReplacement". * * Example CSS declaration: * (code) * .olControlOverviewMapRectReplacement { * overflow: hidden; * cursor: move; * background-image: url("img/overview_replacement.gif"); * background-repeat: no-repeat; * background-position: center; * } * (end) */ minRectDisplayClass: "RectReplacement", /** * APIProperty: minRatio * {Float} The ratio of the overview map resolution to the main map * resolution at which to zoom farther out on the overview map. */ minRatio: 8, /** * APIProperty: maxRatio * {Float} The ratio of the overview map resolution to the main map * resolution at which to zoom farther in on the overview map. */ maxRatio: 32, /** * APIProperty: mapOptions * {Object} An object containing any non-default properties to be sent to * the overview map's map constructor. These should include any * non-default options that the main map was constructed with. */ mapOptions: null, /** * APIProperty: autoPan * {Boolean} Always pan the overview map, so the extent marker remains in * the center. Default is false. If true, when you drag the extent * marker, the overview map will update itself so the marker returns * to the center. */ autoPan: false, /** * Property: handlers * {Object} */ handlers: null, /** * Property: resolutionFactor * {Object} */ resolutionFactor: 1, /** * APIProperty: maximized * {Boolean} Start as maximized (visible). Defaults to false. */ maximized: false, /** * APIProperty: maximizeTitle * {String} This property is used for showing a tooltip over the * maximize div. Defaults to "" (no title). */ maximizeTitle: "", /** * APIProperty: minimizeTitle * {String} This property is used for showing a tooltip over the * minimize div. Defaults to "" (no title). */ minimizeTitle: "", /** * Constructor: OpenLayers.Control.OverviewMap * Create a new overview map * * Parameters: * options - {Object} Properties of this object will be set on the overview * map object. Note, to set options on the map object contained in this * control, set <mapOptions> as one of the options properties. */ initialize: function(options) { this.layers = []; this.handlers = {}; OpenLayers.Control.prototype.initialize.apply(this, [options]); }, /** * APIMethod: destroy * Deconstruct the control */ destroy: function() { if (!this.mapDiv) { // we've already been destroyed return; } if (this.handlers.click) { this.handlers.click.destroy(); } if (this.handlers.drag) { this.handlers.drag.destroy(); } this.ovmap && this.ovmap.viewPortDiv.removeChild(this.extentRectangle); this.extentRectangle = null; if (this.rectEvents) { this.rectEvents.destroy(); this.rectEvents = null; } if (this.ovmap) { this.ovmap.destroy(); this.ovmap = null; } this.element.removeChild(this.mapDiv); this.mapDiv = null; this.div.removeChild(this.element); this.element = null; if (this.maximizeDiv) { this.div.removeChild(this.maximizeDiv); this.maximizeDiv = null; } if (this.minimizeDiv) { this.div.removeChild(this.minimizeDiv); this.minimizeDiv = null; } this.map.events.un({ buttonclick: this.onButtonClick, moveend: this.update, changebaselayer: this.baseLayerDraw, scope: this }); OpenLayers.Control.prototype.destroy.apply(this, arguments); }, /** * Method: draw * Render the control in the browser. */ draw: function() { OpenLayers.Control.prototype.draw.apply(this, arguments); if (this.layers.length === 0) { if (this.map.baseLayer) { var layer = this.map.baseLayer.clone(); this.layers = [layer]; } else { this.map.events.register("changebaselayer", this, this.baseLayerDraw); return this.div; } } // create overview map DOM elements this.element = document.createElement('div'); this.element.className = this.displayClass + 'Element'; this.element.style.display = 'none'; this.mapDiv = document.createElement('div'); this.mapDiv.style.width = this.size.w + 'px'; this.mapDiv.style.height = this.size.h + 'px'; this.mapDiv.style.position = 'relative'; this.mapDiv.style.overflow = 'hidden'; this.mapDiv.id = OpenLayers.Util.createUniqueID('overviewMap'); this.extentRectangle = document.createElement('div'); this.extentRectangle.style.position = 'absolute'; this.extentRectangle.style.zIndex = 1000; //HACK this.extentRectangle.className = this.displayClass+'ExtentRectangle'; this.element.appendChild(this.mapDiv); this.div.appendChild(this.element); // Optionally add min/max buttons if the control will go in the // map viewport. if(!this.outsideViewport) { this.div.className += " " + this.displayClass + 'Container'; // maximize button div var img = OpenLayers.Util.getImageLocation('layer-switcher-maximize.png'); this.maximizeDiv = OpenLayers.Util.createAlphaImageDiv( this.displayClass + 'MaximizeButton', null, null, img, 'absolute'); this.maximizeDiv.style.display = 'none'; this.maximizeDiv.className = this.displayClass + 'MaximizeButton olButton'; if (this.maximizeTitle) { this.maximizeDiv.title = this.maximizeTitle; } this.div.appendChild(this.maximizeDiv); // minimize button div var img = OpenLayers.Util.getImageLocation('layer-switcher-minimize.png'); this.minimizeDiv = OpenLayers.Util.createAlphaImageDiv( 'OpenLayers_Control_minimizeDiv', null, null, img, 'absolute'); this.minimizeDiv.style.display = 'none'; this.minimizeDiv.className = this.displayClass + 'MinimizeButton olButton'; if (this.minimizeTitle) { this.minimizeDiv.title = this.minimizeTitle; } this.div.appendChild(this.minimizeDiv); this.minimizeControl(); } else { // show the overview map this.element.style.display = ''; } if(this.map.getExtent()) { this.update(); } this.map.events.on({ buttonclick: this.onButtonClick, moveend: this.update, scope: this }); if (this.maximized) { this.maximizeControl(); } return this.div; }, /** * Method: baseLayerDraw * Draw the base layer - called if unable to complete in the initial draw */ baseLayerDraw: function() { this.draw(); this.map.events.unregister("changebaselayer", this, this.baseLayerDraw); }, /** * Method: rectDrag * Handle extent rectangle drag * * Parameters: * px - {<OpenLayers.Pixel>} The pixel location of the drag. */ rectDrag: function(px) { var deltaX = this.handlers.drag.last.x - px.x; var deltaY = this.handlers.drag.last.y - px.y; if(deltaX != 0 || deltaY != 0) { var rectTop = this.rectPxBounds.top; var rectLeft = this.rectPxBounds.left; var rectHeight = Math.abs(this.rectPxBounds.getHeight()); var rectWidth = this.rectPxBounds.getWidth(); // don't allow dragging off of parent element var newTop = Math.max(0, (rectTop - deltaY)); newTop = Math.min(newTop, this.ovmap.size.h - this.hComp - rectHeight); var newLeft = Math.max(0, (rectLeft - deltaX)); newLeft = Math.min(newLeft, this.ovmap.size.w - this.wComp - rectWidth); this.setRectPxBounds(new OpenLayers.Bounds(newLeft, newTop + rectHeight, newLeft + rectWidth, newTop)); } }, /** * Method: mapDivClick * Handle browser events * * Parameters: * evt - {<OpenLayers.Event>} evt */ mapDivClick: function(evt) { var pxCenter = this.rectPxBounds.getCenterPixel(); var deltaX = evt.xy.x - pxCenter.x; var deltaY = evt.xy.y - pxCenter.y; var top = this.rectPxBounds.top; var left = this.rectPxBounds.left; var height = Math.abs(this.rectPxBounds.getHeight()); var width = this.rectPxBounds.getWidth(); var newTop = Math.max(0, (top + deltaY)); newTop = Math.min(newTop, this.ovmap.size.h - height); var newLeft = Math.max(0, (left + deltaX)); newLeft = Math.min(newLeft, this.ovmap.size.w - width); this.setRectPxBounds(new OpenLayers.Bounds(newLeft, newTop + height, newLeft + width, newTop)); this.updateMapToRect(); }, /** * Method: onButtonClick * * Parameters: * evt - {Event} */ onButtonClick: function(evt) { if (evt.buttonElement === this.minimizeDiv) { this.minimizeControl(); } else if (evt.buttonElement === this.maximizeDiv) { this.maximizeControl(); } }, /** * Method: maximizeControl * Unhide the control. Called when the control is in the map viewport. * * Parameters: * e - {<OpenLayers.Event>} */ maximizeControl: function(e) { this.element.style.display = ''; this.showToggle(false); if (e != null) { OpenLayers.Event.stop(e); } }, /** * Method: minimizeControl * Hide all the contents of the control, shrink the size, * add the maximize icon * * Parameters: * e - {<OpenLayers.Event>} */ minimizeControl: function(e) { this.element.style.display = 'none'; this.showToggle(true); if (e != null) { OpenLayers.Event.stop(e); } }, /** * Method: showToggle * Hide/Show the toggle depending on whether the control is minimized * * Parameters: * minimize - {Boolean} */ showToggle: function(minimize) { if (this.maximizeDiv) { this.maximizeDiv.style.display = minimize ? '' : 'none'; } if (this.minimizeDiv) { this.minimizeDiv.style.display = minimize ? 'none' : ''; } }, /** * Method: update * Update the overview map after layers move. */ update: function() { if(this.ovmap == null) { this.createMap(); } if(this.autoPan || !this.isSuitableOverview()) { this.updateOverview(); } // update extent rectangle this.updateRectToMap(); }, /** * Method: isSuitableOverview * Determines if the overview map is suitable given the extent and * resolution of the main map. */ isSuitableOverview: function() { var mapExtent = this.map.getExtent(); var maxExtent = this.map.getMaxExtent(); var testExtent = new OpenLayers.Bounds( Math.max(mapExtent.left, maxExtent.left), Math.max(mapExtent.bottom, maxExtent.bottom), Math.min(mapExtent.right, maxExtent.right), Math.min(mapExtent.top, maxExtent.top)); if (this.ovmap.getProjection() != this.map.getProjection()) { testExtent = testExtent.transform( this.map.getProjectionObject(), this.ovmap.getProjectionObject() ); } var resRatio = this.ovmap.getResolution() / this.map.getResolution(); return ((resRatio > this.minRatio) && (resRatio <= this.maxRatio) && (this.ovmap.getExtent().containsBounds(testExtent))); }, /** * Method updateOverview * Called by <update> if <isSuitableOverview> returns true */ updateOverview: function() { var mapRes = this.map.getResolution(); var targetRes = this.ovmap.getResolution(); var resRatio = targetRes / mapRes; if(resRatio > this.maxRatio) { // zoom in overview map targetRes = this.minRatio * mapRes; } else if(resRatio <= this.minRatio) { // zoom out overview map targetRes = this.maxRatio * mapRes; } var center; if (this.ovmap.getProjection() != this.map.getProjection()) { center = this.map.center.clone(); center.transform(this.map.getProjectionObject(), this.ovmap.getProjectionObject() ); } else { center = this.map.center; } this.ovmap.setCenter(center, this.ovmap.getZoomForResolution( targetRes * this.resolutionFactor)); this.updateRectToMap(); }, /** * Method: createMap * Construct the map that this control contains */ createMap: function() { // create the overview map var options = OpenLayers.Util.extend( {controls: [], maxResolution: 'auto', fallThrough: false}, this.mapOptions); this.ovmap = new OpenLayers.Map(this.mapDiv, options); this.ovmap.viewPortDiv.appendChild(this.extentRectangle); // prevent ovmap from being destroyed when the page unloads, because // the OverviewMap control has to do this (and does it). OpenLayers.Event.stopObserving(window, 'unload', this.ovmap.unloadDestroy); this.ovmap.addLayers(this.layers); this.ovmap.zoomToMaxExtent(); // check extent rectangle border width this.wComp = parseInt(OpenLayers.Element.getStyle(this.extentRectangle, 'border-left-width')) + parseInt(OpenLayers.Element.getStyle(this.extentRectangle, 'border-right-width')); this.wComp = (this.wComp) ? this.wComp : 2; this.hComp = parseInt(OpenLayers.Element.getStyle(this.extentRectangle, 'border-top-width')) + parseInt(OpenLayers.Element.getStyle(this.extentRectangle, 'border-bottom-width')); this.hComp = (this.hComp) ? this.hComp : 2; this.handlers.drag = new OpenLayers.Handler.Drag( this, {move: this.rectDrag, done: this.updateMapToRect}, {map: this.ovmap} ); this.handlers.click = new OpenLayers.Handler.Click( this, { "click": this.mapDivClick },{ "single": true, "double": false, "stopSingle": true, "stopDouble": true, "pixelTolerance": 1, map: this.ovmap } ); this.handlers.click.activate(); this.rectEvents = new OpenLayers.Events(this, this.extentRectangle, null, true); this.rectEvents.register("mouseover", this, function(e) { if(!this.handlers.drag.active && !this.map.dragging) { this.handlers.drag.activate(); } }); this.rectEvents.register("mouseout", this, function(e) { if(!this.handlers.drag.dragging) { this.handlers.drag.deactivate(); } }); if (this.ovmap.getProjection() != this.map.getProjection()) { var sourceUnits = this.map.getProjectionObject().getUnits() || this.map.units || this.map.baseLayer.units; var targetUnits = this.ovmap.getProjectionObject().getUnits() || this.ovmap.units || this.ovmap.baseLayer.units; this.resolutionFactor = sourceUnits && targetUnits ? OpenLayers.INCHES_PER_UNIT[sourceUnits] / OpenLayers.INCHES_PER_UNIT[targetUnits] : 1; } }, /** * Method: updateRectToMap * Updates the extent rectangle position and size to match the map extent */ updateRectToMap: function() { // If the projections differ we need to reproject var bounds; if (this.ovmap.getProjection() != this.map.getProjection()) { bounds = this.map.getExtent().transform( this.map.getProjectionObject(), this.ovmap.getProjectionObject() ); } else { bounds = this.map.getExtent(); } var pxBounds = this.getRectBoundsFromMapBounds(bounds); if (pxBounds) { this.setRectPxBounds(pxBounds); } }, /** * Method: updateMapToRect * Updates the map extent to match the extent rectangle position and size */ updateMapToRect: function() { var lonLatBounds = this.getMapBoundsFromRectBounds(this.rectPxBounds); if (this.ovmap.getProjection() != this.map.getProjection()) { lonLatBounds = lonLatBounds.transform( this.ovmap.getProjectionObject(), this.map.getProjectionObject() ); } this.map.panTo(lonLatBounds.getCenterLonLat()); }, /** * Method: setRectPxBounds * Set extent rectangle pixel bounds. * * Parameters: * pxBounds - {<OpenLayers.Bounds>} */ setRectPxBounds: function(pxBounds) { var top = Math.max(pxBounds.top, 0); var left = Math.max(pxBounds.left, 0); var bottom = Math.min(pxBounds.top + Math.abs(pxBounds.getHeight()), this.ovmap.size.h - this.hComp); var right = Math.min(pxBounds.left + pxBounds.getWidth(), this.ovmap.size.w - this.wComp); var width = Math.max(right - left, 0); var height = Math.max(bottom - top, 0); if(width < this.minRectSize || height < this.minRectSize) { this.extentRectangle.className = this.displayClass + this.minRectDisplayClass; var rLeft = left + (width / 2) - (this.minRectSize / 2); var rTop = top + (height / 2) - (this.minRectSize / 2); this.extentRectangle.style.top = Math.round(rTop) + 'px'; this.extentRectangle.style.left = Math.round(rLeft) + 'px'; this.extentRectangle.style.height = this.minRectSize + 'px'; this.extentRectangle.style.width = this.minRectSize + 'px'; } else { this.extentRectangle.className = this.displayClass + 'ExtentRectangle'; this.extentRectangle.style.top = Math.round(top) + 'px'; this.extentRectangle.style.left = Math.round(left) + 'px'; this.extentRectangle.style.height = Math.round(height) + 'px'; this.extentRectangle.style.width = Math.round(width) + 'px'; } this.rectPxBounds = new OpenLayers.Bounds( Math.round(left), Math.round(bottom), Math.round(right), Math.round(top) ); }, /** * Method: getRectBoundsFromMapBounds * Get the rect bounds from the map bounds. * * Parameters: * lonLatBounds - {<OpenLayers.Bounds>} * * Returns: * {<OpenLayers.Bounds>}A bounds which is the passed-in map lon/lat extent * translated into pixel bounds for the overview map */ getRectBoundsFromMapBounds: function(lonLatBounds) { var leftBottomPx = this.getOverviewPxFromLonLat({ lon: lonLatBounds.left, lat: lonLatBounds.bottom }); var rightTopPx = this.getOverviewPxFromLonLat({ lon: lonLatBounds.right, lat: lonLatBounds.top }); var bounds = null; if (leftBottomPx && rightTopPx) { bounds = new OpenLayers.Bounds(leftBottomPx.x, leftBottomPx.y, rightTopPx.x, rightTopPx.y); } return bounds; }, /** * Method: getMapBoundsFromRectBounds * Get the map bounds from the rect bounds. * * Parameters: * pxBounds - {<OpenLayers.Bounds>} * * Returns: * {<OpenLayers.Bounds>} Bounds which is the passed-in overview rect bounds * translated into lon/lat bounds for the overview map */ getMapBoundsFromRectBounds: function(pxBounds) { var leftBottomLonLat = this.getLonLatFromOverviewPx({ x: pxBounds.left, y: pxBounds.bottom }); var rightTopLonLat = this.getLonLatFromOverviewPx({ x: pxBounds.right, y: pxBounds.top }); return new OpenLayers.Bounds(leftBottomLonLat.lon, leftBottomLonLat.lat, rightTopLonLat.lon, rightTopLonLat.lat); }, /** * Method: getLonLatFromOverviewPx * Get a map location from a pixel location * * Parameters: * overviewMapPx - {<OpenLayers.Pixel>|Object} OpenLayers.Pixel or * an object with a * 'x' and 'y' properties. * * Returns: * {Object} Location which is the passed-in overview map * OpenLayers.Pixel, translated into lon/lat by the overview * map. An object with a 'lon' and 'lat' properties. */ getLonLatFromOverviewPx: function(overviewMapPx) { var size = this.ovmap.size; var res = this.ovmap.getResolution(); var center = this.ovmap.getExtent().getCenterLonLat(); var deltaX = overviewMapPx.x - (size.w / 2); var deltaY = overviewMapPx.y - (size.h / 2); return { lon: center.lon + deltaX * res, lat: center.lat - deltaY * res }; }, /** * Method: getOverviewPxFromLonLat * Get a pixel location from a map location * * Parameters: * lonlat - {<OpenLayers.LonLat>|Object} OpenLayers.LonLat or an * object with a 'lon' and 'lat' properties. * * Returns: * {Object} Location which is the passed-in OpenLayers.LonLat, * translated into overview map pixels */ getOverviewPxFromLonLat: function(lonlat) { var res = this.ovmap.getResolution(); var extent = this.ovmap.getExtent(); if (extent) { return { x: Math.round(1/res * (lonlat.lon - extent.left)), y: Math.round(1/res * (extent.top - lonlat.lat)) }; } }, CLASS_NAME: 'OpenLayers.Control.OverviewMap' }); /* ====================================================================== OpenLayers/Control/ArgParser.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.ArgParser * The ArgParser control adds location bar query string parsing functionality * to an OpenLayers Map. * When added to a Map control, on a page load/refresh, the Map will * automatically take the href string and parse it for lon, lat, zoom, and * layers information. * * Inherits from: * - <OpenLayers.Control> */ OpenLayers.Control.ArgParser = OpenLayers.Class(OpenLayers.Control, { /** * Property: center * {<OpenLayers.LonLat>} */ center: null, /** * Property: zoom * {int} */ zoom: null, /** * Property: layers * {String} Each character represents the state of the corresponding layer * on the map. */ layers: null, /** * APIProperty: displayProjection * {<OpenLayers.Projection>} Requires proj4js support. * Projection used when reading the coordinates from the URL. This will * reproject the map coordinates from the URL into the map's * projection. * * If you are using this functionality, be aware that any permalink * which is added to the map will determine the coordinate type which * is read from the URL, which means you should not add permalinks with * different displayProjections to the same map. */ displayProjection: null, /** * Constructor: OpenLayers.Control.ArgParser * * Parameters: * options - {Object} */ /** * Method: getParameters */ getParameters: function(url) { url = url || window.location.href; var parameters = OpenLayers.Util.getParameters(url); // If we have an anchor in the url use it to split the url var index = url.indexOf('#'); if (index > 0) { // create an url to parse on the getParameters url = '?' + url.substring(index + 1, url.length); OpenLayers.Util.extend(parameters, OpenLayers.Util.getParameters(url)); } return parameters; }, /** * Method: setMap * Set the map property for the control. * * Parameters: * map - {<OpenLayers.Map>} */ setMap: function(map) { OpenLayers.Control.prototype.setMap.apply(this, arguments); //make sure we dont already have an arg parser attached for(var i=0, len=this.map.controls.length; i<len; i++) { var control = this.map.controls[i]; if ( (control != this) && (control.CLASS_NAME == "OpenLayers.Control.ArgParser") ) { // If a second argparser is added to the map, then we // override the displayProjection to be the one added to the // map. if (control.displayProjection != this.displayProjection) { this.displayProjection = control.displayProjection; } break; } } if (i == this.map.controls.length) { var args = this.getParameters(); // Be careful to set layer first, to not trigger unnecessary layer loads if (args.layers) { this.layers = args.layers; // when we add a new layer, set its visibility this.map.events.register('addlayer', this, this.configureLayers); this.configureLayers(); } if (args.lat && args.lon) { this.center = new OpenLayers.LonLat(parseFloat(args.lon), parseFloat(args.lat)); if (args.zoom) { this.zoom = parseFloat(args.zoom); } // when we add a new baselayer to see when we can set the center this.map.events.register('changebaselayer', this, this.setCenter); this.setCenter(); } } }, /** * Method: setCenter * As soon as a baseLayer has been loaded, we center and zoom * ...and remove the handler. */ setCenter: function() { if (this.map.baseLayer) { //dont need to listen for this one anymore this.map.events.unregister('changebaselayer', this, this.setCenter); if (this.displayProjection) { this.center.transform(this.displayProjection, this.map.getProjectionObject()); } this.map.setCenter(this.center, this.zoom); } }, /** * Method: configureLayers * As soon as all the layers are loaded, cycle through them and * hide or show them. */ configureLayers: function() { if (this.layers.length == this.map.layers.length) { this.map.events.unregister('addlayer', this, this.configureLayers); for(var i=0, len=this.layers.length; i<len; i++) { var layer = this.map.layers[i]; var c = this.layers.charAt(i); if (c == "B") { this.map.setBaseLayer(layer); } else if ( (c == "T") || (c == "F") ) { layer.setVisibility(c == "T"); } } } }, CLASS_NAME: "OpenLayers.Control.ArgParser" }); /* ====================================================================== OpenLayers/Control/Measure.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.Measure * Allows for drawing of features for measurements. * * Inherits from: * - <OpenLayers.Control> */ OpenLayers.Control.Measure = 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>): * measure - Triggered when a measurement sketch is complete. Listeners * will receive an event with measure, units, order, and geometry * properties. * measurepartial - Triggered when a new point is added to the * measurement sketch or if the <immediate> property is true and the * measurement sketch is modified. Listeners receive an event with measure, * units, order, and geometry. */ /** * APIProperty: handlerOptions * {Object} Used to set non-default properties on the control's handler */ /** * Property: callbacks * {Object} The functions that are sent to the handler for callback */ callbacks: null, /** * APIProperty: displaySystem * {String} Display system for output measurements. Supported values * are 'english', 'metric', and 'geographic'. Default is 'metric'. */ displaySystem: 'metric', /** * APIProperty: geodesic * {Boolean} Calculate geodesic metrics instead of planar metrics. This * requires that geometries can be transformed into Geographic/WGS84 * (if that is not already the map projection). Default is false. */ geodesic: false, /** * Property: displaySystemUnits * {Object} Units for various measurement systems. Values are arrays * of unit abbreviations (from OpenLayers.INCHES_PER_UNIT) in decreasing * order of length. */ displaySystemUnits: { geographic: ['dd'], english: ['mi', 'ft', 'in'], metric: ['km', 'm'] }, /** * Property: delay * {Number} Number of milliseconds between clicks before the event is * considered a double-click. The "measurepartial" event will not * be triggered if the sketch is completed within this time. This * is required for IE where creating a browser reflow (if a listener * is modifying the DOM by displaying the measurement values) messes * with the dblclick listener in the sketch handler. */ partialDelay: 300, /** * Property: delayedTrigger * {Number} Timeout id of trigger for measurepartial. */ delayedTrigger: null, /** * APIProperty: persist * {Boolean} Keep the temporary measurement sketch drawn after the * measurement is complete. The geometry will persist until a new * measurement is started, the control is deactivated, or <cancel> is * called. */ persist: false, /** * APIProperty: immediate * {Boolean} Activates the immediate measurement so that the "measurepartial" * event is also fired once the measurement sketch is modified. * Default is false. */ immediate : false, /** * Constructor: OpenLayers.Control.Measure * * Parameters: * handler - {<OpenLayers.Handler>} * options - {Object} */ initialize: function(handler, options) { OpenLayers.Control.prototype.initialize.apply(this, [options]); var callbacks = {done: this.measureComplete, point: this.measurePartial}; if (this.immediate){ callbacks.modify = this.measureImmediate; } this.callbacks = OpenLayers.Util.extend(callbacks, this.callbacks); // let the handler options override, so old code that passes 'persist' // directly to the handler does not need an update this.handlerOptions = OpenLayers.Util.extend( {persist: this.persist}, this.handlerOptions ); this.handler = new handler(this, this.callbacks, this.handlerOptions); }, /** * APIMethod: deactivate */ deactivate: function() { this.cancelDelay(); return OpenLayers.Control.prototype.deactivate.apply(this, arguments); }, /** * APIMethod: cancel * Stop the control from measuring. If <persist> is true, the temporary * sketch will be erased. */ cancel: function() { this.cancelDelay(); this.handler.cancel(); }, /** * APIMethod: setImmediate * Sets the <immediate> property. Changes the activity of immediate * measurement. */ setImmediate: function(immediate) { this.immediate = immediate; if (this.immediate){ this.callbacks.modify = this.measureImmediate; } else { delete this.callbacks.modify; } }, /** * Method: updateHandler * * Parameters: * handler - {Function} One of the sketch handler constructors. * options - {Object} Options for the handler. */ updateHandler: function(handler, options) { var active = this.active; if(active) { this.deactivate(); } this.handler = new handler(this, this.callbacks, options); if(active) { this.activate(); } }, /** * Method: measureComplete * Called when the measurement sketch is done. * * Parameters: * geometry - {<OpenLayers.Geometry>} */ measureComplete: function(geometry) { this.cancelDelay(); this.measure(geometry, "measure"); }, /** * Method: measurePartial * Called each time a new point is added to the measurement sketch. * * Parameters: * point - {<OpenLayers.Geometry.Point>} The last point added. * geometry - {<OpenLayers.Geometry>} The sketch geometry. */ measurePartial: function(point, geometry) { this.cancelDelay(); geometry = geometry.clone(); // when we're wating for a dblclick, we have to trigger measurepartial // after some delay to deal with reflow issues in IE if (this.handler.freehandMode(this.handler.evt)) { // no dblclick in freehand mode this.measure(geometry, "measurepartial"); } else { this.delayedTrigger = window.setTimeout( OpenLayers.Function.bind(function() { this.delayedTrigger = null; this.measure(geometry, "measurepartial"); }, this), this.partialDelay ); } }, /** * Method: measureImmediate * Called each time the measurement sketch is modified. * * Parameters: * point - {<OpenLayers.Geometry.Point>} The point at the mouse position. * feature - {<OpenLayers.Feature.Vector>} The sketch feature. * drawing - {Boolean} Indicates whether we're currently drawing. */ measureImmediate : function(point, feature, drawing) { if (drawing && !this.handler.freehandMode(this.handler.evt)) { this.cancelDelay(); this.measure(feature.geometry, "measurepartial"); } }, /** * Method: cancelDelay * Cancels the delay measurement that measurePartial began. */ cancelDelay: function() { if (this.delayedTrigger !== null) { window.clearTimeout(this.delayedTrigger); this.delayedTrigger = null; } }, /** * Method: measure * * Parameters: * geometry - {<OpenLayers.Geometry>} * eventType - {String} */ measure: function(geometry, eventType) { var stat, order; if(geometry.CLASS_NAME.indexOf('LineString') > -1) { stat = this.getBestLength(geometry); order = 1; } else { stat = this.getBestArea(geometry); order = 2; } this.events.triggerEvent(eventType, { measure: stat[0], units: stat[1], order: order, geometry: geometry }); }, /** * Method: getBestArea * Based on the <displaySystem> returns the area of a geometry. * * Parameters: * geometry - {<OpenLayers.Geometry>} * * Returns: * {Array([Float, String])} Returns a two item array containing the * area and the units abbreviation. */ getBestArea: function(geometry) { var units = this.displaySystemUnits[this.displaySystem]; var unit, area; for(var i=0, len=units.length; i<len; ++i) { unit = units[i]; area = this.getArea(geometry, unit); if(area > 1) { break; } } return [area, unit]; }, /** * Method: getArea * * Parameters: * geometry - {<OpenLayers.Geometry>} * units - {String} Unit abbreviation * * Returns: * {Float} The geometry area in the given units. */ getArea: function(geometry, units) { var area, geomUnits; if(this.geodesic) { area = geometry.getGeodesicArea(this.map.getProjectionObject()); geomUnits = "m"; } else { area = geometry.getArea(); geomUnits = this.map.getUnits(); } var inPerDisplayUnit = OpenLayers.INCHES_PER_UNIT[units]; if(inPerDisplayUnit) { var inPerMapUnit = OpenLayers.INCHES_PER_UNIT[geomUnits]; area *= Math.pow((inPerMapUnit / inPerDisplayUnit), 2); } return area; }, /** * Method: getBestLength * Based on the <displaySystem> returns the length of a geometry. * * Parameters: * geometry - {<OpenLayers.Geometry>} * * Returns: * {Array([Float, String])} Returns a two item array containing the * length and the units abbreviation. */ getBestLength: function(geometry) { var units = this.displaySystemUnits[this.displaySystem]; var unit, length; for(var i=0, len=units.length; i<len; ++i) { unit = units[i]; length = this.getLength(geometry, unit); if(length > 1) { break; } } return [length, unit]; }, /** * Method: getLength * * Parameters: * geometry - {<OpenLayers.Geometry>} * units - {String} Unit abbreviation * * Returns: * {Float} The geometry length in the given units. */ getLength: function(geometry, units) { var length, geomUnits; if(this.geodesic) { length = geometry.getGeodesicLength(this.map.getProjectionObject()); geomUnits = "m"; } else { length = geometry.getLength(); geomUnits = this.map.getUnits(); } var inPerDisplayUnit = OpenLayers.INCHES_PER_UNIT[units]; if(inPerDisplayUnit) { var inPerMapUnit = OpenLayers.INCHES_PER_UNIT[geomUnits]; length *= (inPerMapUnit / inPerDisplayUnit); } return length; }, CLASS_NAME: "OpenLayers.Control.Measure" }); /* ====================================================================== OpenLayers/Control/Permalink.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/Control/ArgParser.js * @requires OpenLayers/Lang.js */ /** * Class: OpenLayers.Control.Permalink * The Permalink control is hyperlink that will return the user to the * current map view. By default it is drawn in the lower right corner of the * map. The href is updated as the map is zoomed, panned and whilst layers * are switched. * * Inherits from: * - <OpenLayers.Control> */ OpenLayers.Control.Permalink = OpenLayers.Class(OpenLayers.Control, { /** * APIProperty: argParserClass * {Class} The ArgParser control class (not instance) to use with this * control. */ argParserClass: OpenLayers.Control.ArgParser, /** * Property: element * {DOMElement} */ element: null, /** * APIProperty: anchor * {Boolean} This option changes 3 things: * the character '#' is used in place of the character '?', * the window.href is updated if no element is provided. * When this option is set to true it's not recommend to provide * a base without provide an element. */ anchor: false, /** * APIProperty: base * {String} */ base: '', /** * APIProperty: displayProjection * {<OpenLayers.Projection>} Requires proj4js support. Projection used * when creating the coordinates in the link. This will reproject the * map coordinates into display coordinates. If you are using this * functionality, the permalink which is last added to the map will * determine the coordinate type which is read from the URL, which * means you should not add permalinks with different * displayProjections to the same map. */ displayProjection: null, /** * Constructor: OpenLayers.Control.Permalink * * Parameters: * element - {DOMElement} * base - {String} * options - {Object} options to the control. * * Or for anchor: * options - {Object} options to the control. */ initialize: function(element, base, options) { if (element !== null && typeof element == 'object' && !OpenLayers.Util.isElement(element)) { options = element; this.base = document.location.href; OpenLayers.Control.prototype.initialize.apply(this, [options]); if (this.element != null) { this.element = OpenLayers.Util.getElement(this.element); } } else { OpenLayers.Control.prototype.initialize.apply(this, [options]); this.element = OpenLayers.Util.getElement(element); this.base = base || document.location.href; } }, /** * APIMethod: destroy */ destroy: function() { if (this.element && this.element.parentNode == this.div) { this.div.removeChild(this.element); this.element = null; } if (this.map) { this.map.events.unregister('moveend', this, this.updateLink); } OpenLayers.Control.prototype.destroy.apply(this, arguments); }, /** * Method: setMap * Set the map property for the control. * * Parameters: * map - {<OpenLayers.Map>} */ setMap: function(map) { OpenLayers.Control.prototype.setMap.apply(this, arguments); //make sure we have an arg parser attached for(var i=0, len=this.map.controls.length; i<len; i++) { var control = this.map.controls[i]; if (control.CLASS_NAME == this.argParserClass.CLASS_NAME) { // If a permalink is added to the map, and an ArgParser already // exists, we override the displayProjection to be the one // on the permalink. if (control.displayProjection != this.displayProjection) { this.displayProjection = control.displayProjection; } break; } } if (i == this.map.controls.length) { this.map.addControl(new this.argParserClass( { 'displayProjection': this.displayProjection })); } }, /** * Method: draw * * Returns: * {DOMElement} */ draw: function() { OpenLayers.Control.prototype.draw.apply(this, arguments); if (!this.element && !this.anchor) { this.element = document.createElement("a"); this.element.innerHTML = OpenLayers.i18n("Permalink"); this.element.href=""; this.div.appendChild(this.element); } this.map.events.on({ 'moveend': this.updateLink, 'changelayer': this.updateLink, 'changebaselayer': this.updateLink, scope: this }); // Make it so there is at least a link even though the map may not have // moved yet. this.updateLink(); return this.div; }, /** * Method: updateLink */ updateLink: function() { var separator = this.anchor ? '#' : '?'; var href = this.base; var anchor = null; if (href.indexOf("#") != -1 && this.anchor == false) { anchor = href.substring( href.indexOf("#"), href.length); } if (href.indexOf(separator) != -1) { href = href.substring( 0, href.indexOf(separator) ); } var splits = href.split("#"); href = splits[0] + separator+ OpenLayers.Util.getParameterString(this.createParams()); if (anchor) { href += anchor; } if (this.anchor && !this.element) { window.location.href = href; } else { this.element.href = href; } }, /** * APIMethod: createParams * Creates the parameters that need to be encoded into the permalink url. * * Parameters: * center - {<OpenLayers.LonLat>} center to encode in the permalink. * Defaults to the current map center. * zoom - {Integer} zoom level to encode in the permalink. Defaults to the * current map zoom level. * layers - {Array(<OpenLayers.Layer>)} layers to encode in the permalink. * Defaults to the current map layers. * * Returns: * {Object} Hash of parameters that will be url-encoded into the * permalink. */ createParams: function(center, zoom, layers) { center = center || this.map.getCenter(); var params = OpenLayers.Util.getParameters(this.base); // If there's still no center, map is not initialized yet. // Break out of this function, and simply return the params from the // base link. if (center) { //zoom params.zoom = zoom || this.map.getZoom(); //lon,lat var lat = center.lat; var lon = center.lon; if (this.displayProjection) { var mapPosition = OpenLayers.Projection.transform( { x: lon, y: lat }, this.map.getProjectionObject(), this.displayProjection ); lon = mapPosition.x; lat = mapPosition.y; } params.lat = Math.round(lat*100000)/100000; params.lon = Math.round(lon*100000)/100000; //layers layers = layers || this.map.layers; params.layers = ''; for (var i=0, len=layers.length; i<len; i++) { var layer = layers[i]; if (layer.isBaseLayer) { params.layers += (layer == this.map.baseLayer) ? "B" : "0"; } else { params.layers += (layer.getVisibility()) ? "T" : "F"; } } } return params; }, CLASS_NAME: "OpenLayers.Control.Permalink" }); /* ====================================================================== OpenLayers/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/Navigation.js ====================================================================== */ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for * full list of contributors). Published under the 2-clause BSD license. * See license.txt in the OpenLayers distribution or repository for the * full text of the license. */ /** * @requires OpenLayers/Control/ZoomBox.js * @requires OpenLayers/Control/DragPan.js * @requires OpenLayers/Handler/MouseWheel.js * @requires OpenLayers/Handler/Click.js */ /** * Class: OpenLayers.Control.Navigation * The navigation control handles map browsing with mouse events (dragging, * double-clicking, and scrolling the wheel). Create a new navigation * control with the <OpenLayers.Control.Navigation> control. * * Note that this control is added to the map by default (if no controls * array is sent in the options object to the <OpenLayers.Map> * constructor). * * Inherits: * - <OpenLayers.Control> */ OpenLayers.Control.Navigation = OpenLayers.Class(OpenLayers.Control, { /** * Property: dragPan * {<OpenLayers.Control.DragPan>} */ dragPan: null, /** * APIProperty: dragPanOptions * {Object} Options passed to the DragPan control. */ dragPanOptions: null, /** * Property: pinchZoom * {<OpenLayers.Control.PinchZoom>} */ pinchZoom: null, /** * APIProperty: pinchZoomOptions * {Object} Options passed to the PinchZoom control. */ pinchZoomOptions: null, /** * APIProperty: documentDrag * {Boolean} Allow panning of the map by dragging outside map viewport. * Default is false. */ documentDrag: false, /** * Property: zoomBox * {<OpenLayers.Control.ZoomBox>} */ zoomBox: null, /** * APIProperty: zoomBoxEnabled * {Boolean} Whether the user can draw a box to zoom */ zoomBoxEnabled: true, /** * APIProperty: zoomWheelEnabled * {Boolean} Whether the mousewheel should zoom the map */ zoomWheelEnabled: true, /** * Property: mouseWheelOptions * {Object} Options passed to the MouseWheel control (only useful if * <zoomWheelEnabled> is set to true). Default is no options for maps * with fractionalZoom set to true, otherwise * {cumulative: false, interval: 50, maxDelta: 6} */ mouseWheelOptions: null, /** * APIProperty: handleRightClicks * {Boolean} Whether or not to handle right clicks. Default is false. */ handleRightClicks: false, /** * APIProperty: zoomBoxKeyMask * {Integer} <OpenLayers.Handler> key code of the key, which has to be * pressed, while drawing the zoom box with the mouse on the screen. * You should probably set handleRightClicks to true if you use this * with MOD_CTRL, to disable the context menu for machines which use * CTRL-Click as a right click. * Default: <OpenLayers.Handler.MOD_SHIFT> */ zoomBoxKeyMask: OpenLayers.Handler.MOD_SHIFT, /** * APIProperty: autoActivate * {Boolean} Activate the control when it is added to a map. Default is * true. */ autoActivate: true, /** * Constructor: OpenLayers.Control.Navigation * Create a new navigation control * * Parameters: * options - {Object} An optional object whose properties will be set on * the control */ initialize: function(options) { this.handlers = {}; OpenLayers.Control.prototype.initialize.apply(this, arguments); }, /** * Method: destroy * The destroy method is used to perform any clean up before the control * is dereferenced. Typically this is where event listeners are removed * to prevent memory leaks. */ destroy: function() { this.deactivate(); if (this.dragPan) { this.dragPan.destroy(); } this.dragPan = null; if (this.zoomBox) { this.zoomBox.destroy(); } this.zoomBox = null; if (this.pinchZoom) { this.pinchZoom.destroy(); } this.pinchZoom = null; OpenLayers.Control.prototype.destroy.apply(this,arguments); }, /** * Method: activate */ activate: function() { this.dragPan.activate(); if (this.zoomWheelEnabled) { this.handlers.wheel.activate(); } this.handlers.click.activate(); if (this.zoomBoxEnabled) { this.zoomBox.activate(); } if (this.pinchZoom) { this.pinchZoom.activate(); } return OpenLayers.Control.prototype.activate.apply(this,arguments); }, /** * Method: deactivate */ deactivate: function() { if (this.pinchZoom) { this.pinchZoom.deactivate(); } this.zoomBox.deactivate(); this.dragPan.deactivate(); this.handlers.click.deactivate(); this.handlers.wheel.deactivate(); return OpenLayers.Control.prototype.deactivate.apply(this,arguments); }, /** * Method: draw */ draw: function() { // disable right mouse context menu for support of right click events if (this.handleRightClicks) { this.map.viewPortDiv.oncontextmenu = OpenLayers.Function.False; } var clickCallbacks = { 'click': this.defaultClick, 'dblclick': this.defaultDblClick, 'dblrightclick': this.defaultDblRightClick }; var clickOptions = { 'double': true, 'stopDouble': true }; this.handlers.click = new OpenLayers.Handler.Click( this, clickCallbacks, clickOptions ); this.dragPan = new OpenLayers.Control.DragPan( OpenLayers.Util.extend({ map: this.map, documentDrag: this.documentDrag }, this.dragPanOptions) ); this.zoomBox = new OpenLayers.Control.ZoomBox( {map: this.map, keyMask: this.zoomBoxKeyMask}); this.dragPan.draw(); this.zoomBox.draw(); var wheelOptions = this.map.fractionalZoom ? {} : { cumulative: false, interval: 50, maxDelta: 6 }; this.handlers.wheel = new OpenLayers.Handler.MouseWheel( this, {up : this.wheelUp, down: this.wheelDown}, OpenLayers.Util.extend(wheelOptions, this.mouseWheelOptions) ); if (OpenLayers.Control.PinchZoom) { this.pinchZoom = new OpenLayers.Control.PinchZoom( OpenLayers.Util.extend( {map: this.map}, this.pinchZoomOptions)); } }, /** * Method: defaultClick * * Parameters: * evt - {Event} */ defaultClick: function (evt) { if (evt.lastTouches && evt.lastTouches.length == 2) { this.map.zoomOut(); } }, /** * Method: defaultDblClick * * Parameters: * evt - {Event} */ defaultDblClick: function (evt) { this.map.zoomTo(this.map.zoom + 1, evt.xy); }, /** * Method: defaultDblRightClick * * Parameters: * evt - {Event} */ defaultDblRightClick: function (evt) { this.map.zoomTo(this.map.zoom - 1, evt.xy); }, /** * Method: wheelChange * * Parameters: * evt - {Event} * deltaZ - {Integer} */ wheelChange: function(evt, deltaZ) { if (!this.map.fractionalZoom) { deltaZ = Math.round(deltaZ); } var currentZoom = this.map.getZoom(), newZoom = currentZoom + deltaZ; newZoom = Math.max(newZoom, 0); newZoom = Math.min(newZoom, this.map.getNumZoomLevels()); if (newZoom === currentZoom) { return; } this.map.zoomTo(newZoom, evt.xy); }, /** * Method: wheelUp * User spun scroll wheel up * * Parameters: * evt - {Event} * delta - {Integer} */ wheelUp: function(evt, delta) { this.wheelChange(evt, delta || 1); }, /** * Method: wheelDown * User spun scroll wheel down * * Parameters: * evt - {Event} * delta - {Integer} */ wheelDown: function(evt, delta) { this.wheelChange(evt, delta || -1); }, /** * Method: disableZoomBox */ disableZoomBox : function() { this.zoomBoxEnabled = false; this.zoomBox.deactivate(); }, /** * Method: enableZoomBox */ enableZoomBox : function() { this.zoomBoxEnabled = true; if (this.active) { this.zoomBox.activate(); } }, /** * Method: disableZoomWheel */ disableZoomWheel : function() { this.zoomWheelEnabled = false; this.handlers.wheel.deactivate(); }, /** * Method: enableZoomWheel */ enableZoomWheel : function() { this.zoomWheelEnabled = true; if (this.active) { this.handlers.wheel.activate(); } }, CLASS_NAME: "OpenLayers.Control.Navigation" }); /* ====================================================================== OpenLayers/Control/NavToolbar.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/Panel.js * @requires OpenLayers/Control/Navigation.js * @requires OpenLayers/Control/ZoomBox.js */ /** * Class: OpenLayers.Control.NavToolbar * This Toolbar is an alternative to the Navigation control that displays * the state of the control, and provides a UI for changing state to * use the zoomBox via a Panel control. * * If you wish to change the properties of the Navigation control used * in the NavToolbar, see: * http://trac.openlayers.org/wiki/Toolbars#SubclassingNavToolbar * * * Inherits from: * - <OpenLayers.Control.Panel> */ OpenLayers.Control.NavToolbar = OpenLayers.Class(OpenLayers.Control.Panel, { /** * Constructor: OpenLayers.Control.NavToolbar * Add our two mousedefaults controls. * * Parameters: * options - {Object} An optional object whose properties will be used * to extend the control. */ initialize: function(options) { OpenLayers.Control.Panel.prototype.initialize.apply(this, [options]); this.addControls([ new OpenLayers.Control.Navigation(), new OpenLayers.Control.ZoomBox() ]); }, /** * Method: draw * calls the default draw, and then activates mouse defaults. */ draw: function() { var div = OpenLayers.Control.Panel.prototype.draw.apply(this, arguments); if (this.defaultControl === null) { this.defaultControl = this.controls[0]; } return div; }, CLASS_NAME: "OpenLayers.Control.NavToolbar" }); /* ====================================================================== OpenLayers/Control/WMSGetFeatureInfo.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/Click.js * @requires OpenLayers/Handler/Hover.js * @requires OpenLayers/Request.js * @requires OpenLayers/Format/WMSGetFeatureInfo.js */ /** * Class: OpenLayers.Control.WMSGetFeatureInfo * The WMSGetFeatureInfo control uses a WMS query to get information about a point on the map. The * information may be in a display-friendly format such as HTML, or a machine-friendly format such * as GML, depending on the server's capabilities and the client's configuration. This control * handles click or hover events, attempts to parse the results using an OpenLayers.Format, and * fires a 'getfeatureinfo' event with the click position, the raw body of the response, and an * array of features if it successfully read the response. * * Inherits from: * - <OpenLayers.Control> */ OpenLayers.Control.WMSGetFeatureInfo = OpenLayers.Class(OpenLayers.Control, { /** * APIProperty: hover * {Boolean} Send GetFeatureInfo requests when mouse stops moving. * Default is false. */ hover: false, /** * APIProperty: drillDown * {Boolean} Drill down over all WMS layers in the map. When * using drillDown mode, hover is not possible, and an infoFormat that * returns parseable features is required. Default is false. */ drillDown: false, /** * APIProperty: maxFeatures * {Integer} Maximum number of features to return from a WMS query. This * sets the feature_count parameter on WMS GetFeatureInfo * requests. */ maxFeatures: 10, /** * APIProperty: clickCallback * {String} The click callback to register in the * {<OpenLayers.Handler.Click>} object created when the hover * option is set to false. Default is "click". */ clickCallback: "click", /** * APIProperty: output * {String} Either "features" or "object". When triggering a getfeatureinfo * request should we pass on an array of features or an object with with * a "features" property and other properties (such as the url of the * WMS). Default is "features". */ output: "features", /** * APIProperty: layers * {Array(<OpenLayers.Layer.WMS>)} The layers to query for feature info. * If omitted, all map WMS layers with a url that matches this <url> or * <layerUrls> will be considered. */ layers: null, /** * APIProperty: queryVisible * {Boolean} If true, filter out hidden layers when searching the map for * layers to query. Default is false. */ queryVisible: false, /** * APIProperty: url * {String} The URL of the WMS service to use. If not provided, the url * of the first eligible layer will be used. */ url: null, /** * APIProperty: layerUrls * {Array(String)} Optional list of urls for layers that should be queried. * This can be used when the layer url differs from the url used for * making GetFeatureInfo requests (in the case of a layer using cached * tiles). */ layerUrls: null, /** * APIProperty: infoFormat * {String} The mimetype to request from the server. If you are using * drillDown mode and have multiple servers that do not share a common * infoFormat, you can override the control's infoFormat by providing an * INFO_FORMAT parameter in your <OpenLayers.Layer.WMS> instance(s). */ infoFormat: 'text/html', /** * APIProperty: vendorParams * {Object} Additional parameters that will be added to the request, for * WMS implementations that support them. This could e.g. look like * (start code) * { * radius: 5 * } * (end) */ vendorParams: {}, /** * APIProperty: format * {<OpenLayers.Format>} A format for parsing GetFeatureInfo responses. * Default is <OpenLayers.Format.WMSGetFeatureInfo>. */ format: null, /** * APIProperty: formatOptions * {Object} Optional properties to set on the format (if one is not provided * in the <format> property. */ formatOptions: null, /** * APIProperty: handlerOptions * {Object} Additional options for the handlers used by this control, e.g. * (start code) * { * "click": {delay: 100}, * "hover": {delay: 300} * } * (end) */ /** * Property: handler * {Object} Reference to the <OpenLayers.Handler> for this control */ handler: null, /** * Property: hoverRequest * {<OpenLayers.Request>} contains the currently running hover request * (if any). */ hoverRequest: 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>): * beforegetfeatureinfo - Triggered before the request is sent. * The event object has an *xy* property with the position of the * mouse click or hover event that triggers the request. * nogetfeatureinfo - no queryable layers were found. * getfeatureinfo - Triggered when a GetFeatureInfo response is received. * The event object has a *text* property with the body of the * response (String), a *features* property with an array of the * parsed features, an *xy* property with the position of the mouse * click or hover event that triggered the request, and a *request* * property with the request itself. If drillDown is set to true and * multiple requests were issued to collect feature info from all * layers, *text* and *request* will only contain the response body * and request object of the last request. */ /** * Constructor: <OpenLayers.Control.WMSGetFeatureInfo> * * Parameters: * options - {Object} */ initialize: function(options) { options = options || {}; options.handlerOptions = options.handlerOptions || {}; OpenLayers.Control.prototype.initialize.apply(this, [options]); if(!this.format) { this.format = new OpenLayers.Format.WMSGetFeatureInfo( options.formatOptions ); } if(this.drillDown === true) { this.hover = false; } if(this.hover) { this.handler = new OpenLayers.Handler.Hover( this, { 'move': this.cancelHover, 'pause': this.getInfoForHover }, OpenLayers.Util.extend(this.handlerOptions.hover || {}, { 'delay': 250 })); } else { var callbacks = {}; callbacks[this.clickCallback] = this.getInfoForClick; this.handler = new OpenLayers.Handler.Click( this, callbacks, this.handlerOptions.click || {}); } }, /** * Method: getInfoForClick * Called on click * * Parameters: * evt - {<OpenLayers.Event>} */ getInfoForClick: function(evt) { this.events.triggerEvent("beforegetfeatureinfo", {xy: evt.xy}); // Set the cursor to "wait" to tell the user we're working on their // click. OpenLayers.Element.addClass(this.map.viewPortDiv, "olCursorWait"); this.request(evt.xy, {}); }, /** * Method: getInfoForHover * Pause callback for the hover handler * * Parameters: * evt - {Object} */ getInfoForHover: function(evt) { this.events.triggerEvent("beforegetfeatureinfo", {xy: evt.xy}); this.request(evt.xy, {hover: true}); }, /** * Method: cancelHover * Cancel callback for the hover handler */ cancelHover: function() { if (this.hoverRequest) { this.hoverRequest.abort(); this.hoverRequest = null; } }, /** * Method: findLayers * Internal method to get the layers, independent of whether we are * inspecting the map or using a client-provided array */ findLayers: function() { var candidates = this.layers || this.map.layers; var layers = []; var layer, url; for(var i = candidates.length - 1; i >= 0; --i) { layer = candidates[i]; if(layer instanceof OpenLayers.Layer.WMS && (!this.queryVisible || layer.getVisibility())) { url = OpenLayers.Util.isArray(layer.url) ? layer.url[0] : layer.url; // if the control was not configured with a url, set it // to the first layer url if(this.drillDown === false && !this.url) { this.url = url; } if(this.drillDown === true || this.urlMatches(url)) { layers.push(layer); } } } return layers; }, /** * Method: urlMatches * Test to see if the provided url matches either the control <url> or one * of the <layerUrls>. * * Parameters: * url - {String} The url to test. * * Returns: * {Boolean} The provided url matches the control <url> or one of the * <layerUrls>. */ urlMatches: function(url) { var matches = OpenLayers.Util.isEquivalentUrl(this.url, url); if(!matches && this.layerUrls) { for(var i=0, len=this.layerUrls.length; i<len; ++i) { if(OpenLayers.Util.isEquivalentUrl(this.layerUrls[i], url)) { matches = true; break; } } } return matches; }, /** * Method: buildWMSOptions * Build an object with the relevant WMS options for the GetFeatureInfo request * * Parameters: * url - {String} The url to be used for sending the request * layers - {Array(<OpenLayers.Layer.WMS)} An array of layers * clickPosition - {<OpenLayers.Pixel>} The position on the map where the mouse * event occurred. * format - {String} The format from the corresponding GetMap request */ buildWMSOptions: function(url, layers, clickPosition, format) { var layerNames = [], styleNames = []; for (var i = 0, len = layers.length; i < len; i++) { if (layers[i].params.LAYERS != null) { layerNames = layerNames.concat(layers[i].params.LAYERS); styleNames = styleNames.concat(this.getStyleNames(layers[i])); } } var firstLayer = layers[0]; // use the firstLayer's projection if it matches the map projection - // this assumes that all layers will be available in this projection var projection = this.map.getProjection(); var layerProj = firstLayer.projection; if (layerProj && layerProj.equals(this.map.getProjectionObject())) { projection = layerProj.getCode(); } var params = OpenLayers.Util.extend({ service: "WMS", version: firstLayer.params.VERSION, request: "GetFeatureInfo", exceptions: firstLayer.params.EXCEPTIONS, bbox: this.map.getExtent().toBBOX(null, firstLayer.reverseAxisOrder()), feature_count: this.maxFeatures, height: this.map.getSize().h, width: this.map.getSize().w, format: format, info_format: firstLayer.params.INFO_FORMAT || this.infoFormat }, (parseFloat(firstLayer.params.VERSION) >= 1.3) ? { crs: projection, i: parseInt(clickPosition.x), j: parseInt(clickPosition.y) } : { srs: projection, x: parseInt(clickPosition.x), y: parseInt(clickPosition.y) } ); if (layerNames.length != 0) { params = OpenLayers.Util.extend({ layers: layerNames, query_layers: layerNames, styles: styleNames }, params); } OpenLayers.Util.applyDefaults(params, this.vendorParams); return { url: url, params: OpenLayers.Util.upperCaseObject(params), callback: function(request) { this.handleResponse(clickPosition, request, url); }, scope: this }; }, /** * Method: getStyleNames * Gets the STYLES parameter for the layer. Make sure the STYLES parameter * matches the LAYERS parameter * * Parameters: * layer - {<OpenLayers.Layer.WMS>} * * Returns: * {Array(String)} The STYLES parameter */ getStyleNames: function(layer) { // in the event of a WMS layer bundling multiple layers but not // specifying styles,we need the same number of commas to specify // the default style for each of the layers. We can't just leave it // blank as we may be including other layers that do specify styles. var styleNames; if (layer.params.STYLES) { styleNames = layer.params.STYLES; } else { if (OpenLayers.Util.isArray(layer.params.LAYERS)) { styleNames = new Array(layer.params.LAYERS.length); } else { // Assume it's a String styleNames = layer.params.LAYERS.replace(/[^,]/g, ""); } } return styleNames; }, /** * Method: request * Sends a GetFeatureInfo request to the WMS * * Parameters: * clickPosition - {<OpenLayers.Pixel>} The position on the map where the * mouse event occurred. * options - {Object} additional options for this method. * * Valid options: * - *hover* {Boolean} true if we do the request for the hover handler */ request: function(clickPosition, options) { var layers = this.findLayers(); if(layers.length == 0) { this.events.triggerEvent("nogetfeatureinfo"); // Reset the cursor. OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait"); return; } options = options || {}; if(this.drillDown === false) { var wmsOptions = this.buildWMSOptions(this.url, layers, clickPosition, layers[0].params.FORMAT); var request = OpenLayers.Request.GET(wmsOptions); if (options.hover === true) { this.hoverRequest = request; } } else { this._requestCount = 0; this._numRequests = 0; this.features = []; // group according to service url to combine requests var services = {}, url; for(var i=0, len=layers.length; i<len; i++) { var layer = layers[i]; var service, found = false; url = OpenLayers.Util.isArray(layer.url) ? layer.url[0] : layer.url; if(url in services) { services[url].push(layer); } else { this._numRequests++; services[url] = [layer]; } } var layers; for (var url in services) { layers = services[url]; var wmsOptions = this.buildWMSOptions(url, layers, clickPosition, layers[0].params.FORMAT); OpenLayers.Request.GET(wmsOptions); } } }, /** * Method: triggerGetFeatureInfo * Trigger the getfeatureinfo event when all is done * * Parameters: * request - {XMLHttpRequest} The request object * xy - {<OpenLayers.Pixel>} The position on the map where the * mouse event occurred. * features - {Array(<OpenLayers.Feature.Vector>)} or * {Array({Object}) when output is "object". The object has a url and a * features property which contains an array of features. */ triggerGetFeatureInfo: function(request, xy, features) { this.events.triggerEvent("getfeatureinfo", { text: request.responseText, features: features, request: request, xy: xy }); // Reset the cursor. OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait"); }, /** * Method: handleResponse * Handler for the GetFeatureInfo response. * * Parameters: * xy - {<OpenLayers.Pixel>} The position on the map where the * mouse event occurred. * request - {XMLHttpRequest} The request object. * url - {String} The url which was used for this request. */ handleResponse: function(xy, request, url) { var doc = request.responseXML; if(!doc || !doc.documentElement) { doc = request.responseText; } var features = this.format.read(doc); if (this.drillDown === false) { this.triggerGetFeatureInfo(request, xy, features); } else { this._requestCount++; if (this.output === "object") { this._features = (this._features || []).concat( {url: url, features: features} ); } else { this._features = (this._features || []).concat(features); } if (this._requestCount === this._numRequests) { this.triggerGetFeatureInfo(request, xy, this._features.concat()); delete this._features; delete this._requestCount; delete this._numRequests; } } }, CLASS_NAME: "OpenLayers.Control.WMSGetFeatureInfo" }); /* ====================================================================== OpenLayers/Control/DragFeature.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/Feature.js */ /** * Class: OpenLayers.Control.DragFeature * The DragFeature control moves a feature with a drag of the mouse. Create a * new control with the <OpenLayers.Control.DragFeature> constructor. * * Inherits From: * - <OpenLayers.Control> */ OpenLayers.Control.DragFeature = OpenLayers.Class(OpenLayers.Control, { /** * APIProperty: geometryTypes * {Array(String)} To restrict dragging to a limited set of geometry types, * send a list of strings corresponding to the geometry class names. */ geometryTypes: null, /** * APIProperty: onStart * {Function} Define this function if you want to know when a drag starts. * The function should expect to receive two arguments: the feature * that is about to be dragged and the pixel location of the mouse. * * Parameters: * feature - {<OpenLayers.Feature.Vector>} The feature that is about to be * dragged. * pixel - {<OpenLayers.Pixel>} The pixel location of the mouse. */ onStart: function(feature, pixel) {}, /** * APIProperty: onDrag * {Function} Define this function if you want to know about each move of a * feature. The function should expect to receive two arguments: the * feature that is being dragged and the pixel location of the mouse. * * Parameters: * feature - {<OpenLayers.Feature.Vector>} The feature that was dragged. * pixel - {<OpenLayers.Pixel>} The pixel location of the mouse. */ onDrag: function(feature, pixel) {}, /** * APIProperty: onComplete * {Function} Define this function if you want to know when a feature is * done dragging. The function should expect to receive two arguments: * the feature that is being dragged and the pixel location of the * mouse. * * Parameters: * feature - {<OpenLayers.Feature.Vector>} The feature that was dragged. * pixel - {<OpenLayers.Pixel>} The pixel location of the mouse. */ onComplete: function(feature, pixel) {}, /** * APIProperty: onEnter * {Function} Define this function if you want to know when the mouse * goes over a feature and thereby makes this feature a candidate * for dragging. * * Parameters: * feature - {<OpenLayers.Feature.Vector>} The feature that is ready * to be dragged. */ onEnter: function(feature) {}, /** * APIProperty: onLeave * {Function} Define this function if you want to know when the mouse * goes out of the feature that was dragged. * * Parameters: * feature - {<OpenLayers.Feature.Vector>} The feature that was dragged. */ onLeave: function(feature) {}, /** * 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: layer * {<OpenLayers.Layer.Vector>} */ layer: null, /** * Property: feature * {<OpenLayers.Feature.Vector>} */ feature: null, /** * Property: dragCallbacks * {Object} The functions that are sent to the drag handler for callback. */ dragCallbacks: {}, /** * Property: featureCallbacks * {Object} The functions that are sent to the feature handler for callback. */ featureCallbacks: {}, /** * Property: lastPixel * {<OpenLayers.Pixel>} */ lastPixel: null, /** * Constructor: OpenLayers.Control.DragFeature * Create a new control to drag features. * * Parameters: * layer - {<OpenLayers.Layer.Vector>} The layer containing features to be * dragged. * options - {Object} Optional object whose properties will be set on the * control. */ initialize: function(layer, options) { OpenLayers.Control.prototype.initialize.apply(this, [options]); this.layer = layer; this.handlers = { drag: new OpenLayers.Handler.Drag( this, OpenLayers.Util.extend({ down: this.downFeature, move: this.moveFeature, up: this.upFeature, out: this.cancel, done: this.doneDragging }, this.dragCallbacks), { documentDrag: this.documentDrag } ), feature: new OpenLayers.Handler.Feature( this, this.layer, OpenLayers.Util.extend({ // 'click' and 'clickout' callback are for the mobile // support: no 'over' or 'out' in touch based browsers. click: this.clickFeature, clickout: this.clickoutFeature, over: this.overFeature, out: this.outFeature }, this.featureCallbacks), {geometryTypes: this.geometryTypes} ) }; }, /** * Method: clickFeature * Called when the feature handler detects a click-in on a feature. * * Parameters: * feature - {<OpenLayers.Feature.Vector>} */ clickFeature: function(feature) { if (this.handlers.feature.touch && !this.over && this.overFeature(feature)) { this.handlers.drag.dragstart(this.handlers.feature.evt); // to let the events propagate to the feature handler (click callback) this.handlers.drag.stopDown = false; } }, /** * Method: clickoutFeature * Called when the feature handler detects a click-out on a feature. * * Parameters: * feature - {<OpenLayers.Feature.Vector>} */ clickoutFeature: function(feature) { if (this.handlers.feature.touch && this.over) { this.outFeature(feature); this.handlers.drag.stopDown = true; } }, /** * APIMethod: destroy * Take care of things that are not handled in superclass */ destroy: function() { this.layer = null; OpenLayers.Control.prototype.destroy.apply(this, []); }, /** * APIMethod: activate * Activate the control and the feature handler. * * Returns: * {Boolean} Successfully activated the control and feature handler. */ activate: function() { return (this.handlers.feature.activate() && OpenLayers.Control.prototype.activate.apply(this, arguments)); }, /** * APIMethod: deactivate * Deactivate the control and all handlers. * * Returns: * {Boolean} Successfully deactivated the control. */ deactivate: function() { // the return from the handlers is unimportant in this case this.handlers.drag.deactivate(); this.handlers.feature.deactivate(); this.feature = null; this.dragging = false; this.lastPixel = null; OpenLayers.Element.removeClass( this.map.viewPortDiv, this.displayClass + "Over" ); return OpenLayers.Control.prototype.deactivate.apply(this, arguments); }, /** * Method: overFeature * Called when the feature handler detects a mouse-over on a feature. * This activates the drag handler. * * Parameters: * feature - {<OpenLayers.Feature.Vector>} The selected feature. * * Returns: * {Boolean} Successfully activated the drag handler. */ overFeature: function(feature) { var activated = false; if(!this.handlers.drag.dragging) { this.feature = feature; this.handlers.drag.activate(); activated = true; this.over = true; OpenLayers.Element.addClass(this.map.viewPortDiv, this.displayClass + "Over"); this.onEnter(feature); } else { if(this.feature.id == feature.id) { this.over = true; } else { this.over = false; } } return activated; }, /** * Method: downFeature * Called when the drag handler detects a mouse-down. * * Parameters: * pixel - {<OpenLayers.Pixel>} Location of the mouse event. */ downFeature: function(pixel) { this.lastPixel = pixel; this.onStart(this.feature, pixel); }, /** * Method: moveFeature * Called when the drag handler detects a mouse-move. Also calls the * optional onDrag method. * * Parameters: * pixel - {<OpenLayers.Pixel>} Location of the mouse event. */ moveFeature: function(pixel) { var res = this.map.getResolution(); this.feature.geometry.move(res * (pixel.x - this.lastPixel.x), res * (this.lastPixel.y - pixel.y)); this.layer.drawFeature(this.feature); this.lastPixel = pixel; this.onDrag(this.feature, pixel); }, /** * Method: upFeature * Called when the drag handler detects a mouse-up. * * Parameters: * pixel - {<OpenLayers.Pixel>} Location of the mouse event. */ upFeature: function(pixel) { if(!this.over) { this.handlers.drag.deactivate(); } }, /** * Method: doneDragging * Called when the drag handler is done dragging. * * Parameters: * pixel - {<OpenLayers.Pixel>} The last event pixel location. If this event * came from a mouseout, this may not be in the map viewport. */ doneDragging: function(pixel) { this.onComplete(this.feature, pixel); }, /** * Method: outFeature * Called when the feature handler detects a mouse-out on a feature. * * Parameters: * feature - {<OpenLayers.Feature.Vector>} The feature that the mouse left. */ outFeature: function(feature) { if(!this.handlers.drag.dragging) { this.over = false; this.handlers.drag.deactivate(); OpenLayers.Element.removeClass( this.map.viewPortDiv, this.displayClass + "Over" ); this.onLeave(feature); this.feature = null; } else { if(this.feature.id == feature.id) { this.over = false; } } }, /** * Method: cancel * Called when the drag handler detects a mouse-out (from the map viewport). */ cancel: function() { this.handlers.drag.deactivate(); this.over = false; }, /** * Method: setMap * Set the map property for the control and all handlers. * * Parameters: * map - {<OpenLayers.Map>} The control's map. */ setMap: function(map) { this.handlers.drag.setMap(map); this.handlers.feature.setMap(map); OpenLayers.Control.prototype.setMap.apply(this, arguments); }, CLASS_NAME: "OpenLayers.Control.DragFeature" }); /* ====================================================================== OpenLayers/Control/SLDSelect.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/Layer/WMS.js * @requires OpenLayers/Handler/RegularPolygon.js * @requires OpenLayers/Handler/Polygon.js * @requires OpenLayers/Handler/Path.js * @requires OpenLayers/Handler/Click.js * @requires OpenLayers/Filter/Spatial.js * @requires OpenLayers/Format/SLD/v1_0_0.js */ /** * Class: OpenLayers.Control.SLDSelect * Perform selections on WMS layers using Styled Layer Descriptor (SLD) * * Inherits from: * - <OpenLayers.Control> */ OpenLayers.Control.SLDSelect = 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>): * selected - Triggered when a selection occurs. Listeners receive an * event with *filters* and *layer* properties. Filters will be an * array of OpenLayers.Filter objects created in order to perform * the particular selection. */ /** * APIProperty: clearOnDeactivate * {Boolean} Should the selection be cleared when the control is * deactivated. Default value is false. */ clearOnDeactivate: false, /** * APIProperty: layers * {Array(<OpenLayers.Layer.WMS>)} The WMS layers this control will work * on. */ layers: null, /** * Property: callbacks * {Object} The functions that are sent to the handler for callback */ callbacks: null, /** * APIProperty: selectionSymbolizer * {Object} Determines the styling of the selected objects. Default is * a selection in red. */ selectionSymbolizer: { 'Polygon': {fillColor: '#FF0000', stroke: false}, 'Line': {strokeColor: '#FF0000', strokeWidth: 2}, 'Point': {graphicName: 'square', fillColor: '#FF0000', pointRadius: 5} }, /** * APIProperty: layerOptions * {Object} The options to apply to the selection layer, by default the * selection layer will be kept out of the layer switcher. */ layerOptions: null, /** * APIProperty: handlerOptions * {Object} Used to set non-default properties on the control's handler */ /** * APIProperty: sketchStyle * {<OpenLayers.Style>|Object} Style or symbolizer to use for the sketch * handler. The recommended way of styling the sketch layer, however, is * to configure an <OpenLayers.StyleMap> in the layerOptions of the * <handlerOptions>: * * (code) * new OpenLayers.Control.SLDSelect(OpenLayers.Handler.Path, { * handlerOptions: { * layerOptions: { * styleMap: new OpenLayers.StyleMap({ * "default": {strokeColor: "yellow"} * }) * } * } * }); * (end) */ sketchStyle: null, /** * APIProperty: wfsCache * {Object} Cache to use for storing parsed results from * <OpenLayers.Format.WFSDescribeFeatureType.read>. If not provided, * these will be cached on the prototype. */ wfsCache: {}, /** * APIProperty: layerCache * {Object} Cache to use for storing references to the selection layers. * Normally each source layer will have exactly 1 selection layer of * type OpenLayers.Layer.WMS. If not provided, layers will * be cached on the prototype. Note that if <clearOnDeactivate> is * true, the layer will no longer be cached after deactivating the * control. */ layerCache: {}, /** * Constructor: OpenLayers.Control.SLDSelect * Create a new control for selecting features in WMS layers using * Styled Layer Descriptor (SLD). * * Parameters: * handler - {<OpenLayers.Class>} A sketch handler class. This determines * the type of selection, e.g. box (<OpenLayers.Handler.Box>), point * (<OpenLayers.Handler.Point>), path (<OpenLayers.Handler.Path>) or * polygon (<OpenLayers.Handler.Polygon>) selection. To use circle * type selection, use <OpenLayers.Handler.RegularPolygon> and pass * the number of desired sides (e.g. 40) as "sides" property to the * <handlerOptions>. * options - {Object} An object containing all configuration properties for * the control. * * Valid options: * layers - Array({<OpenLayers.Layer.WMS>}) The layers to perform the * selection on. */ initialize: function(handler, options) { OpenLayers.Control.prototype.initialize.apply(this, [options]); this.callbacks = OpenLayers.Util.extend({done: this.select, click: this.select}, this.callbacks); this.handlerOptions = this.handlerOptions || {}; this.layerOptions = OpenLayers.Util.applyDefaults(this.layerOptions, { displayInLayerSwitcher: false, tileOptions: {maxGetUrlLength: 2048} }); if (this.sketchStyle) { this.handlerOptions.layerOptions = OpenLayers.Util.applyDefaults( this.handlerOptions.layerOptions, {styleMap: new OpenLayers.StyleMap({"default": this.sketchStyle})} ); } this.handler = new handler(this, this.callbacks, this.handlerOptions); }, /** * APIMethod: destroy * Take care of things that are not handled in superclass. */ destroy: function() { for (var key in this.layerCache) { delete this.layerCache[key]; } for (var key in this.wfsCache) { delete this.wfsCache[key]; } OpenLayers.Control.prototype.destroy.apply(this, arguments); }, /** * Method: coupleLayerVisiblity * Couple the selection layer and the source layer with respect to * layer visibility. So if the source layer is turned off, the * selection layer is also turned off. * * Context: * - {<OpenLayers.Layer>} * * Parameters: * evt - {Object} */ coupleLayerVisiblity: function(evt) { this.setVisibility(evt.object.getVisibility()); }, /** * Method: createSelectionLayer * Creates a "clone" from the source layer in which the selection can * be drawn. This ensures both the source layer and the selection are * visible and not only the selection. * * Parameters: * source - {<OpenLayers.Layer.WMS>} The source layer on which the selection * is performed. * * Returns: * {<OpenLayers.Layer.WMS>} A WMS layer with maxGetUrlLength configured to 2048 * since SLD selections can easily get quite long. */ createSelectionLayer: function(source) { // check if we already have a selection layer for the source layer var selectionLayer; if (!this.layerCache[source.id]) { selectionLayer = new OpenLayers.Layer.WMS(source.name, source.url, source.params, OpenLayers.Util.applyDefaults( this.layerOptions, source.getOptions()) ); this.layerCache[source.id] = selectionLayer; // make sure the layers are coupled wrt visibility, but only // if they are not displayed in the layer switcher, because in // that case the user cannot control visibility. if (this.layerOptions.displayInLayerSwitcher === false) { source.events.on({ "visibilitychanged": this.coupleLayerVisiblity, scope: selectionLayer}); } this.map.addLayer(selectionLayer); } else { selectionLayer = this.layerCache[source.id]; } return selectionLayer; }, /** * Method: createSLD * Create the SLD document for the layer using the supplied filters. * * Parameters: * layer - {<OpenLayers.Layer.WMS>} * filters - Array({<OpenLayers.Filter>}) The filters to be applied. * geometryAttributes - Array({Object}) The geometry attributes of the * layer. * * Returns: * {String} The SLD document generated as a string. */ createSLD: function(layer, filters, geometryAttributes) { var sld = {version: "1.0.0", namedLayers: {}}; var layerNames = [layer.params.LAYERS].join(",").split(","); for (var i=0, len=layerNames.length; i<len; i++) { var name = layerNames[i]; sld.namedLayers[name] = {name: name, userStyles: []}; var symbolizer = this.selectionSymbolizer; var geometryAttribute = geometryAttributes[i]; if (geometryAttribute.type.indexOf('Polygon') >= 0) { symbolizer = {Polygon: this.selectionSymbolizer['Polygon']}; } else if (geometryAttribute.type.indexOf('LineString') >= 0) { symbolizer = {Line: this.selectionSymbolizer['Line']}; } else if (geometryAttribute.type.indexOf('Point') >= 0) { symbolizer = {Point: this.selectionSymbolizer['Point']}; } var filter = filters[i]; sld.namedLayers[name].userStyles.push({name: 'default', rules: [ new OpenLayers.Rule({symbolizer: symbolizer, filter: filter, maxScaleDenominator: layer.options.minScale}) ]}); } return new OpenLayers.Format.SLD({srsName: this.map.getProjection()}).write(sld); }, /** * Method: parseDescribeLayer * Parse the SLD WMS DescribeLayer response and issue the corresponding * WFS DescribeFeatureType request * * request - {XMLHttpRequest} The request object. */ parseDescribeLayer: function(request) { var format = new OpenLayers.Format.WMSDescribeLayer(); var doc = request.responseXML; if(!doc || !doc.documentElement) { doc = request.responseText; } var describeLayer = format.read(doc); var typeNames = []; var url = null; for (var i=0, len=describeLayer.length; i<len; i++) { // perform a WFS DescribeFeatureType request if (describeLayer[i].owsType == "WFS") { typeNames.push(describeLayer[i].typeName); url = describeLayer[i].owsURL; } } var options = { url: url, params: { SERVICE: "WFS", TYPENAME: typeNames.toString(), REQUEST: "DescribeFeatureType", VERSION: "1.0.0" }, callback: function(request) { var format = new OpenLayers.Format.WFSDescribeFeatureType(); var doc = request.responseXML; if(!doc || !doc.documentElement) { doc = request.responseText; } var describeFeatureType = format.read(doc); this.control.wfsCache[this.layer.id] = describeFeatureType; this.control._queue && this.control.applySelection(); }, scope: this }; OpenLayers.Request.GET(options); }, /** * Method: getGeometryAttributes * Look up the geometry attributes from the WFS DescribeFeatureType response * * Parameters: * layer - {<OpenLayers.Layer.WMS>} The layer for which to look up the * geometry attributes. * * Returns: * Array({Object}) Array of geometry attributes */ getGeometryAttributes: function(layer) { var result = []; var cache = this.wfsCache[layer.id]; for (var i=0, len=cache.featureTypes.length; i<len; i++) { var typeName = cache.featureTypes[i]; var properties = typeName.properties; for (var j=0, lenj=properties.length; j < lenj; j++) { var property = properties[j]; var type = property.type; if ((type.indexOf('LineString') >= 0) || (type.indexOf('GeometryAssociationType') >=0) || (type.indexOf('GeometryPropertyType') >= 0) || (type.indexOf('Point') >= 0) || (type.indexOf('Polygon') >= 0) ) { result.push(property); } } } return result; }, /** * APIMethod: activate * Activate the control. Activating the control will perform a SLD WMS * DescribeLayer request followed by a WFS DescribeFeatureType request * so that the proper symbolizers can be chosen based on the geometry * type. */ activate: function() { var activated = OpenLayers.Control.prototype.activate.call(this); if(activated) { for (var i=0, len=this.layers.length; i<len; i++) { var layer = this.layers[i]; if (layer && !this.wfsCache[layer.id]) { var options = { url: layer.url, params: { SERVICE: "WMS", VERSION: layer.params.VERSION, LAYERS: layer.params.LAYERS, REQUEST: "DescribeLayer" }, callback: this.parseDescribeLayer, scope: {layer: layer, control: this} }; OpenLayers.Request.GET(options); } } } return activated; }, /** * APIMethod: deactivate * Deactivate the control. If clearOnDeactivate is true, remove the * selection layer(s). */ deactivate: function() { var deactivated = OpenLayers.Control.prototype.deactivate.call(this); if(deactivated) { for (var i=0, len=this.layers.length; i<len; i++) { var layer = this.layers[i]; if (layer && this.clearOnDeactivate === true) { var layerCache = this.layerCache; var selectionLayer = layerCache[layer.id]; if (selectionLayer) { layer.events.un({ "visibilitychanged": this.coupleLayerVisiblity, scope: selectionLayer}); selectionLayer.destroy(); delete layerCache[layer.id]; } } } } return deactivated; }, /** * APIMethod: setLayers * Set the layers on which the selection should be performed. Call the * setLayers method if the layer(s) to be used change and the same * control should be used on a new set of layers. * If the control is already active, it will be active after the new * set of layers is set. * * Parameters: * layers - {Array(<OpenLayers.Layer.WMS>)} The new set of layers on which * the selection should be performed. */ setLayers: function(layers) { if(this.active) { this.deactivate(); this.layers = layers; this.activate(); } else { this.layers = layers; } }, /** * Function: createFilter * Create the filter to be used in the SLD. * * Parameters: * geometryAttribute - {Object} Used to get the name of the geometry * attribute which is needed for constructing the spatial filter. * geometry - {<OpenLayers.Geometry>} The geometry to use. * * Returns: * {<OpenLayers.Filter.Spatial>} The spatial filter created. */ createFilter: function(geometryAttribute, geometry) { var filter = null; if (this.handler instanceof OpenLayers.Handler.RegularPolygon) { // box if (this.handler.irregular === true) { filter = new OpenLayers.Filter.Spatial({ type: OpenLayers.Filter.Spatial.BBOX, property: geometryAttribute.name, value: geometry.getBounds()} ); } else { filter = new OpenLayers.Filter.Spatial({ type: OpenLayers.Filter.Spatial.INTERSECTS, property: geometryAttribute.name, value: geometry} ); } } else if (this.handler instanceof OpenLayers.Handler.Polygon) { filter = new OpenLayers.Filter.Spatial({ type: OpenLayers.Filter.Spatial.INTERSECTS, property: geometryAttribute.name, value: geometry} ); } else if (this.handler instanceof OpenLayers.Handler.Path) { // if source layer is point based, use DWITHIN instead if (geometryAttribute.type.indexOf('Point') >= 0) { filter = new OpenLayers.Filter.Spatial({ type: OpenLayers.Filter.Spatial.DWITHIN, property: geometryAttribute.name, distance: this.map.getExtent().getWidth()*0.01 , distanceUnits: this.map.getUnits(), value: geometry} ); } else { filter = new OpenLayers.Filter.Spatial({ type: OpenLayers.Filter.Spatial.INTERSECTS, property: geometryAttribute.name, value: geometry} ); } } else if (this.handler instanceof OpenLayers.Handler.Click) { if (geometryAttribute.type.indexOf('Polygon') >= 0) { filter = new OpenLayers.Filter.Spatial({ type: OpenLayers.Filter.Spatial.INTERSECTS, property: geometryAttribute.name, value: geometry} ); } else { filter = new OpenLayers.Filter.Spatial({ type: OpenLayers.Filter.Spatial.DWITHIN, property: geometryAttribute.name, distance: this.map.getExtent().getWidth()*0.01 , distanceUnits: this.map.getUnits(), value: geometry} ); } } return filter; }, /** * Method: select * When the handler is done, use SLD_BODY on the selection layer to * display the selection in the map. * * Parameters: * geometry - {Object} or {<OpenLayers.Geometry>} */ select: function(geometry) { this._queue = function() { for (var i=0, len=this.layers.length; i<len; i++) { var layer = this.layers[i]; var geometryAttributes = this.getGeometryAttributes(layer); var filters = []; for (var j=0, lenj=geometryAttributes.length; j<lenj; j++) { var geometryAttribute = geometryAttributes[j]; if (geometryAttribute !== null) { // from the click handler we will not get an actual // geometry so transform if (!(geometry instanceof OpenLayers.Geometry)) { var point = this.map.getLonLatFromPixel( geometry.xy); geometry = new OpenLayers.Geometry.Point( point.lon, point.lat); } var filter = this.createFilter(geometryAttribute, geometry); if (filter !== null) { filters.push(filter); } } } var selectionLayer = this.createSelectionLayer(layer); this.events.triggerEvent("selected", { layer: layer, filters: filters }); var sld = this.createSLD(layer, filters, geometryAttributes); selectionLayer.mergeNewParams({SLD_BODY: sld}); delete this._queue; } }; this.applySelection(); }, /** * Method: applySelection * Checks if all required wfs data is cached, and applies the selection */ applySelection: function() { var canApply = true; for (var i=0, len=this.layers.length; i<len; i++) { if(!this.wfsCache[this.layers[i].id]) { canApply = false; break; } } canApply && this._queue.call(this); }, CLASS_NAME: "OpenLayers.Control.SLDSelect" }); /* ====================================================================== 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/Control/EditingToolbar.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/Panel.js * @requires OpenLayers/Control/Navigation.js * @requires OpenLayers/Control/DrawFeature.js * @requires OpenLayers/Handler/Point.js * @requires OpenLayers/Handler/Path.js * @requires OpenLayers/Handler/Polygon.js */ /** * Class: OpenLayers.Control.EditingToolbar * The EditingToolbar is a panel of 4 controls to draw polygons, lines, * points, or to navigate the map by panning. By default it appears in the * upper right corner of the map. * * Inherits from: * - <OpenLayers.Control.Panel> */ OpenLayers.Control.EditingToolbar = OpenLayers.Class( OpenLayers.Control.Panel, { /** * 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, /** * Constructor: OpenLayers.Control.EditingToolbar * Create an editing toolbar for a given layer. * * Parameters: * layer - {<OpenLayers.Layer.Vector>} * options - {Object} */ initialize: function(layer, options) { OpenLayers.Control.Panel.prototype.initialize.apply(this, [options]); this.addControls( [ new OpenLayers.Control.Navigation() ] ); var controls = [ new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Point, { displayClass: 'olControlDrawFeaturePoint', handlerOptions: {citeCompliant: this.citeCompliant} }), new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Path, { displayClass: 'olControlDrawFeaturePath', handlerOptions: {citeCompliant: this.citeCompliant} }), new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Polygon, { displayClass: 'olControlDrawFeaturePolygon', handlerOptions: {citeCompliant: this.citeCompliant} }) ]; this.addControls(controls); }, /** * Method: draw * calls the default draw, and then activates mouse defaults. * * Returns: * {DOMElement} */ draw: function() { var div = OpenLayers.Control.Panel.prototype.draw.apply(this, arguments); if (this.defaultControl === null) { this.defaultControl = this.controls[0]; } return div; }, CLASS_NAME: "OpenLayers.Control.EditingToolbar" }); /* ====================================================================== 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/Control/Pan.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/Button.js */ /** * Class: OpenLayers.Control.Pan * The Pan control is a single button to pan the map in one direction. For * a more complete control see <OpenLayers.Control.PanPanel>. * * Inherits from: * - <OpenLayers.Control> */ OpenLayers.Control.Pan = OpenLayers.Class(OpenLayers.Control.Button, { /** * APIProperty: slideFactor * {Integer} Number of pixels by which we'll pan the map in any direction * on clicking the arrow buttons, defaults to 50. If you want to pan * by some ratio of the map dimensions, use <slideRatio> instead. */ slideFactor: 50, /** * APIProperty: slideRatio * {Number} The fraction of map width/height by which we'll pan the map * on clicking the arrow buttons. Default is null. If set, will * override <slideFactor>. E.g. if slideRatio is .5, then Pan Up will * pan up half the map height. */ slideRatio: null, /** * Property: direction * {String} in {'North', 'South', 'East', 'West'} */ direction: null, /** * Constructor: OpenLayers.Control.Pan * Control which handles the panning (in any of the cardinal directions) * of the map by a set px distance. * * Parameters: * direction - {String} The direction this button should pan. * options - {Object} An optional object whose properties will be used * to extend the control. */ initialize: function(direction, options) { this.direction = direction; this.CLASS_NAME += this.direction; OpenLayers.Control.prototype.initialize.apply(this, [options]); }, /** * Method: trigger */ trigger: function(){ if (this.map) { var getSlideFactor = OpenLayers.Function.bind(function (dim) { return this.slideRatio ? this.map.getSize()[dim] * this.slideRatio : this.slideFactor; }, this); switch (this.direction) { case OpenLayers.Control.Pan.NORTH: this.map.pan(0, -getSlideFactor("h")); break; case OpenLayers.Control.Pan.SOUTH: this.map.pan(0, getSlideFactor("h")); break; case OpenLayers.Control.Pan.WEST: this.map.pan(-getSlideFactor("w"), 0); break; case OpenLayers.Control.Pan.EAST: this.map.pan(getSlideFactor("w"), 0); break; } } }, CLASS_NAME: "OpenLayers.Control.Pan" }); OpenLayers.Control.Pan.NORTH = "North"; OpenLayers.Control.Pan.SOUTH = "South"; OpenLayers.Control.Pan.EAST = "East"; OpenLayers.Control.Pan.WEST = "West"; /* ====================================================================== OpenLayers/Control/PanZoom.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.PanZoom * The PanZoom is a visible control, composed of a * <OpenLayers.Control.PanPanel> and a <OpenLayers.Control.ZoomPanel>. By * default it is drawn in the upper left corner of the map. * * Inherits from: * - <OpenLayers.Control> */ OpenLayers.Control.PanZoom = OpenLayers.Class(OpenLayers.Control, { /** * APIProperty: slideFactor * {Integer} Number of pixels by which we'll pan the map in any direction * on clicking the arrow buttons. If you want to pan by some ratio * of the map dimensions, use <slideRatio> instead. */ slideFactor: 50, /** * APIProperty: slideRatio * {Number} The fraction of map width/height by which we'll pan the map * on clicking the arrow buttons. Default is null. If set, will * override <slideFactor>. E.g. if slideRatio is .5, then the Pan Up * button will pan up half the map height. */ slideRatio: null, /** * Property: buttons * {Array(DOMElement)} Array of Button Divs */ buttons: null, /** * Property: position * {<OpenLayers.Pixel>} */ position: null, /** * Constructor: OpenLayers.Control.PanZoom * * Parameters: * options - {Object} */ initialize: function(options) { this.position = new OpenLayers.Pixel(OpenLayers.Control.PanZoom.X, OpenLayers.Control.PanZoom.Y); OpenLayers.Control.prototype.initialize.apply(this, arguments); }, /** * APIMethod: destroy */ destroy: function() { if (this.map) { this.map.events.unregister("buttonclick", this, this.onButtonClick); } this.removeButtons(); this.buttons = null; this.position = null; OpenLayers.Control.prototype.destroy.apply(this, arguments); }, /** * Method: setMap * * Properties: * map - {<OpenLayers.Map>} */ setMap: function(map) { OpenLayers.Control.prototype.setMap.apply(this, arguments); this.map.events.register("buttonclick", this, this.onButtonClick); }, /** * Method: draw * * Parameters: * px - {<OpenLayers.Pixel>} * * Returns: * {DOMElement} A reference to the container div for the PanZoom control. */ draw: function(px) { // initialize our internal div OpenLayers.Control.prototype.draw.apply(this, arguments); px = this.position; // place the controls this.buttons = []; var sz = {w: 18, h: 18}; var centered = new OpenLayers.Pixel(px.x+sz.w/2, px.y); this._addButton("panup", "north-mini.png", centered, sz); px.y = centered.y+sz.h; this._addButton("panleft", "west-mini.png", px, sz); this._addButton("panright", "east-mini.png", px.add(sz.w, 0), sz); this._addButton("pandown", "south-mini.png", centered.add(0, sz.h*2), sz); this._addButton("zoomin", "zoom-plus-mini.png", centered.add(0, sz.h*3+5), sz); this._addButton("zoomworld", "zoom-world-mini.png", centered.add(0, sz.h*4+5), sz); this._addButton("zoomout", "zoom-minus-mini.png", centered.add(0, sz.h*5+5), sz); return this.div; }, /** * Method: _addButton * * Parameters: * id - {String} * img - {String} * xy - {<OpenLayers.Pixel>} * sz - {<OpenLayers.Size>} * * Returns: * {DOMElement} A Div (an alphaImageDiv, to be precise) that contains the * image of the button, and has all the proper event handlers set. */ _addButton:function(id, img, xy, sz) { var imgLocation = OpenLayers.Util.getImageLocation(img); var btn = OpenLayers.Util.createAlphaImageDiv( this.id + "_" + id, xy, sz, imgLocation, "absolute"); btn.style.cursor = "pointer"; //we want to add the outer div this.div.appendChild(btn); btn.action = id; btn.className = "olButton"; //we want to remember/reference the outer div this.buttons.push(btn); return btn; }, /** * Method: _removeButton * * Parameters: * btn - {Object} */ _removeButton: function(btn) { this.div.removeChild(btn); OpenLayers.Util.removeItem(this.buttons, btn); }, /** * Method: removeButtons */ removeButtons: function() { for(var i=this.buttons.length-1; i>=0; --i) { this._removeButton(this.buttons[i]); } }, /** * Method: onButtonClick * * Parameters: * evt - {Event} */ onButtonClick: function(evt) { var btn = evt.buttonElement; switch (btn.action) { case "panup": this.map.pan(0, -this.getSlideFactor("h")); break; case "pandown": this.map.pan(0, this.getSlideFactor("h")); break; case "panleft": this.map.pan(-this.getSlideFactor("w"), 0); break; case "panright": this.map.pan(this.getSlideFactor("w"), 0); break; case "zoomin": this.map.zoomIn(); break; case "zoomout": this.map.zoomOut(); break; case "zoomworld": this.map.zoomToMaxExtent(); break; } }, /** * Method: getSlideFactor * * Parameters: * dim - {String} "w" or "h" (for width or height). * * Returns: * {Number} The slide factor for panning in the requested direction. */ getSlideFactor: function(dim) { return this.slideRatio ? this.map.getSize()[dim] * this.slideRatio : this.slideFactor; }, CLASS_NAME: "OpenLayers.Control.PanZoom" }); /** * Constant: X * {Integer} */ OpenLayers.Control.PanZoom.X = 4; /** * Constant: Y * {Integer} */ OpenLayers.Control.PanZoom.Y = 4; /* ====================================================================== OpenLayers/Control/WMTSGetFeatureInfo.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/Click.js * @requires OpenLayers/Handler/Hover.js * @requires OpenLayers/Request.js * @requires OpenLayers/Format/WMSGetFeatureInfo.js */ /** * Class: OpenLayers.Control.WMTSGetFeatureInfo * The WMTSGetFeatureInfo control uses a WMTS query to get information about a * point on the map. The information may be in a display-friendly format * such as HTML, or a machine-friendly format such as GML, depending on the * server's capabilities and the client's configuration. This control * handles click or hover events, attempts to parse the results using an * OpenLayers.Format, and fires a 'getfeatureinfo' event for each layer * queried. * * Inherits from: * - <OpenLayers.Control> */ OpenLayers.Control.WMTSGetFeatureInfo = OpenLayers.Class(OpenLayers.Control, { /** * APIProperty: hover * {Boolean} Send GetFeatureInfo requests when mouse stops moving. * Default is false. */ hover: false, /** * Property: requestEncoding * {String} One of "KVP" or "REST". Only KVP encoding is supported at this * time. */ requestEncoding: "KVP", /** * APIProperty: drillDown * {Boolean} Drill down over all WMTS layers in the map. When * using drillDown mode, hover is not possible. A getfeatureinfo event * will be fired for each layer queried. */ drillDown: false, /** * APIProperty: maxFeatures * {Integer} Maximum number of features to return from a WMTS query. This * sets the feature_count parameter on WMTS GetFeatureInfo * requests. */ maxFeatures: 10, /** APIProperty: clickCallback * {String} The click callback to register in the * {<OpenLayers.Handler.Click>} object created when the hover * option is set to false. Default is "click". */ clickCallback: "click", /** * Property: layers * {Array(<OpenLayers.Layer.WMTS>)} The layers to query for feature info. * If omitted, all map WMTS layers will be considered. */ layers: null, /** * APIProperty: queryVisible * {Boolean} Filter out hidden layers when searching the map for layers to * query. Default is true. */ queryVisible: true, /** * Property: infoFormat * {String} The mimetype to request from the server */ infoFormat: 'text/html', /** * Property: vendorParams * {Object} Additional parameters that will be added to the request, for * WMTS implementations that support them. This could e.g. look like * (start code) * { * radius: 5 * } * (end) */ vendorParams: {}, /** * Property: format * {<OpenLayers.Format>} A format for parsing GetFeatureInfo responses. * Default is <OpenLayers.Format.WMSGetFeatureInfo>. */ format: null, /** * Property: formatOptions * {Object} Optional properties to set on the format (if one is not provided * in the <format> property. */ formatOptions: null, /** * APIProperty: handlerOptions * {Object} Additional options for the handlers used by this control, e.g. * (start code) * { * "click": {delay: 100}, * "hover": {delay: 300} * } * (end) */ /** * Property: handler * {Object} Reference to the <OpenLayers.Handler> for this control */ handler: null, /** * Property: hoverRequest * {<OpenLayers.Request>} contains the currently running hover request * (if any). */ hoverRequest: 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>): * beforegetfeatureinfo - Triggered before each request is sent. * The event object has an *xy* property with the position of the * mouse click or hover event that triggers the request and a *layer* * property referencing the layer about to be queried. If a listener * returns false, the request will not be issued. * getfeatureinfo - Triggered when a GetFeatureInfo response is received. * The event object has a *text* property with the body of the * response (String), a *features* property with an array of the * parsed features, an *xy* property with the position of the mouse * click or hover event that triggered the request, a *layer* property * referencing the layer queried and a *request* property with the * request itself. If drillDown is set to true, one event will be fired * for each layer queried. * exception - Triggered when a GetFeatureInfo request fails (with a * status other than 200) or whenparsing fails. Listeners will receive * an event with *request*, *xy*, and *layer* properties. In the case * of a parsing error, the event will also contain an *error* property. */ /** * Property: pending * {Number} The number of pending requests. */ pending: 0, /** * Constructor: <OpenLayers.Control.WMTSGetFeatureInfo> * * Parameters: * options - {Object} */ initialize: function(options) { options = options || {}; options.handlerOptions = options.handlerOptions || {}; OpenLayers.Control.prototype.initialize.apply(this, [options]); if (!this.format) { this.format = new OpenLayers.Format.WMSGetFeatureInfo( options.formatOptions ); } if (this.drillDown === true) { this.hover = false; } if (this.hover) { this.handler = new OpenLayers.Handler.Hover( this, { move: this.cancelHover, pause: this.getInfoForHover }, OpenLayers.Util.extend( this.handlerOptions.hover || {}, {delay: 250} ) ); } else { var callbacks = {}; callbacks[this.clickCallback] = this.getInfoForClick; this.handler = new OpenLayers.Handler.Click( this, callbacks, this.handlerOptions.click || {} ); } }, /** * Method: getInfoForClick * Called on click * * Parameters: * evt - {<OpenLayers.Event>} */ getInfoForClick: function(evt) { this.request(evt.xy, {}); }, /** * Method: getInfoForHover * Pause callback for the hover handler * * Parameters: * evt - {Object} */ getInfoForHover: function(evt) { this.request(evt.xy, {hover: true}); }, /** * Method: cancelHover * Cancel callback for the hover handler */ cancelHover: function() { if (this.hoverRequest) { --this.pending; if (this.pending <= 0) { OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait"); this.pending = 0; } this.hoverRequest.abort(); this.hoverRequest = null; } }, /** * Method: findLayers * Internal method to get the layers, independent of whether we are * inspecting the map or using a client-provided array */ findLayers: function() { var candidates = this.layers || this.map.layers; var layers = []; var layer; for (var i=candidates.length-1; i>=0; --i) { layer = candidates[i]; if (layer instanceof OpenLayers.Layer.WMTS && layer.requestEncoding === this.requestEncoding && (!this.queryVisible || layer.getVisibility())) { layers.push(layer); if (!this.drillDown || this.hover) { break; } } } return layers; }, /** * Method: buildRequestOptions * Build an object with the relevant options for the GetFeatureInfo request. * * Parameters: * layer - {<OpenLayers.Layer.WMTS>} A WMTS layer. * xy - {<OpenLayers.Pixel>} The position on the map where the * mouse event occurred. */ buildRequestOptions: function(layer, xy) { var loc = this.map.getLonLatFromPixel(xy); var getTileUrl = layer.getURL( new OpenLayers.Bounds(loc.lon, loc.lat, loc.lon, loc.lat) ); var params = OpenLayers.Util.getParameters(getTileUrl); var tileInfo = layer.getTileInfo(loc); OpenLayers.Util.extend(params, { service: "WMTS", version: layer.version, request: "GetFeatureInfo", infoFormat: this.infoFormat, i: tileInfo.i, j: tileInfo.j }); OpenLayers.Util.applyDefaults(params, this.vendorParams); return { url: OpenLayers.Util.isArray(layer.url) ? layer.url[0] : layer.url, params: OpenLayers.Util.upperCaseObject(params), callback: function(request) { this.handleResponse(xy, request, layer); }, scope: this }; }, /** * Method: request * Sends a GetFeatureInfo request to the WMTS * * Parameters: * xy - {<OpenLayers.Pixel>} The position on the map where the mouse event * occurred. * options - {Object} additional options for this method. * * Valid options: * - *hover* {Boolean} true if we do the request for the hover handler */ request: function(xy, options) { options = options || {}; var layers = this.findLayers(); if (layers.length > 0) { var issue, layer; for (var i=0, len=layers.length; i<len; i++) { layer = layers[i]; issue = this.events.triggerEvent("beforegetfeatureinfo", { xy: xy, layer: layer }); if (issue !== false) { ++this.pending; var requestOptions = this.buildRequestOptions(layer, xy); var request = OpenLayers.Request.GET(requestOptions); if (options.hover === true) { this.hoverRequest = request; } } } if (this.pending > 0) { OpenLayers.Element.addClass(this.map.viewPortDiv, "olCursorWait"); } } }, /** * Method: handleResponse * Handler for the GetFeatureInfo response. * * Parameters: * xy - {<OpenLayers.Pixel>} The position on the map where the mouse event * occurred. * request - {XMLHttpRequest} The request object. * layer - {<OpenLayers.Layer.WMTS>} The queried layer. */ handleResponse: function(xy, request, layer) { --this.pending; if (this.pending <= 0) { OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait"); this.pending = 0; } if (request.status && (request.status < 200 || request.status >= 300)) { this.events.triggerEvent("exception", { xy: xy, request: request, layer: layer }); } else { var doc = request.responseXML; if (!doc || !doc.documentElement) { doc = request.responseText; } var features, except; try { features = this.format.read(doc); } catch (error) { except = true; this.events.triggerEvent("exception", { xy: xy, request: request, error: error, layer: layer }); } if (!except) { this.events.triggerEvent("getfeatureinfo", { text: request.responseText, features: features, request: request, xy: xy, layer: layer }); } } }, CLASS_NAME: "OpenLayers.Control.WMTSGetFeatureInfo" }); /* ====================================================================== OpenLayers/Control/Split.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/Path.js * @requires OpenLayers/Layer/Vector.js */ /** * Class: OpenLayers.Control.Split * Acts as a split feature agent while editing vector features. * * Inherits from: * - <OpenLayers.Control> */ OpenLayers.Control.Split = 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>): * beforesplit - Triggered before a split occurs. Listeners receive an * event object with *source* and *target* properties. * split - Triggered when a split occurs. Listeners receive an event with * an *original* property and a *features* property. The original * is a reference to the target feature that the sketch or modified * feature intersects. The features property is a list of all features * that result from this single split. This event is triggered before * the resulting features are added to the layer (while the layer still * has a reference to the original). * aftersplit - Triggered after all splits resulting from a single sketch * or feature modification have occurred. The original features * have been destroyed and features that result from the split * have already been added to the layer. Listeners receive an event * with a *source* and *features* property. The source references the * sketch or modified feature used as a splitter. The features * property is a list of all resulting features. */ /** * APIProperty: layer * {<OpenLayers.Layer.Vector>} The target layer with features to be split. * Set at construction or after construction with <setLayer>. */ layer: null, /** * Property: source * {<OpenLayers.Layer.Vector>} Optional source layer. Any newly created * or modified features from this layer will be used to split features * on the target layer. If not provided, a temporary sketch layer will * be created. */ source: null, /** * Property: sourceOptions * {Options} If a temporary sketch layer is created, these layer options * will be applied. */ sourceOptions: null, /** * APIProperty: tolerance * {Number} Distance between the calculated intersection and a vertex on * the source geometry below which the existing vertex will be used * for the split. Default is null. */ tolerance: null, /** * APIProperty: edge * {Boolean} Allow splits given intersection of edges only. Default is * true. If false, a vertex on the source must be within the * <tolerance> distance of the calculated intersection for a split * to occur. */ edge: true, /** * APIProperty: deferDelete * {Boolean} Instead of removing features from the layer, set feature * states of split features to DELETE. This assumes a save strategy * or other component is in charge of removing features from the * layer. Default is false. If false, split features will be * immediately deleted from the layer. */ deferDelete: false, /** * APIProperty: mutual * {Boolean} If source and target layers are the same, split source * features and target features where they intersect. Default is * true. If false, only target features will be split. */ mutual: true, /** * APIProperty: targetFilter * {<OpenLayers.Filter>} Optional filter that will be evaluated * to determine if a feature from the target layer is eligible for * splitting. */ targetFilter: null, /** * APIProperty: sourceFilter * {<OpenLayers.Filter>} Optional filter that will be evaluated * to determine if a feature from the source layer is eligible for * splitting. */ sourceFilter: null, /** * Property: handler * {<OpenLayers.Handler.Path>} The temporary sketch handler created if * no source layer is provided. */ handler: null, /** * Constructor: OpenLayers.Control.Split * Creates a new split control. A control is constructed with a target * layer and an optional source layer. While the control is active, * creating new features or modifying existing features on the source * layer will result in splitting any eligible features on the target * layer. If no source layer is provided, a temporary sketch layer will * be created to create lines for splitting features on the target. * * Parameters: * options - {Object} An object containing all configuration properties for * the control. * * Valid options: * layer - {<OpenLayers.Layer.Vector>} The target layer. Features from this * layer will be split by new or modified features on the source layer * or temporary sketch layer. * source - {<OpenLayers.Layer.Vector>} Optional source layer. If provided * newly created features or modified features will be used to split * features on the target layer. If not provided, a temporary sketch * layer will be created for drawing lines. * tolerance - {Number} Optional value for the distance between a source * vertex and the calculated intersection below which the split will * occur at the vertex. * edge - {Boolean} Allow splits given intersection of edges only. Default * is true. If false, a vertex on the source must be within the * <tolerance> distance of the calculated intersection for a split * to occur. * mutual - {Boolean} If source and target are the same, split source * features and target features where they intersect. Default is * true. If false, only target features will be split. * targetFilter - {<OpenLayers.Filter>} Optional filter that will be evaluated * to determine if a feature from the target layer is eligible for * splitting. * sourceFilter - {<OpenLayers.Filter>} Optional filter that will be evaluated * to determine if a feature from the target layer is eligible for * splitting. */ initialize: function(options) { OpenLayers.Control.prototype.initialize.apply(this, [options]); this.options = options || {}; // TODO: this could be done by the super // set the source layer if provided if(this.options.source) { this.setSource(this.options.source); } }, /** * APIMethod: setSource * Set the source layer for edits layer. * * Parameters: * layer - {<OpenLayers.Layer.Vector>} The new source layer layer. If * null, a temporary sketch layer will be created. */ setSource: function(layer) { if(this.active) { this.deactivate(); if(this.handler) { this.handler.destroy(); delete this.handler; } this.source = layer; this.activate(); } else { this.source = layer; } }, /** * APIMethod: activate * Activate the control. Activating the control registers listeners for * editing related events so that during feature creation and * modification, features in the target will be considered for * splitting. */ activate: function() { var activated = OpenLayers.Control.prototype.activate.call(this); if(activated) { if(!this.source) { if(!this.handler) { this.handler = new OpenLayers.Handler.Path(this, {done: function(geometry) { this.onSketchComplete({ feature: new OpenLayers.Feature.Vector(geometry) }); }}, {layerOptions: this.sourceOptions} ); } this.handler.activate(); } else if(this.source.events) { this.source.events.on({ sketchcomplete: this.onSketchComplete, afterfeaturemodified: this.afterFeatureModified, scope: this }); } } return activated; }, /** * APIMethod: deactivate * Deactivate the control. Deactivating the control unregisters listeners * so feature editing may proceed without engaging the split agent. */ deactivate: function() { var deactivated = OpenLayers.Control.prototype.deactivate.call(this); if(deactivated) { if(this.source && this.source.events) { this.source.events.un({ sketchcomplete: this.onSketchComplete, afterfeaturemodified: this.afterFeatureModified, scope: this }); } } return deactivated; }, /** * Method: onSketchComplete * Registered as a listener for the sketchcomplete event on the editable * layer. * * Parameters: * event - {Object} The sketch complete event. * * Returns: * {Boolean} Stop the sketch from being added to the layer (it has been * split). */ onSketchComplete: function(event) { this.feature = null; return !this.considerSplit(event.feature); }, /** * Method: afterFeatureModified * Registered as a listener for the afterfeaturemodified event on the * editable layer. * * Parameters: * event - {Object} The after feature modified event. */ afterFeatureModified: function(event) { if(event.modified) { var feature = event.feature; if (typeof feature.geometry.split === "function") { this.feature = event.feature; this.considerSplit(event.feature); } } }, /** * Method: removeByGeometry * Remove a feature from a list based on the given geometry. * * Parameters: * features - {Array(<OpenLayers.Feature.Vector>)} A list of features. * geometry - {<OpenLayers.Geometry>} A geometry. */ removeByGeometry: function(features, geometry) { for(var i=0, len=features.length; i<len; ++i) { if(features[i].geometry === geometry) { features.splice(i, 1); break; } } }, /** * Method: isEligible * Test if a target feature is eligible for splitting. * * Parameters: * target - {<OpenLayers.Feature.Vector>} The target feature. * * Returns: * {Boolean} The target is eligible for splitting. */ isEligible: function(target) { if (!target.geometry) { return false; } else { return ( target.state !== OpenLayers.State.DELETE ) && ( typeof target.geometry.split === "function" ) && ( this.feature !== target ) && ( !this.targetFilter || this.targetFilter.evaluate(target.attributes) ); } }, /** * Method: considerSplit * Decide whether or not to split target features with the supplied * feature. If <mutual> is true, both the source and target features * will be split if eligible. * * Parameters: * feature - {<OpenLayers.Feature.Vector>} The newly created or modified * feature. * * Returns: * {Boolean} The supplied feature was split (and destroyed). */ considerSplit: function(feature) { var sourceSplit = false; var targetSplit = false; if(!this.sourceFilter || this.sourceFilter.evaluate(feature.attributes)) { var features = this.layer && this.layer.features || []; var target, results, proceed; var additions = [], removals = []; var mutual = (this.layer === this.source) && this.mutual; var options = { edge: this.edge, tolerance: this.tolerance, mutual: mutual }; var sourceParts = [feature.geometry]; var targetFeature, targetParts; var source, parts; for(var i=0, len=features.length; i<len; ++i) { targetFeature = features[i]; if(this.isEligible(targetFeature)) { targetParts = [targetFeature.geometry]; // work through source geoms - this array may change for(var j=0; j<sourceParts.length; ++j) { source = sourceParts[j]; // work through target parts - this array may change for(var k=0; k<targetParts.length; ++k) { target = targetParts[k]; if(source.getBounds().intersectsBounds(target.getBounds())) { results = source.split(target, options); if(results) { proceed = this.events.triggerEvent( "beforesplit", {source: feature, target: targetFeature} ); if(proceed !== false) { if(mutual) { parts = results[0]; // handle parts that result from source splitting if(parts.length > 1) { // splice in new source parts parts.unshift(j, 1); // add args for splice below Array.prototype.splice.apply(sourceParts, parts); j += parts.length - 3; } results = results[1]; } // handle parts that result from target splitting if(results.length > 1) { // splice in new target parts results.unshift(k, 1); // add args for splice below Array.prototype.splice.apply(targetParts, results); k += results.length - 3; } } } } } } if(targetParts && targetParts.length > 1) { this.geomsToFeatures(targetFeature, targetParts); this.events.triggerEvent("split", { original: targetFeature, features: targetParts }); Array.prototype.push.apply(additions, targetParts); removals.push(targetFeature); targetSplit = true; } } } if(sourceParts && sourceParts.length > 1) { this.geomsToFeatures(feature, sourceParts); this.events.triggerEvent("split", { original: feature, features: sourceParts }); Array.prototype.push.apply(additions, sourceParts); removals.push(feature); sourceSplit = true; } if(sourceSplit || targetSplit) { // remove and add feature events are suppressed // listen for split event on this control instead if(this.deferDelete) { // Set state instead of removing. Take care to avoid // setting delete for features that have not yet been // inserted - those should be destroyed immediately. var feat, destroys = []; for(var i=0, len=removals.length; i<len; ++i) { feat = removals[i]; if(feat.state === OpenLayers.State.INSERT) { destroys.push(feat); } else { feat.state = OpenLayers.State.DELETE; this.layer.drawFeature(feat); } } this.layer.destroyFeatures(destroys, {silent: true}); for(var i=0, len=additions.length; i<len; ++i) { additions[i].state = OpenLayers.State.INSERT; } } else { this.layer.destroyFeatures(removals, {silent: true}); } this.layer.addFeatures(additions, {silent: true}); this.events.triggerEvent("aftersplit", { source: feature, features: additions }); } } return sourceSplit; }, /** * Method: geomsToFeatures * Create new features given a template feature and a list of geometries. * The list of geometries is modified in place. The result will be * a list of new features. * * Parameters: * feature - {<OpenLayers.Feature.Vector>} The feature to be cloned. * geoms - {Array(<OpenLayers.Geometry>)} List of goemetries. This will * become a list of new features. */ geomsToFeatures: function(feature, geoms) { var clone = feature.clone(); delete clone.geometry; var newFeature; for(var i=0, len=geoms.length; i<len; ++i) { // turn results list from geoms to features newFeature = clone.clone(); newFeature.geometry = geoms[i]; newFeature.state = OpenLayers.State.INSERT; geoms[i] = newFeature; } }, /** * Method: destroy * Clean up the control. */ destroy: function() { if(this.active) { this.deactivate(); // TODO: this should be handled by the super } OpenLayers.Control.prototype.destroy.call(this); }, CLASS_NAME: "OpenLayers.Control.Split" }); /* ====================================================================== OpenLayers/Control/SelectFeature.js ====================================================================== */ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for * full list of contributors). Published under the 2-clause BSD license. * See license.txt in the OpenLayers distribution or repository for the * full text of the license. */ /** * @requires OpenLayers/Control.js * @requires OpenLayers/Feature/Vector.js * @requires OpenLayers/Handler/Feature.js * @requires OpenLayers/Layer/Vector/RootContainer.js */ /** * Class: OpenLayers.Control.SelectFeature * The SelectFeature control selects vector features from a given layer on * click or hover. * * Inherits from: * - <OpenLayers.Control> */ OpenLayers.Control.SelectFeature = OpenLayers.Class(OpenLayers.Control, { /** * APIProperty: events * {<OpenLayers.Events>} Events instance for listeners and triggering * control specific events. * * Register a listener for a particular event with the following syntax: * (code) * control.events.register(type, obj, listener); * (end) * * Supported event types (in addition to those from <OpenLayers.Control.events>): * beforefeaturehighlighted - Triggered before a feature is highlighted * featurehighlighted - Triggered when a feature is highlighted * featureunhighlighted - Triggered when a feature is unhighlighted * boxselectionstart - Triggered before box selection starts * boxselectionend - Triggered after box selection ends */ /** * Property: multipleKey * {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets * the <multiple> property to true. Default is null. */ multipleKey: null, /** * Property: toggleKey * {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets * the <toggle> property to true. Default is null. */ toggleKey: null, /** * APIProperty: multiple * {Boolean} Allow selection of multiple geometries. Default is false. */ multiple: false, /** * APIProperty: clickout * {Boolean} Unselect features when clicking outside any feature. * Default is true. */ clickout: true, /** * APIProperty: toggle * {Boolean} Unselect a selected feature on click. Default is false. Only * has meaning if hover is false. */ toggle: false, /** * APIProperty: hover * {Boolean} Select on mouse over and deselect on mouse out. If true, this * ignores clicks and only listens to mouse moves. */ hover: false, /** * APIProperty: highlightOnly * {Boolean} If true do not actually select features (that is place them in * the layer's selected features array), just highlight them. This property * has no effect if hover is false. Defaults to false. */ highlightOnly: false, /** * APIProperty: box * {Boolean} Allow feature selection by drawing a box. */ box: false, /** * Property: onBeforeSelect * {Function} Optional function to be called before a feature is selected. * The function should expect to be called with a feature. */ onBeforeSelect: function() {}, /** * APIProperty: onSelect * {Function} Optional function to be called when a feature is selected. * The function should expect to be called with a feature. */ onSelect: function() {}, /** * APIProperty: onUnselect * {Function} Optional function to be called when a feature is unselected. * The function should expect to be called with a feature. */ onUnselect: function() {}, /** * Property: scope * {Object} The scope to use with the onBeforeSelect, onSelect, onUnselect * callbacks. If null the scope will be this control. */ scope: null, /** * APIProperty: geometryTypes * {Array(String)} To restrict selecting to a limited set of geometry types, * send a list of strings corresponding to the geometry class names. */ geometryTypes: null, /** * Property: layer * {<OpenLayers.Layer.Vector>} The vector layer with a common renderer * root for all layers this control is configured with (if an array of * layers was passed to the constructor), or the vector layer the control * was configured with (if a single layer was passed to the constructor). */ layer: null, /** * Property: layers * {Array(<OpenLayers.Layer.Vector>)} The layers this control will work on, * or null if the control was configured with a single layer */ layers: null, /** * APIProperty: callbacks * {Object} The functions that are sent to the handlers.feature for callback */ callbacks: null, /** * APIProperty: selectStyle * {Object} Hash of styles */ selectStyle: null, /** * Property: renderIntent * {String} key used to retrieve the select style from the layer's * style map. */ renderIntent: "select", /** * Property: handlers * {Object} Object with references to multiple <OpenLayers.Handler> * instances. */ handlers: null, /** * Constructor: OpenLayers.Control.SelectFeature * Create a new control for selecting features. * * Parameters: * layers - {<OpenLayers.Layer.Vector>}, or an array of vector layers. The * layer(s) this control will select features from. * options - {Object} */ initialize: function(layers, options) { OpenLayers.Control.prototype.initialize.apply(this, [options]); if(this.scope === null) { this.scope = this; } this.initLayer(layers); var callbacks = { click: this.clickFeature, clickout: this.clickoutFeature }; if (this.hover) { callbacks.over = this.overFeature; callbacks.out = this.outFeature; } this.callbacks = OpenLayers.Util.extend(callbacks, this.callbacks); this.handlers = { feature: new OpenLayers.Handler.Feature( this, this.layer, this.callbacks, {geometryTypes: this.geometryTypes} ) }; if (this.box) { this.handlers.box = new OpenLayers.Handler.Box( this, {done: this.selectBox}, {boxDivClassName: "olHandlerBoxSelectFeature"} ); } }, /** * Method: initLayer * Assign the layer property. If layers is an array, we need to use * a RootContainer. * * Parameters: * layers - {<OpenLayers.Layer.Vector>}, or an array of vector layers. */ initLayer: function(layers) { if(OpenLayers.Util.isArray(layers)) { this.layers = layers; this.layer = new OpenLayers.Layer.Vector.RootContainer( this.id + "_container", { layers: layers } ); } else { this.layer = layers; } }, /** * Method: destroy */ destroy: function() { if(this.active && this.layers) { this.map.removeLayer(this.layer); } OpenLayers.Control.prototype.destroy.apply(this, arguments); if(this.layers) { this.layer.destroy(); } }, /** * Method: activate * Activates the control. * * Returns: * {Boolean} The control was effectively activated. */ activate: function () { if (!this.active) { if(this.layers) { this.map.addLayer(this.layer); } this.handlers.feature.activate(); if(this.box && this.handlers.box) { this.handlers.box.activate(); } } return OpenLayers.Control.prototype.activate.apply( this, arguments ); }, /** * Method: deactivate * Deactivates the control. * * Returns: * {Boolean} The control was effectively deactivated. */ deactivate: function () { if (this.active) { this.handlers.feature.deactivate(); if(this.handlers.box) { this.handlers.box.deactivate(); } if(this.layers) { this.map.removeLayer(this.layer); } } return OpenLayers.Control.prototype.deactivate.apply( this, arguments ); }, /** * Method: unselectAll * Unselect all selected features. To unselect all except for a single * feature, set the options.except property to the feature. * * Parameters: * options - {Object} Optional configuration object. */ unselectAll: function(options) { // we'll want an option to supress notification here var layers = this.layers || [this.layer], layer, feature, l, numExcept; for(l=0; l<layers.length; ++l) { layer = layers[l]; numExcept = 0; //layer.selectedFeatures is null when layer is destroyed and //one of it's preremovelayer listener calls setLayer //with another layer on this control if(layer.selectedFeatures != null) { while(layer.selectedFeatures.length > numExcept) { feature = layer.selectedFeatures[numExcept]; if(!options || options.except != feature) { this.unselect(feature); } else { ++numExcept; } } } } }, /** * Method: clickFeature * Called on click in a feature * Only responds if this.hover is false. * * Parameters: * feature - {<OpenLayers.Feature.Vector>} */ clickFeature: function(feature) { if(!this.hover) { var selected = (OpenLayers.Util.indexOf( feature.layer.selectedFeatures, feature) > -1); if(selected) { if(this.toggleSelect()) { this.unselect(feature); } else if(!this.multipleSelect()) { this.unselectAll({except: feature}); } } else { if(!this.multipleSelect()) { this.unselectAll({except: feature}); } this.select(feature); } } }, /** * Method: multipleSelect * Allow for multiple selected features based on <multiple> property and * <multipleKey> event modifier. * * Returns: * {Boolean} Allow for multiple selected features. */ multipleSelect: function() { return this.multiple || (this.handlers.feature.evt && this.handlers.feature.evt[this.multipleKey]); }, /** * Method: toggleSelect * Event should toggle the selected state of a feature based on <toggle> * property and <toggleKey> event modifier. * * Returns: * {Boolean} Toggle the selected state of a feature. */ toggleSelect: function() { return this.toggle || (this.handlers.feature.evt && this.handlers.feature.evt[this.toggleKey]); }, /** * Method: clickoutFeature * Called on click outside a previously clicked (selected) feature. * Only responds if this.hover is false. * * Parameters: * feature - {<OpenLayers.Vector.Feature>} */ clickoutFeature: function(feature) { if(!this.hover && this.clickout) { this.unselectAll(); } }, /** * Method: overFeature * Called on over a feature. * Only responds if this.hover is true. * * Parameters: * feature - {<OpenLayers.Feature.Vector>} */ overFeature: function(feature) { var layer = feature.layer; if(this.hover) { if(this.highlightOnly) { this.highlight(feature); } else if(OpenLayers.Util.indexOf( layer.selectedFeatures, feature) == -1) { this.select(feature); } } }, /** * Method: outFeature * Called on out of a selected feature. * Only responds if this.hover is true. * * Parameters: * feature - {<OpenLayers.Feature.Vector>} */ outFeature: function(feature) { if(this.hover) { if(this.highlightOnly) { // we do nothing if we're not the last highlighter of the // feature if(feature._lastHighlighter == this.id) { // if another select control had highlighted the feature before // we did it ourself then we use that control to highlight the // feature as it was before we highlighted it, else we just // unhighlight it if(feature._prevHighlighter && feature._prevHighlighter != this.id) { delete feature._lastHighlighter; var control = this.map.getControl( feature._prevHighlighter); if(control) { control.highlight(feature); } } else { this.unhighlight(feature); } } } else { this.unselect(feature); } } }, /** * Method: highlight * Redraw feature with the select style. * * Parameters: * feature - {<OpenLayers.Feature.Vector>} */ highlight: function(feature) { var layer = feature.layer; var cont = this.events.triggerEvent("beforefeaturehighlighted", { feature : feature }); if(cont !== false) { feature._prevHighlighter = feature._lastHighlighter; feature._lastHighlighter = this.id; var style = this.selectStyle || this.renderIntent; layer.drawFeature(feature, style); this.events.triggerEvent("featurehighlighted", {feature : feature}); } }, /** * Method: unhighlight * Redraw feature with the "default" style * * Parameters: * feature - {<OpenLayers.Feature.Vector>} */ unhighlight: function(feature) { var layer = feature.layer; // three cases: // 1. there's no other highlighter, in that case _prev is undefined, // and we just need to undef _last // 2. another control highlighted the feature after we did it, in // that case _last references this other control, and we just // need to undef _prev // 3. another control highlighted the feature before we did it, in // that case _prev references this other control, and we need to // set _last to _prev and undef _prev if(feature._prevHighlighter == undefined) { delete feature._lastHighlighter; } else if(feature._prevHighlighter == this.id) { delete feature._prevHighlighter; } else { feature._lastHighlighter = feature._prevHighlighter; delete feature._prevHighlighter; } layer.drawFeature(feature, feature.style || feature.layer.style || "default"); this.events.triggerEvent("featureunhighlighted", {feature : feature}); }, /** * Method: select * Add feature to the layer's selectedFeature array, render the feature as * selected, and call the onSelect function. * * Parameters: * feature - {<OpenLayers.Feature.Vector>} */ select: function(feature) { var cont = this.onBeforeSelect.call(this.scope, feature); var layer = feature.layer; if(cont !== false) { cont = layer.events.triggerEvent("beforefeatureselected", { feature: feature }); if(cont !== false) { layer.selectedFeatures.push(feature); this.highlight(feature); // if the feature handler isn't involved in the feature // selection (because the box handler is used or the // feature is selected programatically) we fake the // feature handler to allow unselecting on click if(!this.handlers.feature.lastFeature) { this.handlers.feature.lastFeature = layer.selectedFeatures[0]; } layer.events.triggerEvent("featureselected", {feature: feature}); this.onSelect.call(this.scope, feature); } } }, /** * Method: unselect * Remove feature from the layer's selectedFeature array, render the feature as * normal, and call the onUnselect function. * * Parameters: * feature - {<OpenLayers.Feature.Vector>} */ unselect: function(feature) { var layer = feature.layer; // Store feature style for restoration later this.unhighlight(feature); OpenLayers.Util.removeItem(layer.selectedFeatures, feature); layer.events.triggerEvent("featureunselected", {feature: feature}); this.onUnselect.call(this.scope, feature); }, /** * Method: selectBox * Callback from the handlers.box set up when <box> selection is true * on. * * Parameters: * position - {<OpenLayers.Bounds> || <OpenLayers.Pixel> } */ selectBox: function(position) { if (position instanceof OpenLayers.Bounds) { var minXY = this.map.getLonLatFromPixel({ x: position.left, y: position.bottom }); var maxXY = this.map.getLonLatFromPixel({ x: position.right, y: position.top }); var bounds = new OpenLayers.Bounds( minXY.lon, minXY.lat, maxXY.lon, maxXY.lat ); // if multiple is false, first deselect currently selected features if (!this.multipleSelect()) { this.unselectAll(); } // because we're using a box, we consider we want multiple selection var prevMultiple = this.multiple; this.multiple = true; var layers = this.layers || [this.layer]; this.events.triggerEvent("boxselectionstart", {layers: layers}); var layer; for(var l=0; l<layers.length; ++l) { layer = layers[l]; for(var i=0, len = layer.features.length; i<len; ++i) { var feature = layer.features[i]; // check if the feature is displayed if (!feature.getVisibility()) { continue; } if (this.geometryTypes == null || OpenLayers.Util.indexOf( this.geometryTypes, feature.geometry.CLASS_NAME) > -1) { if (bounds.toGeometry().intersects(feature.geometry)) { if (OpenLayers.Util.indexOf(layer.selectedFeatures, feature) == -1) { this.select(feature); } } } } } this.multiple = prevMultiple; this.events.triggerEvent("boxselectionend", {layers: layers}); } }, /** * Method: setMap * Set the map property for the control. * * Parameters: * map - {<OpenLayers.Map>} */ setMap: function(map) { this.handlers.feature.setMap(map); if (this.box) { this.handlers.box.setMap(map); } OpenLayers.Control.prototype.setMap.apply(this, arguments); }, /** * APIMethod: setLayer * Attach a new layer to the control, overriding any existing layers. * * Parameters: * layers - Array of {<OpenLayers.Layer.Vector>} or a single * {<OpenLayers.Layer.Vector>} */ setLayer: function(layers) { var isActive = this.active; this.unselectAll(); this.deactivate(); if(this.layers) { this.layer.destroy(); this.layers = null; } this.initLayer(layers); this.handlers.feature.layer = this.layer; if (isActive) { this.activate(); } }, CLASS_NAME: "OpenLayers.Control.SelectFeature" }); /* ====================================================================== OpenLayers/Control/Attribution.js ====================================================================== */ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for * full list of contributors). Published under the 2-clause BSD license. * See license.txt in the OpenLayers distribution or repository for the * full text of the license. */ /** * @requires OpenLayers/Control.js */ /** * Class: OpenLayers.Control.Attribution * The attribution control adds attribution from layers to the map display. * It uses 'attribution' property of each layer. * * Inherits from: * - <OpenLayers.Control> */ OpenLayers.Control.Attribution = OpenLayers.Class(OpenLayers.Control, { /** * APIProperty: separator * {String} String used to separate layers. */ separator: ", ", /** * APIProperty: template * {String} Template for the attribution. This has to include the substring * "${layers}", which will be replaced by the layer specific * attributions, separated by <separator>. The default is "${layers}". */ template: "${layers}", /** * Constructor: OpenLayers.Control.Attribution * * Parameters: * options - {Object} Options for control. */ /** * Method: destroy * Destroy control. */ destroy: function() { this.map.events.un({ "removelayer": this.updateAttribution, "addlayer": this.updateAttribution, "changelayer": this.updateAttribution, "changebaselayer": this.updateAttribution, scope: this }); OpenLayers.Control.prototype.destroy.apply(this, arguments); }, /** * Method: draw * Initialize control. * * Returns: * {DOMElement} A reference to the DIV DOMElement containing the control */ draw: function() { OpenLayers.Control.prototype.draw.apply(this, arguments); this.map.events.on({ 'changebaselayer': this.updateAttribution, 'changelayer': this.updateAttribution, 'addlayer': this.updateAttribution, 'removelayer': this.updateAttribution, scope: this }); this.updateAttribution(); return this.div; }, /** * Method: updateAttribution * Update attribution string. */ updateAttribution: function() { var attributions = []; if (this.map && this.map.layers) { for(var i=0, len=this.map.layers.length; i<len; i++) { var layer = this.map.layers[i]; if (layer.attribution && layer.getVisibility()) { // add attribution only if attribution text is unique if (OpenLayers.Util.indexOf( attributions, layer.attribution) === -1) { attributions.push( layer.attribution ); } } } this.div.innerHTML = OpenLayers.String.format(this.template, { layers: attributions.join(this.separator) }); } }, CLASS_NAME: "OpenLayers.Control.Attribution" }); /* ====================================================================== OpenLayers/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/Control/PanPanel.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/Panel.js * @requires OpenLayers/Control/Pan.js */ /** * Class: OpenLayers.Control.PanPanel * The PanPanel is visible control for panning the map North, South, East or * West in small steps. By default it is drawn in the top left corner of the * map. * * Note: * If you wish to use this class with the default images and you want * it to look nice in ie6, you should add the following, conditionally * added css stylesheet to your HTML file: * * (code) * <!--[if lte IE 6]> * <link rel="stylesheet" href="../theme/default/ie6-style.css" type="text/css" /> * <![endif]--> * (end) * * Inherits from: * - <OpenLayers.Control.Panel> */ OpenLayers.Control.PanPanel = OpenLayers.Class(OpenLayers.Control.Panel, { /** * APIProperty: slideFactor * {Integer} Number of pixels by which we'll pan the map in any direction * on clicking the arrow buttons, defaults to 50. If you want to pan * by some ratio of the map dimensions, use <slideRatio> instead. */ slideFactor: 50, /** * APIProperty: slideRatio * {Number} The fraction of map width/height by which we'll pan the map * on clicking the arrow buttons. Default is null. If set, will * override <slideFactor>. E.g. if slideRatio is .5, then Pan Up will * pan up half the map height. */ slideRatio: null, /** * Constructor: OpenLayers.Control.PanPanel * Add the four directional pan buttons. * * Parameters: * options - {Object} An optional object whose properties will be used * to extend the control. */ initialize: function(options) { OpenLayers.Control.Panel.prototype.initialize.apply(this, [options]); var options = { slideFactor: this.slideFactor, slideRatio: this.slideRatio }; this.addControls([ new OpenLayers.Control.Pan(OpenLayers.Control.Pan.NORTH, options), new OpenLayers.Control.Pan(OpenLayers.Control.Pan.SOUTH, options), new OpenLayers.Control.Pan(OpenLayers.Control.Pan.EAST, options), new OpenLayers.Control.Pan(OpenLayers.Control.Pan.WEST, options) ]); }, CLASS_NAME: "OpenLayers.Control.PanPanel" }); /* ====================================================================== OpenLayers/Control/MousePosition.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.MousePosition * The MousePosition control displays geographic coordinates of the mouse * pointer, as it is moved about the map. * * You can use the <prefix>- or <suffix>-properties to provide more information * about the displayed coordinates to the user: * * (code) * var mousePositionCtrl = new OpenLayers.Control.MousePosition({ * prefix: '<a target="_blank" ' + * 'href="http://spatialreference.org/ref/epsg/4326/">' + * 'EPSG:4326</a> coordinates: ' * } * ); * (end code) * * Inherits from: * - <OpenLayers.Control> */ OpenLayers.Control.MousePosition = OpenLayers.Class(OpenLayers.Control, { /** * APIProperty: autoActivate * {Boolean} Activate the control when it is added to a map. Default is * true. */ autoActivate: true, /** * Property: element * {DOMElement} */ element: null, /** * APIProperty: prefix * {String} A string to be prepended to the current pointers coordinates * when it is rendered. Defaults to the empty string ''. */ prefix: '', /** * APIProperty: separator * {String} A string to be used to seperate the two coordinates from each * other. Defaults to the string ', ', which will result in a * rendered coordinate of e.g. '42.12, 21.22'. */ separator: ', ', /** * APIProperty: suffix * {String} A string to be appended to the current pointers coordinates * when it is rendered. Defaults to the empty string ''. */ suffix: '', /** * APIProperty: numDigits * {Integer} The number of digits each coordinate shall have when being * rendered, Defaults to 5. */ numDigits: 5, /** * APIProperty: granularity * {Integer} */ granularity: 10, /** * APIProperty: emptyString * {String} Set this to some value to set when the mouse is outside the * map. */ emptyString: null, /** * Property: lastXy * {<OpenLayers.Pixel>} */ lastXy: null, /** * APIProperty: displayProjection * {<OpenLayers.Projection>} The projection in which the mouse position is * displayed. */ displayProjection: null, /** * Constructor: OpenLayers.Control.MousePosition * * Parameters: * options - {Object} Options for control. */ /** * Method: destroy */ destroy: function() { this.deactivate(); OpenLayers.Control.prototype.destroy.apply(this, arguments); }, /** * APIMethod: activate */ activate: function() { if (OpenLayers.Control.prototype.activate.apply(this, arguments)) { this.map.events.register('mousemove', this, this.redraw); this.map.events.register('mouseout', this, this.reset); this.redraw(); return true; } else { return false; } }, /** * APIMethod: deactivate */ deactivate: function() { if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) { this.map.events.unregister('mousemove', this, this.redraw); this.map.events.unregister('mouseout', this, this.reset); this.element.innerHTML = ""; return true; } else { return false; } }, /** * Method: draw * {DOMElement} */ draw: function() { OpenLayers.Control.prototype.draw.apply(this, arguments); if (!this.element) { this.div.left = ""; this.div.top = ""; this.element = this.div; } return this.div; }, /** * Method: redraw */ redraw: function(evt) { var lonLat; if (evt == null) { this.reset(); return; } else { if (this.lastXy == null || Math.abs(evt.xy.x - this.lastXy.x) > this.granularity || Math.abs(evt.xy.y - this.lastXy.y) > this.granularity) { this.lastXy = evt.xy; return; } lonLat = this.map.getLonLatFromPixel(evt.xy); if (!lonLat) { // map has not yet been properly initialized return; } if (this.displayProjection) { lonLat.transform(this.map.getProjectionObject(), this.displayProjection ); } this.lastXy = evt.xy; } var newHtml = this.formatOutput(lonLat); if (newHtml != this.element.innerHTML) { this.element.innerHTML = newHtml; } }, /** * Method: reset */ reset: function(evt) { if (this.emptyString != null) { this.element.innerHTML = this.emptyString; } }, /** * Method: formatOutput * Override to provide custom display output * * Parameters: * lonLat - {<OpenLayers.LonLat>} Location to display */ formatOutput: function(lonLat) { var digits = parseInt(this.numDigits); var newHtml = this.prefix + lonLat.lon.toFixed(digits) + this.separator + lonLat.lat.toFixed(digits) + this.suffix; return newHtml; }, CLASS_NAME: "OpenLayers.Control.MousePosition" }); /* ====================================================================== OpenLayers/Control/CacheWrite.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/Request.js * @requires OpenLayers/Console.js */ /** * Class: OpenLayers.Control.CacheWrite * A control for caching image tiles in the browser's local storage. The * <OpenLayers.Control.CacheRead> control is used to fetch and use the cached * tile images. * * Note: Before using this control on any layer that is not your own, make sure * that the terms of service of the tile provider allow local storage of tiles. * * Inherits from: * - <OpenLayers.Control> */ OpenLayers.Control.CacheWrite = OpenLayers.Class(OpenLayers.Control, { /** * APIProperty: events * {<OpenLayers.Events>} Events instance for listeners and triggering * control specific events. * * To register events in the constructor, configure <eventListeners>. * * 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>): * cachefull - Triggered when the cache is full. Listeners receive an * object with a tile property as first argument. The tile references * the tile that couldn't be cached. */ /** * APIProperty: eventListeners * {Object} Object with event listeners, keyed by event name. An optional * scope property defines the scope that listeners will be executed in. */ /** * APIProperty: layers * {Array(<OpenLayers.Layer.Grid>)}. Optional. If provided, caching * will be enabled for these layers only, otherwise for all cacheable * layers. */ layers: null, /** * APIProperty: imageFormat * {String} The image format used for caching. The default is "image/png". * Supported formats depend on the user agent. If an unsupported * <imageFormat> is provided, "image/png" will be used. For aerial * imagery, "image/jpeg" is recommended. */ imageFormat: "image/png", /** * Property: quotaRegEx * {RegExp} */ quotaRegEx: (/quota/i), /** * Constructor: OpenLayers.Control.CacheWrite * * Parameters: * options - {Object} Object with API properties for this control. */ /** * Method: setMap * Set the map property for the control. * * Parameters: * map - {<OpenLayers.Map>} */ setMap: function(map) { OpenLayers.Control.prototype.setMap.apply(this, arguments); var i, layers = this.layers || map.layers; for (i=layers.length-1; i>=0; --i) { this.addLayer({layer: layers[i]}); } if (!this.layers) { map.events.on({ addlayer: this.addLayer, removeLayer: this.removeLayer, scope: this }); } }, /** * Method: addLayer * Adds a layer to the control. Once added, tiles requested for this layer * will be cached. * * Parameters: * evt - {Object} Object with a layer property referencing an * <OpenLayers.Layer> instance */ addLayer: function(evt) { evt.layer.events.on({ tileloadstart: this.makeSameOrigin, tileloaded: this.onTileLoaded, scope: this }); }, /** * Method: removeLayer * Removes a layer from the control. Once removed, tiles requested for this * layer will no longer be cached. * * Parameters: * evt - {Object} Object with a layer property referencing an * <OpenLayers.Layer> instance */ removeLayer: function(evt) { evt.layer.events.un({ tileloadstart: this.makeSameOrigin, tileloaded: this.onTileLoaded, scope: this }); }, /** * Method: makeSameOrigin * If the tile does not have CORS image loading enabled and is from a * different origin, use OpenLayers.ProxyHost to make it a same origin url. * * Parameters: * evt - {<OpenLayers.Event>} */ makeSameOrigin: function(evt) { if (this.active) { var tile = evt.tile; if (tile instanceof OpenLayers.Tile.Image && !tile.crossOriginKeyword && tile.url.substr(0, 5) !== "data:") { var sameOriginUrl = OpenLayers.Request.makeSameOrigin( tile.url, OpenLayers.ProxyHost ); OpenLayers.Control.CacheWrite.urlMap[sameOriginUrl] = tile.url; tile.url = sameOriginUrl; } } }, /** * Method: onTileLoaded * Decides whether a tile can be cached and calls the cache method. * * Parameters: * evt - {Event} */ onTileLoaded: function(evt) { if (this.active && !evt.aborted && evt.tile instanceof OpenLayers.Tile.Image && evt.tile.url.substr(0, 5) !== 'data:') { this.cache({tile: evt.tile}); delete OpenLayers.Control.CacheWrite.urlMap[evt.tile.url]; } }, /** * Method: cache * Adds a tile to the cache. When the cache is full, the "cachefull" event * is triggered. * * Parameters: * obj - {Object} Object with a tile property, tile being the * <OpenLayers.Tile.Image> with the data to add to the cache */ cache: function(obj) { if (window.localStorage) { var tile = obj.tile; try { var canvasContext = tile.getCanvasContext(); if (canvasContext) { var urlMap = OpenLayers.Control.CacheWrite.urlMap; var url = urlMap[tile.url] || tile.url; window.localStorage.setItem( "olCache_" + url, canvasContext.canvas.toDataURL(this.imageFormat) ); } } catch(e) { // local storage full or CORS violation var reason = e.name || e.message; if (reason && this.quotaRegEx.test(reason)) { this.events.triggerEvent("cachefull", {tile: tile}); } else { OpenLayers.Console.error(e.toString()); } } } }, /** * 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.layers || this.map) { var i, layers = this.layers || this.map.layers; for (i=layers.length-1; i>=0; --i) { this.removeLayer({layer: layers[i]}); } } if (this.map) { this.map.events.un({ addlayer: this.addLayer, removeLayer: this.removeLayer, scope: this }); } OpenLayers.Control.prototype.destroy.apply(this, arguments); }, CLASS_NAME: "OpenLayers.Control.CacheWrite" }); /** * APIFunction: OpenLayers.Control.CacheWrite.clearCache * Clears all tiles cached with <OpenLayers.Control.CacheWrite> from the cache. */ OpenLayers.Control.CacheWrite.clearCache = function() { if (!window.localStorage) { return; } var i, key; for (i=window.localStorage.length-1; i>=0; --i) { key = window.localStorage.key(i); if (key.substr(0, 8) === "olCache_") { window.localStorage.removeItem(key); } } }; /** * Property: OpenLayers.Control.CacheWrite.urlMap * {Object} Mapping of same origin urls to cache url keys. Entries will be * deleted as soon as a tile was cached. */ OpenLayers.Control.CacheWrite.urlMap = {}; /* ====================================================================== OpenLayers/Control/NavigationHistory.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/Control/Button.js */ /** * Class: OpenLayers.Control.NavigationHistory * A navigation history control. This is a meta-control, that creates two * dependent controls: <previous> and <next>. Call the trigger method * on the <previous> and <next> controls to restore previous and next * history states. The previous and next controls will become active * when there are available states to restore and will become deactive * when there are no states to restore. * * Inherits from: * - <OpenLayers.Control> */ OpenLayers.Control.NavigationHistory = OpenLayers.Class(OpenLayers.Control, { /** * Property: type * {String} Note that this control is not intended to be added directly * to a control panel. Instead, add the sub-controls previous and * next. These sub-controls are button type controls that activate * and deactivate themselves. If this parent control is added to * a panel, it will act as a toggle. */ type: OpenLayers.Control.TYPE_TOGGLE, /** * APIProperty: previous * {<OpenLayers.Control>} A button type control whose trigger method restores * the previous state managed by this control. */ previous: null, /** * APIProperty: previousOptions * {Object} Set this property on the options argument of the constructor * to set optional properties on the <previous> control. */ previousOptions: null, /** * APIProperty: next * {<OpenLayers.Control>} A button type control whose trigger method restores * the next state managed by this control. */ next: null, /** * APIProperty: nextOptions * {Object} Set this property on the options argument of the constructor * to set optional properties on the <next> control. */ nextOptions: null, /** * APIProperty: limit * {Integer} Optional limit on the number of history items to retain. If * null, there is no limit. Default is 50. */ limit: 50, /** * APIProperty: autoActivate * {Boolean} Activate the control when it is added to a map. Default is * true. */ autoActivate: true, /** * Property: clearOnDeactivate * {Boolean} Clear the history when the control is deactivated. Default * is false. */ clearOnDeactivate: false, /** * Property: registry * {Object} An object with keys corresponding to event types. Values * are functions that return an object representing the current state. */ registry: null, /** * Property: nextStack * {Array} Array of items in the history. */ nextStack: null, /** * Property: previousStack * {Array} List of items in the history. First item represents the current * state. */ previousStack: null, /** * Property: listeners * {Object} An object containing properties corresponding to event types. * This object is used to configure the control and is modified on * construction. */ listeners: null, /** * Property: restoring * {Boolean} Currently restoring a history state. This is set to true * before calling restore and set to false after restore returns. */ restoring: false, /** * Constructor: OpenLayers.Control.NavigationHistory * * Parameters: * options - {Object} An optional object whose properties will be used * to extend the control. */ initialize: function(options) { OpenLayers.Control.prototype.initialize.apply(this, [options]); this.registry = OpenLayers.Util.extend({ "moveend": this.getState }, this.registry); var previousOptions = { trigger: OpenLayers.Function.bind(this.previousTrigger, this), displayClass: this.displayClass + " " + this.displayClass + "Previous" }; OpenLayers.Util.extend(previousOptions, this.previousOptions); this.previous = new OpenLayers.Control.Button(previousOptions); var nextOptions = { trigger: OpenLayers.Function.bind(this.nextTrigger, this), displayClass: this.displayClass + " " + this.displayClass + "Next" }; OpenLayers.Util.extend(nextOptions, this.nextOptions); this.next = new OpenLayers.Control.Button(nextOptions); this.clear(); }, /** * Method: onPreviousChange * Called when the previous history stack changes. * * Parameters: * state - {Object} An object representing the state to be restored * if previous is triggered again or null if no previous states remain. * length - {Integer} The number of remaining previous states that can * be restored. */ onPreviousChange: function(state, length) { if(state && !this.previous.active) { this.previous.activate(); } else if(!state && this.previous.active) { this.previous.deactivate(); } }, /** * Method: onNextChange * Called when the next history stack changes. * * Parameters: * state - {Object} An object representing the state to be restored * if next is triggered again or null if no next states remain. * length - {Integer} The number of remaining next states that can * be restored. */ onNextChange: function(state, length) { if(state && !this.next.active) { this.next.activate(); } else if(!state && this.next.active) { this.next.deactivate(); } }, /** * APIMethod: destroy * Destroy the control. */ destroy: function() { OpenLayers.Control.prototype.destroy.apply(this); this.previous.destroy(); this.next.destroy(); this.deactivate(); for(var prop in this) { this[prop] = null; } }, /** * Method: setMap * Set the map property for the control and <previous> and <next> child * controls. * * Parameters: * map - {<OpenLayers.Map>} */ setMap: function(map) { this.map = map; this.next.setMap(map); this.previous.setMap(map); }, /** * Method: draw * Called when the control is added to the map. */ draw: function() { OpenLayers.Control.prototype.draw.apply(this, arguments); this.next.draw(); this.previous.draw(); }, /** * Method: previousTrigger * Restore the previous state. If no items are in the previous history * stack, this has no effect. * * Returns: * {Object} Item representing state that was restored. Undefined if no * items are in the previous history stack. */ previousTrigger: function() { var current = this.previousStack.shift(); var state = this.previousStack.shift(); if(state != undefined) { this.nextStack.unshift(current); this.previousStack.unshift(state); this.restoring = true; this.restore(state); this.restoring = false; this.onNextChange(this.nextStack[0], this.nextStack.length); this.onPreviousChange( this.previousStack[1], this.previousStack.length - 1 ); } else { this.previousStack.unshift(current); } return state; }, /** * APIMethod: nextTrigger * Restore the next state. If no items are in the next history * stack, this has no effect. The next history stack is populated * as states are restored from the previous history stack. * * Returns: * {Object} Item representing state that was restored. Undefined if no * items are in the next history stack. */ nextTrigger: function() { var state = this.nextStack.shift(); if(state != undefined) { this.previousStack.unshift(state); this.restoring = true; this.restore(state); this.restoring = false; this.onNextChange(this.nextStack[0], this.nextStack.length); this.onPreviousChange( this.previousStack[1], this.previousStack.length - 1 ); } return state; }, /** * APIMethod: clear * Clear history. */ clear: function() { this.previousStack = []; this.previous.deactivate(); this.nextStack = []; this.next.deactivate(); }, /** * Method: getState * Get the current state and return it. * * Returns: * {Object} An object representing the current state. */ getState: function() { return { center: this.map.getCenter(), resolution: this.map.getResolution(), projection: this.map.getProjectionObject(), units: this.map.getProjectionObject().getUnits() || this.map.units || this.map.baseLayer.units }; }, /** * Method: restore * Update the state with the given object. * * Parameters: * state - {Object} An object representing the state to restore. */ restore: function(state) { var center, zoom; if (this.map.getProjectionObject() == state.projection) { zoom = this.map.getZoomForResolution(state.resolution); center = state.center; } else { center = state.center.clone(); center.transform(state.projection, this.map.getProjectionObject()); var sourceUnits = state.units; var targetUnits = this.map.getProjectionObject().getUnits() || this.map.units || this.map.baseLayer.units; var resolutionFactor = sourceUnits && targetUnits ? OpenLayers.INCHES_PER_UNIT[sourceUnits] / OpenLayers.INCHES_PER_UNIT[targetUnits] : 1; zoom = this.map.getZoomForResolution(resolutionFactor*state.resolution); } this.map.setCenter(center, zoom); }, /** * Method: setListeners * Sets functions to be registered in the listeners object. */ setListeners: function() { this.listeners = {}; for(var type in this.registry) { this.listeners[type] = OpenLayers.Function.bind(function() { if(!this.restoring) { var state = this.registry[type].apply(this, arguments); this.previousStack.unshift(state); if(this.previousStack.length > 1) { this.onPreviousChange( this.previousStack[1], this.previousStack.length - 1 ); } if(this.previousStack.length > (this.limit + 1)) { this.previousStack.pop(); } if(this.nextStack.length > 0) { this.nextStack = []; this.onNextChange(null, 0); } } return true; }, this); } }, /** * APIMethod: activate * Activate the control. This registers any listeners. * * Returns: * {Boolean} Control successfully activated. */ activate: function() { var activated = false; if(this.map) { if(OpenLayers.Control.prototype.activate.apply(this)) { if(this.listeners == null) { this.setListeners(); } for(var type in this.listeners) { this.map.events.register(type, this, this.listeners[type]); } activated = true; if(this.previousStack.length == 0) { this.initStack(); } } } return activated; }, /** * Method: initStack * Called after the control is activated if the previous history stack is * empty. */ initStack: function() { if(this.map.getCenter()) { this.listeners.moveend(); } }, /** * APIMethod: deactivate * Deactivate the control. This unregisters any listeners. * * Returns: * {Boolean} Control successfully deactivated. */ deactivate: function() { var deactivated = false; if(this.map) { if(OpenLayers.Control.prototype.deactivate.apply(this)) { for(var type in this.listeners) { this.map.events.unregister( type, this, this.listeners[type] ); } if(this.clearOnDeactivate) { this.clear(); } deactivated = true; } } return deactivated; }, CLASS_NAME: "OpenLayers.Control.NavigationHistory" }); /* ====================================================================== OpenLayers/Control/TransformFeature.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/Control/DragFeature.js * @requires OpenLayers/Feature/Vector.js * @requires OpenLayers/Geometry/LineString.js * @requires OpenLayers/Geometry/Point.js */ /** * Class: OpenLayers.Control.TransformFeature * Control to transform features with a standard transformation box. * * Inherits From: * - <OpenLayers.Control> */ OpenLayers.Control.TransformFeature = 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>): * beforesetfeature - Triggered before a feature is set for * tranformation. The feature will not be set if a listener returns * false. Listeners receive a *feature* property, with the feature * that will be set for transformation. Listeners are allowed to * set the control's *scale*, *ratio* and *rotation* properties, * which will set the initial scale, ratio and rotation of the * feature, like the <setFeature> method's initialParams argument. * setfeature - Triggered when a feature is set for tranformation. * Listeners receive a *feature* property, with the feature that * is now set for transformation. * beforetransform - Triggered while dragging, before a feature is * transformed. The feature will not be transformed if a listener * returns false (but the box still will). Listeners receive one or * more of *center*, *scale*, *ratio* and *rotation*. The *center* * property is an <OpenLayers.Geometry.Point> object with the new * center of the transformed feature, the others are Floats with the * scale, ratio or rotation change since the last transformation. * transform - Triggered while dragging, when a feature is transformed. * Listeners receive an event object with one or more of *center*, * scale*, *ratio* and *rotation*. The *center* property is an * <OpenLayers.Geometry.Point> object with the new center of the * transformed feature, the others are Floats with the scale, ratio * or rotation change of the feature since the last transformation. * transformcomplete - Triggered after dragging. Listeners receive * an event object with the transformed *feature*. */ /** * APIProperty: geometryTypes * {Array(String)} To restrict transformation to a limited set of geometry * types, send a list of strings corresponding to the geometry class * names. */ geometryTypes: null, /** * Property: layer * {<OpenLayers.Layer.Vector>} */ layer: null, /** * APIProperty: preserveAspectRatio * {Boolean} set to true to not change the feature's aspect ratio. */ preserveAspectRatio: false, /** * APIProperty: rotate * {Boolean} set to false if rotation should be disabled. Default is true. * To be passed with the constructor or set when the control is not * active. */ rotate: true, /** * APIProperty: feature * {<OpenLayers.Feature.Vector>} Feature currently available for * transformation. Read-only, use <setFeature> to set it manually. */ feature: null, /** * APIProperty: renderIntent * {String|Object} Render intent for the transformation box and * handles. A symbolizer object can also be provided here. */ renderIntent: "temporary", /** * APIProperty: rotationHandleSymbolizer * {Object|String} Optional. A custom symbolizer for the rotation handles. * A render intent can also be provided here. Defaults to * (code) * { * stroke: false, * pointRadius: 10, * fillOpacity: 0, * cursor: "pointer" * } * (end) */ rotationHandleSymbolizer: null, /** * APIProperty: box * {<OpenLayers.Feature.Vector>} The transformation box rectangle. * Read-only. */ box: null, /** * APIProperty: center * {<OpenLayers.Geometry.Point>} The center of the feature bounds. * Read-only. */ center: null, /** * APIProperty: scale * {Float} The scale of the feature, relative to the scale the time the * feature was set. Read-only, except for *beforesetfeature* * listeners. */ scale: 1, /** * APIProperty: ratio * {Float} The ratio of the feature relative to the ratio the time the * feature was set. Read-only, except for *beforesetfeature* * listeners. */ ratio: 1, /** * Property: rotation * {Integer} the current rotation angle of the box. Read-only, except for * *beforesetfeature* listeners. */ rotation: 0, /** * APIProperty: handles * {Array(<OpenLayers.Feature.Vector>)} The 8 handles currently available * for scaling/resizing. Numbered counterclockwise, starting from the * southwest corner. Read-only. */ handles: null, /** * APIProperty: rotationHandles * {Array(<OpenLayers.Feature.Vector>)} The 4 rotation handles currently * available for rotating. Numbered counterclockwise, starting from * the southwest corner. Read-only. */ rotationHandles: null, /** * Property: dragControl * {<OpenLayers.Control.DragFeature>} */ dragControl: null, /** * APIProperty: irregular * {Boolean} Make scaling/resizing work irregularly. If true then * dragging a handle causes the feature to resize in the direction * of movement. If false then the feature resizes symetrically * about it's center. */ irregular: false, /** * Constructor: OpenLayers.Control.TransformFeature * Create a new transform feature control. * * Parameters: * layer - {<OpenLayers.Layer.Vector>} Layer that contains features that * will be transformed. * options - {Object} Optional object whose properties will be set on the * control. */ initialize: function(layer, options) { OpenLayers.Control.prototype.initialize.apply(this, [options]); this.layer = layer; if(!this.rotationHandleSymbolizer) { this.rotationHandleSymbolizer = { stroke: false, pointRadius: 10, fillOpacity: 0, cursor: "pointer" }; } this.createBox(); this.createControl(); }, /** * APIMethod: activate * Activates the control. */ activate: function() { var activated = false; if(OpenLayers.Control.prototype.activate.apply(this, arguments)) { this.dragControl.activate(); this.layer.addFeatures([this.box]); this.rotate && this.layer.addFeatures(this.rotationHandles); this.layer.addFeatures(this.handles); activated = true; } return activated; }, /** * APIMethod: deactivate * Deactivates the control. */ deactivate: function() { var deactivated = false; if(OpenLayers.Control.prototype.deactivate.apply(this, arguments)) { this.layer.removeFeatures(this.handles); this.rotate && this.layer.removeFeatures(this.rotationHandles); this.layer.removeFeatures([this.box]); this.dragControl.deactivate(); deactivated = true; } return deactivated; }, /** * Method: setMap * * Parameters: * map - {<OpenLayers.Map>} */ setMap: function(map) { this.dragControl.setMap(map); OpenLayers.Control.prototype.setMap.apply(this, arguments); }, /** * APIMethod: setFeature * Place the transformation box on a feature and start transforming it. * If the control is not active, it will be activated. * * Parameters: * feature - {<OpenLayers.Feature.Vector>} * initialParams - {Object} Initial values for rotation, scale or ratio. * Setting a rotation value here will cause the transformation box to * start rotated. Setting a scale or ratio will not affect the * transormation box, but applications may use this to keep track of * scale and ratio of a feature across multiple transforms. */ setFeature: function(feature, initialParams) { initialParams = OpenLayers.Util.applyDefaults(initialParams, { rotation: 0, scale: 1, ratio: 1 }); var oldRotation = this.rotation; var oldCenter = this.center; OpenLayers.Util.extend(this, initialParams); var cont = this.events.triggerEvent("beforesetfeature", {feature: feature} ); if (cont === false) { return; } this.feature = feature; this.activate(); this._setfeature = true; var featureBounds = this.feature.geometry.getBounds(); this.box.move(featureBounds.getCenterLonLat()); this.box.geometry.rotate(-oldRotation, oldCenter); this._angle = 0; var ll; if(this.rotation) { var geom = feature.geometry.clone(); geom.rotate(-this.rotation, this.center); var box = new OpenLayers.Feature.Vector( geom.getBounds().toGeometry()); box.geometry.rotate(this.rotation, this.center); this.box.geometry.rotate(this.rotation, this.center); this.box.move(box.geometry.getBounds().getCenterLonLat()); var llGeom = box.geometry.components[0].components[0]; ll = llGeom.getBounds().getCenterLonLat(); } else { ll = new OpenLayers.LonLat(featureBounds.left, featureBounds.bottom); } this.handles[0].move(ll); delete this._setfeature; this.events.triggerEvent("setfeature", {feature: feature}); }, /** * APIMethod: unsetFeature * Remove the transformation box off any feature. * If the control is active, it will be deactivated first. */ unsetFeature: function() { if (this.active) { this.deactivate(); } else { this.feature = null; this.rotation = 0; this.scale = 1; this.ratio = 1; } }, /** * Method: createBox * Creates the box with all handles and transformation handles. */ createBox: function() { var control = this; this.center = new OpenLayers.Geometry.Point(0, 0); this.box = new OpenLayers.Feature.Vector( new OpenLayers.Geometry.LineString([ new OpenLayers.Geometry.Point(-1, -1), new OpenLayers.Geometry.Point(0, -1), new OpenLayers.Geometry.Point(1, -1), new OpenLayers.Geometry.Point(1, 0), new OpenLayers.Geometry.Point(1, 1), new OpenLayers.Geometry.Point(0, 1), new OpenLayers.Geometry.Point(-1, 1), new OpenLayers.Geometry.Point(-1, 0), new OpenLayers.Geometry.Point(-1, -1) ]), null, typeof this.renderIntent == "string" ? null : this.renderIntent ); // Override for box move - make sure that the center gets updated this.box.geometry.move = function(x, y) { control._moving = true; OpenLayers.Geometry.LineString.prototype.move.apply(this, arguments); control.center.move(x, y); delete control._moving; }; // Overrides for vertex move, resize and rotate - make sure that // handle and rotationHandle geometries are also moved, resized and // rotated. var vertexMoveFn = function(x, y) { OpenLayers.Geometry.Point.prototype.move.apply(this, arguments); this._rotationHandle && this._rotationHandle.geometry.move(x, y); this._handle.geometry.move(x, y); }; var vertexResizeFn = function(scale, center, ratio) { OpenLayers.Geometry.Point.prototype.resize.apply(this, arguments); this._rotationHandle && this._rotationHandle.geometry.resize( scale, center, ratio); this._handle.geometry.resize(scale, center, ratio); }; var vertexRotateFn = function(angle, center) { OpenLayers.Geometry.Point.prototype.rotate.apply(this, arguments); this._rotationHandle && this._rotationHandle.geometry.rotate( angle, center); this._handle.geometry.rotate(angle, center); }; // Override for handle move - make sure that the box and other handles // are updated, and finally transform the feature. var handleMoveFn = function(x, y) { var oldX = this.x, oldY = this.y; OpenLayers.Geometry.Point.prototype.move.call(this, x, y); if(control._moving) { return; } var evt = control.dragControl.handlers.drag.evt; var preserveAspectRatio = !control._setfeature && control.preserveAspectRatio; var reshape = !preserveAspectRatio && !(evt && evt.shiftKey); var oldGeom = new OpenLayers.Geometry.Point(oldX, oldY); var centerGeometry = control.center; this.rotate(-control.rotation, centerGeometry); oldGeom.rotate(-control.rotation, centerGeometry); var dx1 = this.x - centerGeometry.x; var dy1 = this.y - centerGeometry.y; var dx0 = dx1 - (this.x - oldGeom.x); var dy0 = dy1 - (this.y - oldGeom.y); if (control.irregular && !control._setfeature) { dx1 -= (this.x - oldGeom.x) / 2; dy1 -= (this.y - oldGeom.y) / 2; } this.x = oldX; this.y = oldY; var scale, ratio = 1; if (reshape) { scale = Math.abs(dy0) < 0.00001 ? 1 : dy1 / dy0; ratio = (Math.abs(dx0) < 0.00001 ? 1 : (dx1 / dx0)) / scale; } else { var l0 = Math.sqrt((dx0 * dx0) + (dy0 * dy0)); var l1 = Math.sqrt((dx1 * dx1) + (dy1 * dy1)); scale = l1 / l0; } // rotate the box to 0 before resizing - saves us some // calculations and is inexpensive because we don't drawFeature. control._moving = true; control.box.geometry.rotate(-control.rotation, centerGeometry); delete control._moving; control.box.geometry.resize(scale, centerGeometry, ratio); control.box.geometry.rotate(control.rotation, centerGeometry); control.transformFeature({scale: scale, ratio: ratio}); if (control.irregular && !control._setfeature) { var newCenter = centerGeometry.clone(); newCenter.x += Math.abs(oldX - centerGeometry.x) < 0.00001 ? 0 : (this.x - oldX); newCenter.y += Math.abs(oldY - centerGeometry.y) < 0.00001 ? 0 : (this.y - oldY); control.box.geometry.move(this.x - oldX, this.y - oldY); control.transformFeature({center: newCenter}); } }; // Override for rotation handle move - make sure that the box and // other handles are updated, and finally transform the feature. var rotationHandleMoveFn = function(x, y){ var oldX = this.x, oldY = this.y; OpenLayers.Geometry.Point.prototype.move.call(this, x, y); if(control._moving) { return; } var evt = control.dragControl.handlers.drag.evt; var constrain = (evt && evt.shiftKey) ? 45 : 1; var centerGeometry = control.center; var dx1 = this.x - centerGeometry.x; var dy1 = this.y - centerGeometry.y; var dx0 = dx1 - x; var dy0 = dy1 - y; this.x = oldX; this.y = oldY; var a0 = Math.atan2(dy0, dx0); var a1 = Math.atan2(dy1, dx1); var angle = a1 - a0; angle *= 180 / Math.PI; control._angle = (control._angle + angle) % 360; var diff = control.rotation % constrain; if(Math.abs(control._angle) >= constrain || diff !== 0) { angle = Math.round(control._angle / constrain) * constrain - diff; control._angle = 0; control.box.geometry.rotate(angle, centerGeometry); control.transformFeature({rotation: angle}); } }; var handles = new Array(8); var rotationHandles = new Array(4); var geom, handle, rotationHandle; var positions = ["sw", "s", "se", "e", "ne", "n", "nw", "w"]; for(var i=0; i<8; ++i) { geom = this.box.geometry.components[i]; handle = new OpenLayers.Feature.Vector(geom.clone(), { role: positions[i] + "-resize" }, typeof this.renderIntent == "string" ? null : this.renderIntent); if(i % 2 == 0) { rotationHandle = new OpenLayers.Feature.Vector(geom.clone(), { role: positions[i] + "-rotate" }, typeof this.rotationHandleSymbolizer == "string" ? null : this.rotationHandleSymbolizer); rotationHandle.geometry.move = rotationHandleMoveFn; geom._rotationHandle = rotationHandle; rotationHandles[i/2] = rotationHandle; } geom.move = vertexMoveFn; geom.resize = vertexResizeFn; geom.rotate = vertexRotateFn; handle.geometry.move = handleMoveFn; geom._handle = handle; handles[i] = handle; } this.rotationHandles = rotationHandles; this.handles = handles; }, /** * Method: createControl * Creates a DragFeature control for this control. */ createControl: function() { var control = this; this.dragControl = new OpenLayers.Control.DragFeature(this.layer, { documentDrag: true, // avoid moving the feature itself - move the box instead moveFeature: function(pixel) { if(this.feature === control.feature) { this.feature = control.box; } OpenLayers.Control.DragFeature.prototype.moveFeature.apply(this, arguments); }, // transform while dragging onDrag: function(feature, pixel) { if(feature === control.box) { control.transformFeature({center: control.center}); } }, // set a new feature onStart: function(feature, pixel) { var eligible = !control.geometryTypes || OpenLayers.Util.indexOf(control.geometryTypes, feature.geometry.CLASS_NAME) !== -1; var i = OpenLayers.Util.indexOf(control.handles, feature); i += OpenLayers.Util.indexOf(control.rotationHandles, feature); if(feature !== control.feature && feature !== control.box && i == -2 && eligible) { control.setFeature(feature); } }, onComplete: function(feature, pixel) { control.events.triggerEvent("transformcomplete", {feature: control.feature}); } }); }, /** * Method: drawHandles * Draws the handles to match the box. */ drawHandles: function() { var layer = this.layer; for(var i=0; i<8; ++i) { if(this.rotate && i % 2 === 0) { layer.drawFeature(this.rotationHandles[i/2], this.rotationHandleSymbolizer); } layer.drawFeature(this.handles[i], this.renderIntent); } }, /** * Method: transformFeature * Transforms the feature. * * Parameters: * mods - {Object} An object with optional scale, ratio, rotation and * center properties. */ transformFeature: function(mods) { if(!this._setfeature) { this.scale *= (mods.scale || 1); this.ratio *= (mods.ratio || 1); var oldRotation = this.rotation; this.rotation = (this.rotation + (mods.rotation || 0)) % 360; if(this.events.triggerEvent("beforetransform", mods) !== false) { var feature = this.feature; var geom = feature.geometry; var center = this.center; geom.rotate(-oldRotation, center); if(mods.scale || mods.ratio) { geom.resize(mods.scale, center, mods.ratio); } else if(mods.center) { feature.move(mods.center.getBounds().getCenterLonLat()); } geom.rotate(this.rotation, center); this.layer.drawFeature(feature); feature.toState(OpenLayers.State.UPDATE); this.events.triggerEvent("transform", mods); } } this.layer.drawFeature(this.box, this.renderIntent); this.drawHandles(); }, /** * APIMethod: destroy * Take care of things that are not handled in superclass. */ destroy: function() { var geom; for(var i=0; i<8; ++i) { geom = this.box.geometry.components[i]; geom._handle.destroy(); geom._handle = null; geom._rotationHandle && geom._rotationHandle.destroy(); geom._rotationHandle = null; } this.center = null; this.feature = null; this.handles = null; this.rotationHandleSymbolizer = null; this.rotationHandles = null; this.box.destroy(); this.box = null; this.layer = null; this.dragControl.destroy(); this.dragControl = null; OpenLayers.Control.prototype.destroy.apply(this, arguments); }, CLASS_NAME: "OpenLayers.Control.TransformFeature" }); /* ====================================================================== OpenLayers/Control/PanZoomBar.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/PanZoom.js */ /** * Class: OpenLayers.Control.PanZoomBar * The PanZoomBar is a visible control composed of a * <OpenLayers.Control.PanPanel> and a <OpenLayers.Control.ZoomBar>. * By default it is displayed in the upper left corner of the map as 4 * directional arrows above a vertical slider. * * Inherits from: * - <OpenLayers.Control.PanZoom> */ OpenLayers.Control.PanZoomBar = OpenLayers.Class(OpenLayers.Control.PanZoom, { /** * APIProperty: zoomStopWidth */ zoomStopWidth: 18, /** * APIProperty: zoomStopHeight */ zoomStopHeight: 11, /** * Property: slider */ slider: null, /** * Property: sliderEvents * {<OpenLayers.Events>} */ sliderEvents: null, /** * Property: zoombarDiv * {DOMElement} */ zoombarDiv: null, /** * APIProperty: zoomWorldIcon * {Boolean} */ zoomWorldIcon: false, /** * APIProperty: panIcons * {Boolean} Set this property to false not to display the pan icons. If * false the zoom world icon is placed under the zoom bar. Defaults to * true. */ panIcons: true, /** * APIProperty: forceFixedZoomLevel * {Boolean} Force a fixed zoom level even though the map has * fractionalZoom */ forceFixedZoomLevel: false, /** * Property: mouseDragStart * {<OpenLayers.Pixel>} */ mouseDragStart: null, /** * Property: deltaY * {Number} The cumulative vertical pixel offset during a zoom bar drag. */ deltaY: null, /** * Property: zoomStart * {<OpenLayers.Pixel>} */ zoomStart: null, /** * Constructor: OpenLayers.Control.PanZoomBar */ /** * APIMethod: destroy */ destroy: function() { this._removeZoomBar(); this.map.events.un({ "changebaselayer": this.redraw, "updatesize": this.redraw, scope: this }); OpenLayers.Control.PanZoom.prototype.destroy.apply(this, arguments); delete this.mouseDragStart; delete this.zoomStart; }, /** * Method: setMap * * Parameters: * map - {<OpenLayers.Map>} */ setMap: function(map) { OpenLayers.Control.PanZoom.prototype.setMap.apply(this, arguments); this.map.events.on({ "changebaselayer": this.redraw, "updatesize": this.redraw, scope: this }); }, /** * Method: redraw * clear the div and start over. */ redraw: function() { if (this.div != null) { this.removeButtons(); this._removeZoomBar(); } this.draw(); }, /** * Method: draw * * Parameters: * px - {<OpenLayers.Pixel>} */ draw: function(px) { // initialize our internal div OpenLayers.Control.prototype.draw.apply(this, arguments); px = this.position.clone(); // place the controls this.buttons = []; var sz = {w: 18, h: 18}; if (this.panIcons) { var centered = new OpenLayers.Pixel(px.x+sz.w/2, px.y); var wposition = sz.w; if (this.zoomWorldIcon) { centered = new OpenLayers.Pixel(px.x+sz.w, px.y); } this._addButton("panup", "north-mini.png", centered, sz); px.y = centered.y+sz.h; this._addButton("panleft", "west-mini.png", px, sz); if (this.zoomWorldIcon) { this._addButton("zoomworld", "zoom-world-mini.png", px.add(sz.w, 0), sz); wposition *= 2; } this._addButton("panright", "east-mini.png", px.add(wposition, 0), sz); this._addButton("pandown", "south-mini.png", centered.add(0, sz.h*2), sz); this._addButton("zoomin", "zoom-plus-mini.png", centered.add(0, sz.h*3+5), sz); centered = this._addZoomBar(centered.add(0, sz.h*4 + 5)); this._addButton("zoomout", "zoom-minus-mini.png", centered, sz); } else { this._addButton("zoomin", "zoom-plus-mini.png", px, sz); centered = this._addZoomBar(px.add(0, sz.h)); this._addButton("zoomout", "zoom-minus-mini.png", centered, sz); if (this.zoomWorldIcon) { centered = centered.add(0, sz.h+3); this._addButton("zoomworld", "zoom-world-mini.png", centered, sz); } } return this.div; }, /** * Method: _addZoomBar * * Parameters: * centered - {<OpenLayers.Pixel>} where zoombar drawing is to start. */ _addZoomBar:function(centered) { var imgLocation = OpenLayers.Util.getImageLocation("slider.png"); var id = this.id + "_" + this.map.id; var minZoom = this.map.getMinZoom(); var zoomsToEnd = this.map.getNumZoomLevels() - 1 - this.map.getZoom(); var slider = OpenLayers.Util.createAlphaImageDiv(id, centered.add(-1, zoomsToEnd * this.zoomStopHeight), {w: 20, h: 9}, imgLocation, "absolute"); slider.style.cursor = "move"; this.slider = slider; this.sliderEvents = new OpenLayers.Events(this, slider, null, true, {includeXY: true}); this.sliderEvents.on({ "touchstart": this.zoomBarDown, "touchmove": this.zoomBarDrag, "touchend": this.zoomBarUp, "mousedown": this.zoomBarDown, "mousemove": this.zoomBarDrag, "mouseup": this.zoomBarUp }); var sz = { w: this.zoomStopWidth, h: this.zoomStopHeight * (this.map.getNumZoomLevels() - minZoom) }; var imgLocation = OpenLayers.Util.getImageLocation("zoombar.png"); var div = null; if (OpenLayers.Util.alphaHack()) { var id = this.id + "_" + this.map.id; div = OpenLayers.Util.createAlphaImageDiv(id, centered, {w: sz.w, h: this.zoomStopHeight}, imgLocation, "absolute", null, "crop"); div.style.height = sz.h + "px"; } else { div = OpenLayers.Util.createDiv( 'OpenLayers_Control_PanZoomBar_Zoombar' + this.map.id, centered, sz, imgLocation); } div.style.cursor = "pointer"; div.className = "olButton"; this.zoombarDiv = div; this.div.appendChild(div); this.startTop = parseInt(div.style.top); this.div.appendChild(slider); this.map.events.register("zoomend", this, this.moveZoomBar); centered = centered.add(0, this.zoomStopHeight * (this.map.getNumZoomLevels() - minZoom)); return centered; }, /** * Method: _removeZoomBar */ _removeZoomBar: function() { this.sliderEvents.un({ "touchstart": this.zoomBarDown, "touchmove": this.zoomBarDrag, "touchend": this.zoomBarUp, "mousedown": this.zoomBarDown, "mousemove": this.zoomBarDrag, "mouseup": this.zoomBarUp }); this.sliderEvents.destroy(); this.div.removeChild(this.zoombarDiv); this.zoombarDiv = null; this.div.removeChild(this.slider); this.slider = null; this.map.events.unregister("zoomend", this, this.moveZoomBar); }, /** * Method: onButtonClick * * Parameters: * evt - {Event} */ onButtonClick: function(evt) { OpenLayers.Control.PanZoom.prototype.onButtonClick.apply(this, arguments); if (evt.buttonElement === this.zoombarDiv) { var levels = evt.buttonXY.y / this.zoomStopHeight; if(this.forceFixedZoomLevel || !this.map.fractionalZoom) { levels = Math.floor(levels); } var zoom = (this.map.getNumZoomLevels() - 1) - levels; zoom = Math.min(Math.max(zoom, 0), this.map.getNumZoomLevels() - 1); this.map.zoomTo(zoom); } }, /** * Method: passEventToSlider * This function is used to pass events that happen on the div, or the map, * through to the slider, which then does its moving thing. * * Parameters: * evt - {<OpenLayers.Event>} */ passEventToSlider:function(evt) { this.sliderEvents.handleBrowserEvent(evt); }, /* * Method: zoomBarDown * event listener for clicks on the slider * * Parameters: * evt - {<OpenLayers.Event>} */ zoomBarDown:function(evt) { if (!OpenLayers.Event.isLeftClick(evt) && !OpenLayers.Event.isSingleTouch(evt)) { return; } this.map.events.on({ "touchmove": this.passEventToSlider, "mousemove": this.passEventToSlider, "mouseup": this.passEventToSlider, scope: this }); this.mouseDragStart = evt.xy.clone(); this.zoomStart = evt.xy.clone(); this.div.style.cursor = "move"; // reset the div offsets just in case the div moved this.zoombarDiv.offsets = null; OpenLayers.Event.stop(evt); }, /* * Method: zoomBarDrag * This is what happens when a click has occurred, and the client is * dragging. Here we must ensure that the slider doesn't go beyond the * bottom/top of the zoombar div, as well as moving the slider to its new * visual location * * Parameters: * evt - {<OpenLayers.Event>} */ zoomBarDrag:function(evt) { if (this.mouseDragStart != null) { var deltaY = this.mouseDragStart.y - evt.xy.y; var offsets = OpenLayers.Util.pagePosition(this.zoombarDiv); if ((evt.clientY - offsets[1]) > 0 && (evt.clientY - offsets[1]) < parseInt(this.zoombarDiv.style.height) - 2) { var newTop = parseInt(this.slider.style.top) - deltaY; this.slider.style.top = newTop+"px"; this.mouseDragStart = evt.xy.clone(); } // set cumulative displacement this.deltaY = this.zoomStart.y - evt.xy.y; OpenLayers.Event.stop(evt); } }, /* * Method: zoomBarUp * Perform cleanup when a mouseup event is received -- discover new zoom * level and switch to it. * * Parameters: * evt - {<OpenLayers.Event>} */ zoomBarUp:function(evt) { if (!OpenLayers.Event.isLeftClick(evt) && evt.type !== "touchend") { return; } if (this.mouseDragStart) { this.div.style.cursor=""; this.map.events.un({ "touchmove": this.passEventToSlider, "mouseup": this.passEventToSlider, "mousemove": this.passEventToSlider, scope: this }); var zoomLevel = this.map.zoom; if (!this.forceFixedZoomLevel && this.map.fractionalZoom) { zoomLevel += this.deltaY/this.zoomStopHeight; zoomLevel = Math.min(Math.max(zoomLevel, 0), this.map.getNumZoomLevels() - 1); } else { zoomLevel += this.deltaY/this.zoomStopHeight; zoomLevel = Math.max(Math.round(zoomLevel), 0); } this.map.zoomTo(zoomLevel); this.mouseDragStart = null; this.zoomStart = null; this.deltaY = 0; OpenLayers.Event.stop(evt); } }, /* * Method: moveZoomBar * Change the location of the slider to match the current zoom level. */ moveZoomBar:function() { var newTop = ((this.map.getNumZoomLevels()-1) - this.map.getZoom()) * this.zoomStopHeight + this.startTop + 1; this.slider.style.top = newTop + "px"; }, CLASS_NAME: "OpenLayers.Control.PanZoomBar" }); /* ====================================================================== OpenLayers/Control/LayerSwitcher.js ====================================================================== */ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for * full list of contributors). Published under the 2-clause BSD license. * See license.txt in the OpenLayers distribution or repository for the * full text of the license. */ /** * @requires OpenLayers/Control.js * @requires OpenLayers/Lang.js * @requires OpenLayers/Util.js * @requires OpenLayers/Events/buttonclick.js */ /** * Class: OpenLayers.Control.LayerSwitcher * The LayerSwitcher control displays a table of contents for the map. This * allows the user interface to switch between BaseLasyers and to show or hide * Overlays. By default the switcher is shown minimized on the right edge of * the map, the user may expand it by clicking on the handle. * * To create the LayerSwitcher outside of the map, pass the Id of a html div * as the first argument to the constructor. * * Inherits from: * - <OpenLayers.Control> */ OpenLayers.Control.LayerSwitcher = OpenLayers.Class(OpenLayers.Control, { /** * Property: layerStates * {Array(Object)} Basically a copy of the "state" of the map's layers * the last time the control was drawn. We have this in order to avoid * unnecessarily redrawing the control. */ layerStates: null, // DOM Elements /** * Property: layersDiv * {DOMElement} */ layersDiv: null, /** * Property: baseLayersDiv * {DOMElement} */ baseLayersDiv: null, /** * Property: baseLayers * {Array(Object)} */ baseLayers: null, /** * Property: dataLbl * {DOMElement} */ dataLbl: null, /** * Property: dataLayersDiv * {DOMElement} */ dataLayersDiv: null, /** * Property: dataLayers * {Array(Object)} */ dataLayers: null, /** * Property: minimizeDiv * {DOMElement} */ minimizeDiv: null, /** * Property: maximizeDiv * {DOMElement} */ maximizeDiv: null, /** * APIProperty: ascending * {Boolean} */ ascending: true, /** * Constructor: OpenLayers.Control.LayerSwitcher * * Parameters: * options - {Object} */ initialize: function(options) { OpenLayers.Control.prototype.initialize.apply(this, arguments); this.layerStates = []; }, /** * APIMethod: destroy */ destroy: function() { //clear out layers info and unregister their events this.clearLayersArray("base"); this.clearLayersArray("data"); this.map.events.un({ buttonclick: this.onButtonClick, addlayer: this.redraw, changelayer: this.redraw, removelayer: this.redraw, changebaselayer: this.redraw, scope: this }); this.events.unregister("buttonclick", this, this.onButtonClick); OpenLayers.Control.prototype.destroy.apply(this, arguments); }, /** * Method: setMap * * Properties: * map - {<OpenLayers.Map>} */ setMap: function(map) { OpenLayers.Control.prototype.setMap.apply(this, arguments); this.map.events.on({ addlayer: this.redraw, changelayer: this.redraw, removelayer: this.redraw, changebaselayer: this.redraw, scope: this }); if (this.outsideViewport) { this.events.attachToElement(this.div); this.events.register("buttonclick", this, this.onButtonClick); } else { this.map.events.register("buttonclick", this, this.onButtonClick); } }, /** * Method: draw * * Returns: * {DOMElement} A reference to the DIV DOMElement containing the * switcher tabs. */ draw: function() { OpenLayers.Control.prototype.draw.apply(this); // create layout divs this.loadContents(); // set mode to minimize if(!this.outsideViewport) { this.minimizeControl(); } // populate div with current info this.redraw(); return this.div; }, /** * Method: onButtonClick * * Parameters: * evt - {Event} */ onButtonClick: function(evt) { var button = evt.buttonElement; if (button === this.minimizeDiv) { this.minimizeControl(); } else if (button === this.maximizeDiv) { this.maximizeControl(); } else if (button._layerSwitcher === this.id) { if (button["for"]) { button = document.getElementById(button["for"]); } if (!button.disabled) { if (button.type == "radio") { button.checked = true; this.map.setBaseLayer(this.map.getLayer(button._layer)); } else { button.checked = !button.checked; this.updateMap(); } } } }, /** * Method: clearLayersArray * User specifies either "base" or "data". we then clear all the * corresponding listeners, the div, and reinitialize a new array. * * Parameters: * layersType - {String} */ clearLayersArray: function(layersType) { this[layersType + "LayersDiv"].innerHTML = ""; this[layersType + "Layers"] = []; }, /** * Method: checkRedraw * Checks if the layer state has changed since the last redraw() call. * * Returns: * {Boolean} The layer state changed since the last redraw() call. */ checkRedraw: function() { if ( !this.layerStates.length || (this.map.layers.length != this.layerStates.length) ) { return true; } for (var i = 0, len = this.layerStates.length; i < len; i++) { var layerState = this.layerStates[i]; var layer = this.map.layers[i]; if ( (layerState.name != layer.name) || (layerState.inRange != layer.inRange) || (layerState.id != layer.id) || (layerState.visibility != layer.visibility) ) { return true; } } return false; }, /** * Method: redraw * Goes through and takes the current state of the Map and rebuilds the * control to display that state. Groups base layers into a * radio-button group and lists each data layer with a checkbox. * * Returns: * {DOMElement} A reference to the DIV DOMElement containing the control */ redraw: function() { //if the state hasn't changed since last redraw, no need // to do anything. Just return the existing div. if (!this.checkRedraw()) { return this.div; } //clear out previous layers this.clearLayersArray("base"); this.clearLayersArray("data"); var containsOverlays = false; var containsBaseLayers = false; // Save state -- for checking layer if the map state changed. // We save this before redrawing, because in the process of redrawing // we will trigger more visibility changes, and we want to not redraw // and enter an infinite loop. var len = this.map.layers.length; this.layerStates = new Array(len); for (var i=0; i <len; i++) { var layer = this.map.layers[i]; this.layerStates[i] = { 'name': layer.name, 'visibility': layer.visibility, 'inRange': layer.inRange, 'id': layer.id }; } var layers = this.map.layers.slice(); if (!this.ascending) { layers.reverse(); } for(var i=0, len=layers.length; i<len; i++) { var layer = layers[i]; var baseLayer = layer.isBaseLayer; if (layer.displayInLayerSwitcher) { if (baseLayer) { containsBaseLayers = true; } else { containsOverlays = true; } // only check a baselayer if it is *the* baselayer, check data // layers if they are visible var checked = (baseLayer) ? (layer == this.map.baseLayer) : layer.getVisibility(); // create input element var inputElem = document.createElement("input"), // The input shall have an id attribute so we can use // labels to interact with them. inputId = OpenLayers.Util.createUniqueID( this.id + "_input_" ); inputElem.id = inputId; inputElem.name = (baseLayer) ? this.id + "_baseLayers" : layer.name; inputElem.type = (baseLayer) ? "radio" : "checkbox"; inputElem.value = layer.name; inputElem.checked = checked; inputElem.defaultChecked = checked; inputElem.className = "olButton"; inputElem._layer = layer.id; inputElem._layerSwitcher = this.id; if (!baseLayer && !layer.inRange) { inputElem.disabled = true; } // create span var labelSpan = document.createElement("label"); // this isn't the DOM attribute 'for', but an arbitrary name we // use to find the appropriate input element in <onButtonClick> labelSpan["for"] = inputElem.id; OpenLayers.Element.addClass(labelSpan, "labelSpan olButton"); labelSpan._layer = layer.id; labelSpan._layerSwitcher = this.id; if (!baseLayer && !layer.inRange) { labelSpan.style.color = "gray"; } labelSpan.innerHTML = layer.name; labelSpan.style.verticalAlign = (baseLayer) ? "bottom" : "baseline"; // create line break var br = document.createElement("br"); var groupArray = (baseLayer) ? this.baseLayers : this.dataLayers; groupArray.push({ 'layer': layer, 'inputElem': inputElem, 'labelSpan': labelSpan }); var groupDiv = (baseLayer) ? this.baseLayersDiv : this.dataLayersDiv; groupDiv.appendChild(inputElem); groupDiv.appendChild(labelSpan); groupDiv.appendChild(br); } } // if no overlays, dont display the overlay label this.dataLbl.style.display = (containsOverlays) ? "" : "none"; // if no baselayers, dont display the baselayer label this.baseLbl.style.display = (containsBaseLayers) ? "" : "none"; return this.div; }, /** * Method: updateMap * Cycles through the loaded data and base layer input arrays and makes * the necessary calls to the Map object such that that the map's * visual state corresponds to what the user has selected in * the control. */ updateMap: function() { // set the newly selected base layer for(var i=0, len=this.baseLayers.length; i<len; i++) { var layerEntry = this.baseLayers[i]; if (layerEntry.inputElem.checked) { this.map.setBaseLayer(layerEntry.layer, false); } } // set the correct visibilities for the overlays for(var i=0, len=this.dataLayers.length; i<len; i++) { var layerEntry = this.dataLayers[i]; layerEntry.layer.setVisibility(layerEntry.inputElem.checked); } }, /** * Method: maximizeControl * Set up the labels and divs for the control * * Parameters: * e - {Event} */ maximizeControl: function(e) { // set the div's width and height to empty values, so // the div dimensions can be controlled by CSS this.div.style.width = ""; this.div.style.height = ""; this.showControls(false); if (e != null) { OpenLayers.Event.stop(e); } }, /** * Method: minimizeControl * Hide all the contents of the control, shrink the size, * add the maximize icon * * Parameters: * e - {Event} */ minimizeControl: function(e) { // to minimize the control we set its div's width // and height to 0px, we cannot just set "display" // to "none" because it would hide the maximize // div this.div.style.width = "0px"; this.div.style.height = "0px"; this.showControls(true); if (e != null) { OpenLayers.Event.stop(e); } }, /** * Method: showControls * Hide/Show all LayerSwitcher controls depending on whether we are * minimized or not * * Parameters: * minimize - {Boolean} */ showControls: function(minimize) { this.maximizeDiv.style.display = minimize ? "" : "none"; this.minimizeDiv.style.display = minimize ? "none" : ""; this.layersDiv.style.display = minimize ? "none" : ""; }, /** * Method: loadContents * Set up the labels and divs for the control */ loadContents: function() { // layers list div this.layersDiv = document.createElement("div"); this.layersDiv.id = this.id + "_layersDiv"; OpenLayers.Element.addClass(this.layersDiv, "layersDiv"); this.baseLbl = document.createElement("div"); this.baseLbl.innerHTML = OpenLayers.i18n("Base Layer"); OpenLayers.Element.addClass(this.baseLbl, "baseLbl"); this.baseLayersDiv = document.createElement("div"); OpenLayers.Element.addClass(this.baseLayersDiv, "baseLayersDiv"); this.dataLbl = document.createElement("div"); this.dataLbl.innerHTML = OpenLayers.i18n("Overlays"); OpenLayers.Element.addClass(this.dataLbl, "dataLbl"); this.dataLayersDiv = document.createElement("div"); OpenLayers.Element.addClass(this.dataLayersDiv, "dataLayersDiv"); if (this.ascending) { this.layersDiv.appendChild(this.baseLbl); this.layersDiv.appendChild(this.baseLayersDiv); this.layersDiv.appendChild(this.dataLbl); this.layersDiv.appendChild(this.dataLayersDiv); } else { this.layersDiv.appendChild(this.dataLbl); this.layersDiv.appendChild(this.dataLayersDiv); this.layersDiv.appendChild(this.baseLbl); this.layersDiv.appendChild(this.baseLayersDiv); } this.div.appendChild(this.layersDiv); // maximize button div var img = OpenLayers.Util.getImageLocation('layer-switcher-maximize.png'); this.maximizeDiv = OpenLayers.Util.createAlphaImageDiv( "OpenLayers_Control_MaximizeDiv", null, null, img, "absolute"); OpenLayers.Element.addClass(this.maximizeDiv, "maximizeDiv olButton"); this.maximizeDiv.style.display = "none"; this.div.appendChild(this.maximizeDiv); // minimize button div var img = OpenLayers.Util.getImageLocation('layer-switcher-minimize.png'); this.minimizeDiv = OpenLayers.Util.createAlphaImageDiv( "OpenLayers_Control_MinimizeDiv", null, null, img, "absolute"); OpenLayers.Element.addClass(this.minimizeDiv, "minimizeDiv olButton"); this.minimizeDiv.style.display = "none"; this.div.appendChild(this.minimizeDiv); }, CLASS_NAME: "OpenLayers.Control.LayerSwitcher" }); /* ====================================================================== OpenLayers/Control/Graticule.js ====================================================================== */ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for * full list of contributors). Published under the 2-clause BSD license. * See license.txt in the OpenLayers distribution or repository for the * full text of the license. */ /** * @requires OpenLayers/Control.js * @requires OpenLayers/Lang.js * @requires OpenLayers/Rule.js * @requires OpenLayers/StyleMap.js * @requires OpenLayers/Layer/Vector.js */ /** * Class: OpenLayers.Control.Graticule * The Graticule displays a grid of latitude/longitude lines reprojected on * the map. * * Inherits from: * - <OpenLayers.Control> * */ OpenLayers.Control.Graticule = OpenLayers.Class(OpenLayers.Control, { /** * APIProperty: autoActivate * {Boolean} Activate the control when it is added to a map. Default is * true. */ autoActivate: true, /** * APIProperty: intervals * {Array(Float)} A list of possible graticule widths in degrees. */ intervals: [ 45, 30, 20, 10, 5, 2, 1, 0.5, 0.2, 0.1, 0.05, 0.01, 0.005, 0.002, 0.001 ], /** * APIProperty: displayInLayerSwitcher * {Boolean} Allows the Graticule control to be switched on and off by * LayerSwitcher control. Defaults is true. */ displayInLayerSwitcher: true, /** * APIProperty: visible * {Boolean} should the graticule be initially visible (default=true) */ visible: true, /** * APIProperty: numPoints * {Integer} The number of points to use in each graticule line. Higher * numbers result in a smoother curve for projected maps */ numPoints: 50, /** * APIProperty: targetSize * {Integer} The maximum size of the grid in pixels on the map */ targetSize: 200, /** * APIProperty: layerName * {String} The name to be displayed in the layer switcher, default is set * by {<OpenLayers.Lang>}. */ layerName: null, /** * APIProperty: labelled * {Boolean} Should the graticule lines be labelled?. default=true */ labelled: true, /** * APIProperty: labelFormat * {String} the format of the labels, default = 'dm'. See * <OpenLayers.Util.getFormattedLonLat> for other options. */ labelFormat: 'dm', /** * APIProperty: lineSymbolizer * {symbolizer} the symbolizer used to render lines */ lineSymbolizer: { strokeColor: "#333", strokeWidth: 1, strokeOpacity: 0.5 }, /** * APIProperty: labelSymbolizer * {symbolizer} the symbolizer used to render labels */ labelSymbolizer: {}, /** * Property: gratLayer * {<OpenLayers.Layer.Vector>} vector layer used to draw the graticule on */ gratLayer: null, /** * Constructor: OpenLayers.Control.Graticule * Create a new graticule control to display a grid of latitude longitude * lines. * * Parameters: * options - {Object} An optional object whose properties will be used * to extend the control. */ initialize: function(options) { options = options || {}; options.layerName = options.layerName || OpenLayers.i18n("Graticule"); OpenLayers.Control.prototype.initialize.apply(this, [options]); this.labelSymbolizer.stroke = false; this.labelSymbolizer.fill = false; this.labelSymbolizer.label = "${label}"; this.labelSymbolizer.labelAlign = "${labelAlign}"; this.labelSymbolizer.labelXOffset = "${xOffset}"; this.labelSymbolizer.labelYOffset = "${yOffset}"; }, /** * APIMethod: destroy */ destroy: function() { this.deactivate(); OpenLayers.Control.prototype.destroy.apply(this, arguments); if (this.gratLayer) { this.gratLayer.destroy(); this.gratLayer = null; } }, /** * Method: draw * * initializes the graticule layer and does the initial update * * Returns: * {DOMElement} */ draw: function() { OpenLayers.Control.prototype.draw.apply(this, arguments); if (!this.gratLayer) { var gratStyle = new OpenLayers.Style({},{ rules: [new OpenLayers.Rule({'symbolizer': {"Point":this.labelSymbolizer, "Line":this.lineSymbolizer} })] }); this.gratLayer = new OpenLayers.Layer.Vector(this.layerName, { styleMap: new OpenLayers.StyleMap({'default':gratStyle}), visibility: this.visible, displayInLayerSwitcher: this.displayInLayerSwitcher }); } return this.div; }, /** * APIMethod: activate */ activate: function() { if (OpenLayers.Control.prototype.activate.apply(this, arguments)) { this.map.addLayer(this.gratLayer); this.map.events.register('moveend', this, this.update); this.update(); return true; } else { return false; } }, /** * APIMethod: deactivate */ deactivate: function() { if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) { this.map.events.unregister('moveend', this, this.update); this.map.removeLayer(this.gratLayer); return true; } else { return false; } }, /** * Method: update * * calculates the grid to be displayed and actually draws it * * Returns: * {DOMElement} */ update: function() { //wait for the map to be initialized before proceeding var mapBounds = this.map.getExtent(); if (!mapBounds) { return; } //clear out the old grid this.gratLayer.destroyFeatures(); //get the projection objects required var llProj = new OpenLayers.Projection("EPSG:4326"); var mapProj = this.map.getProjectionObject(); var mapRes = this.map.getResolution(); //if the map is in lon/lat, then the lines are straight and only one //point is required if (mapProj.proj && mapProj.proj.projName == "longlat") { this.numPoints = 1; } //get the map center in EPSG:4326 var mapCenter = this.map.getCenter(); //lon and lat here are really map x and y var mapCenterLL = new OpenLayers.Pixel(mapCenter.lon, mapCenter.lat); OpenLayers.Projection.transform(mapCenterLL, mapProj, llProj); /* This block of code determines the lon/lat interval to use for the * grid by calculating the diagonal size of one grid cell at the map * center. Iterates through the intervals array until the diagonal * length is less than the targetSize option. */ //find lat/lon interval that results in a grid of less than the target size var testSq = this.targetSize*mapRes; testSq *= testSq; //compare squares rather than doing a square root to save time var llInterval; for (var i=0; i<this.intervals.length; ++i) { llInterval = this.intervals[i]; //could do this for both x and y?? var delta = llInterval/2; var p1 = mapCenterLL.offset({x: -delta, y: -delta}); //test coords in EPSG:4326 space var p2 = mapCenterLL.offset({x: delta, y: delta}); OpenLayers.Projection.transform(p1, llProj, mapProj); // convert them back to map projection OpenLayers.Projection.transform(p2, llProj, mapProj); var distSq = (p1.x-p2.x)*(p1.x-p2.x) + (p1.y-p2.y)*(p1.y-p2.y); if (distSq <= testSq) { break; } } //alert(llInterval); //round the LL center to an even number based on the interval mapCenterLL.x = Math.floor(mapCenterLL.x/llInterval)*llInterval; mapCenterLL.y = Math.floor(mapCenterLL.y/llInterval)*llInterval; //TODO adjust for minutses/seconds? /* The following 2 blocks calculate the nodes of the grid along a * line of constant longitude (then latitiude) running through the * center of the map until it reaches the map edge. The calculation * goes from the center in both directions to the edge. */ //get the central longitude line, increment the latitude var iter = 0; var centerLonPoints = [mapCenterLL.clone()]; var newPoint = mapCenterLL.clone(); var mapXY; do { newPoint = newPoint.offset({x: 0, y: llInterval}); mapXY = OpenLayers.Projection.transform(newPoint.clone(), llProj, mapProj); centerLonPoints.unshift(newPoint); } while (mapBounds.containsPixel(mapXY) && ++iter<1000); newPoint = mapCenterLL.clone(); do { newPoint = newPoint.offset({x: 0, y: -llInterval}); mapXY = OpenLayers.Projection.transform(newPoint.clone(), llProj, mapProj); centerLonPoints.push(newPoint); } while (mapBounds.containsPixel(mapXY) && ++iter<1000); //get the central latitude line, increment the longitude iter = 0; var centerLatPoints = [mapCenterLL.clone()]; newPoint = mapCenterLL.clone(); do { newPoint = newPoint.offset({x: -llInterval, y: 0}); mapXY = OpenLayers.Projection.transform(newPoint.clone(), llProj, mapProj); centerLatPoints.unshift(newPoint); } while (mapBounds.containsPixel(mapXY) && ++iter<1000); newPoint = mapCenterLL.clone(); do { newPoint = newPoint.offset({x: llInterval, y: 0}); mapXY = OpenLayers.Projection.transform(newPoint.clone(), llProj, mapProj); centerLatPoints.push(newPoint); } while (mapBounds.containsPixel(mapXY) && ++iter<1000); //now generate a line for each node in the central lat and lon lines //first loop over constant longitude var lines = []; for(var i=0; i < centerLatPoints.length; ++i) { var lon = centerLatPoints[i].x; var pointList = []; var labelPoint = null; var latEnd = Math.min(centerLonPoints[0].y, 90); var latStart = Math.max(centerLonPoints[centerLonPoints.length - 1].y, -90); var latDelta = (latEnd - latStart)/this.numPoints; var lat = latStart; for(var j=0; j<= this.numPoints; ++j) { var gridPoint = new OpenLayers.Geometry.Point(lon,lat); gridPoint.transform(llProj, mapProj); pointList.push(gridPoint); lat += latDelta; if (gridPoint.y >= mapBounds.bottom && !labelPoint) { labelPoint = gridPoint; } } if (this.labelled) { //keep track of when this grid line crosses the map bounds to set //the label position //labels along the bottom, add 10 pixel offset up into the map //TODO add option for labels on top var labelPos = new OpenLayers.Geometry.Point(labelPoint.x,mapBounds.bottom); var labelAttrs = { value: lon, label: this.labelled?OpenLayers.Util.getFormattedLonLat(lon, "lon", this.labelFormat):"", labelAlign: "cb", xOffset: 0, yOffset: 2 }; this.gratLayer.addFeatures(new OpenLayers.Feature.Vector(labelPos,labelAttrs)); } var geom = new OpenLayers.Geometry.LineString(pointList); lines.push(new OpenLayers.Feature.Vector(geom)); } //now draw the lines of constant latitude for (var j=0; j < centerLonPoints.length; ++j) { lat = centerLonPoints[j].y; if (lat<-90 || lat>90) { //latitudes only valid between -90 and 90 continue; } var pointList = []; var lonStart = centerLatPoints[0].x; var lonEnd = centerLatPoints[centerLatPoints.length - 1].x; var lonDelta = (lonEnd - lonStart)/this.numPoints; var lon = lonStart; var labelPoint = null; for(var i=0; i <= this.numPoints ; ++i) { var gridPoint = new OpenLayers.Geometry.Point(lon,lat); gridPoint.transform(llProj, mapProj); pointList.push(gridPoint); lon += lonDelta; if (gridPoint.x < mapBounds.right) { labelPoint = gridPoint; } } if (this.labelled) { //keep track of when this grid line crosses the map bounds to set //the label position //labels along the right, 30 pixel offset left into the map //TODO add option for labels on left var labelPos = new OpenLayers.Geometry.Point(mapBounds.right, labelPoint.y); var labelAttrs = { value: lat, label: this.labelled?OpenLayers.Util.getFormattedLonLat(lat, "lat", this.labelFormat):"", labelAlign: "rb", xOffset: -2, yOffset: 2 }; this.gratLayer.addFeatures(new OpenLayers.Feature.Vector(labelPos,labelAttrs)); } var geom = new OpenLayers.Geometry.LineString(pointList); lines.push(new OpenLayers.Feature.Vector(geom)); } this.gratLayer.addFeatures(lines); }, CLASS_NAME: "OpenLayers.Control.Graticule" }); /* ====================================================================== 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/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/Control/UTFGrid.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/Hover.js * @requires OpenLayers/Handler/Click.js */ /** * Class: OpenLayers.Control.UTFGrid * * This Control provides behavior associated with UTFGrid Layers. * These 'hit grids' provide underlying feature attributes without * calling the server (again). This control allows Mousemove, Hovering * and Click events to trigger callbacks that use the attributes in * whatever way you need. * * The most common example may be a UTFGrid layer containing feature * attributes that are displayed in a div as you mouseover. * * Example Code: * * (start code) * var world_utfgrid = new OpenLayers.Layer.UTFGrid( * 'UTFGrid Layer', * "http://tiles/world_utfgrid/${z}/${x}/${y}.json" * ); * map.addLayer(world_utfgrid); * * var control = new OpenLayers.Control.UTFGrid({ * layers: [world_utfgrid], * handlerMode: 'move', * callback: function(infoLookup) { * // do something with returned data * * } * }) * (end code) * * * Inherits from: * - <OpenLayers.Control> */ OpenLayers.Control.UTFGrid = OpenLayers.Class(OpenLayers.Control, { /** * APIProperty: autoActivate * {Boolean} Activate the control when it is added to a map. Default is * true. */ autoActivate: true, /** * APIProperty: Layers * List of layers to consider. Must be Layer.UTFGrids * `null` is the default indicating all UTFGrid Layers are queried. * {Array} <OpenLayers.Layer.UTFGrid> */ layers: null, /* Property: defaultHandlerOptions * The default opts passed to the handler constructors */ defaultHandlerOptions: { 'delay': 300, 'pixelTolerance': 4, 'stopMove': false, 'single': true, 'double': false, 'stopSingle': false, 'stopDouble': false }, /* APIProperty: handlerMode * Defaults to 'click'. Can be 'hover' or 'move'. */ handlerMode: 'click', /** * APIMethod: setHandler * sets this.handlerMode and calls resetHandler() * * Parameters: * hm - {String} Handler Mode string; 'click', 'hover' or 'move'. */ setHandler: function(hm) { this.handlerMode = hm; this.resetHandler(); }, /** * Method: resetHandler * Deactivates the old hanlder and creates a new * <OpenLayers.Handler> based on the mode specified in * this.handlerMode * */ resetHandler: function() { if (this.handler) { this.handler.deactivate(); this.handler.destroy(); this.handler = null; } if (this.handlerMode == 'hover') { // Handle this event on hover this.handler = new OpenLayers.Handler.Hover( this, {'pause': this.handleEvent, 'move': this.reset}, this.handlerOptions ); } else if (this.handlerMode == 'click') { // Handle this event on click this.handler = new OpenLayers.Handler.Click( this, { 'click': this.handleEvent }, this.handlerOptions ); } else if (this.handlerMode == 'move') { this.handler = new OpenLayers.Handler.Hover( this, // Handle this event while hovering OR moving {'pause': this.handleEvent, 'move': this.handleEvent}, this.handlerOptions ); } if (this.handler) { return true; } else { return false; } }, /** * Constructor: <OpenLayers.Control.UTFGrid> * * Parameters: * options - {Object} */ initialize: function(options) { options = options || {}; options.handlerOptions = options.handlerOptions || this.defaultHandlerOptions; OpenLayers.Control.prototype.initialize.apply(this, [options]); this.resetHandler(); }, /** * Method: handleEvent * Internal method called when specified event is triggered. * * This method does several things: * * Gets the lonLat of the event. * * Loops through the appropriate hit grid layers and gathers the attributes. * * Passes the attributes to the callback * * Parameters: * evt - {<OpenLayers.Event>} */ handleEvent: function(evt) { if (evt == null) { this.reset(); return; } var lonLat = this.map.getLonLatFromPixel(evt.xy); if (!lonLat) { return; } var layers = this.findLayers(); if (layers.length > 0) { var infoLookup = {}; var layer, idx; for (var i=0, len=layers.length; i<len; i++) { layer = layers[i]; idx = OpenLayers.Util.indexOf(this.map.layers, layer); infoLookup[idx] = layer.getFeatureInfo(lonLat); } this.callback(infoLookup, lonLat, evt.xy); } }, /** * APIMethod: callback * Function to be called when a mouse event corresponds with a location that * includes data in one of the configured UTFGrid layers. * * Parameters: * infoLookup - {Object} Keys of this object are layer indexes and can be * used to resolve a layer in the map.layers array. The structure of * the property values depend on the data included in the underlying * UTFGrid and may be any valid JSON type. */ callback: function(infoLookup) { // to be provided in the constructor }, /** * Method: reset * Calls the callback with null. */ reset: function(evt) { this.callback(null); }, /** * Method: findLayers * Internal method to get the layers, independent of whether we are * inspecting the map or using a client-provided array * * The default value of this.layers is null; this causes the * findLayers method to return ALL UTFGrid layers encountered. * * Parameters: * None * * Returns: * {Array} Layers to handle on each event */ findLayers: function() { var candidates = this.layers || this.map.layers; var layers = []; var layer; for (var i=candidates.length-1; i>=0; --i) { layer = candidates[i]; if (layer instanceof OpenLayers.Layer.UTFGrid ) { layers.push(layer); } } return layers; }, CLASS_NAME: "OpenLayers.Control.UTFGrid" }); /* ====================================================================== OpenLayers/Control/CacheRead.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.CacheRead * A control for using image tiles cached with <OpenLayers.Control.CacheWrite> * from the browser's local storage. * * Inherits from: * - <OpenLayers.Control> */ OpenLayers.Control.CacheRead = OpenLayers.Class(OpenLayers.Control, { /** * APIProperty: fetchEvent * {String} The layer event to listen to for replacing remote resource tile * URLs with cached data URIs. Supported values are "tileerror" (try * remote first, fall back to cached) and "tileloadstart" (try cache * first, fall back to remote). Default is "tileloadstart". * * Note that "tileerror" will not work for CORS enabled images (see * https://developer.mozilla.org/en/CORS_Enabled_Image), i.e. layers * configured with a <OpenLayers.Tile.Image.crossOriginKeyword> in * <OpenLayers.Layer.Grid.tileOptions>. */ fetchEvent: "tileloadstart", /** * APIProperty: layers * {Array(<OpenLayers.Layer.Grid>)}. Optional. If provided, only these * layers will receive tiles from the cache. */ layers: null, /** * APIProperty: autoActivate * {Boolean} Activate the control when it is added to a map. Default is * true. */ autoActivate: true, /** * Constructor: OpenLayers.Control.CacheRead * * Parameters: * options - {Object} Object with API properties for this control */ /** * Method: setMap * Set the map property for the control. * * Parameters: * map - {<OpenLayers.Map>} */ setMap: function(map) { OpenLayers.Control.prototype.setMap.apply(this, arguments); var i, layers = this.layers || map.layers; for (i=layers.length-1; i>=0; --i) { this.addLayer({layer: layers[i]}); } if (!this.layers) { map.events.on({ addlayer: this.addLayer, removeLayer: this.removeLayer, scope: this }); } }, /** * Method: addLayer * Adds a layer to the control. Once added, tiles requested for this layer * will be cached. * * Parameters: * evt - {Object} Object with a layer property referencing an * <OpenLayers.Layer> instance */ addLayer: function(evt) { evt.layer.events.register(this.fetchEvent, this, this.fetch); }, /** * Method: removeLayer * Removes a layer from the control. Once removed, tiles requested for this * layer will no longer be cached. * * Parameters: * evt - {Object} Object with a layer property referencing an * <OpenLayers.Layer> instance */ removeLayer: function(evt) { evt.layer.events.unregister(this.fetchEvent, this, this.fetch); }, /** * Method: fetch * Listener to the <fetchEvent> event. Replaces a tile's url with a data * URI from the cache. * * Parameters: * evt - {Object} Event object with a tile property. */ fetch: function(evt) { if (this.active && window.localStorage && evt.tile instanceof OpenLayers.Tile.Image) { var tile = evt.tile, url = tile.url; // deal with modified tile urls when both CacheWrite and CacheRead // are active if (!tile.layer.crossOriginKeyword && OpenLayers.ProxyHost && url.indexOf(OpenLayers.ProxyHost) === 0) { url = OpenLayers.Control.CacheWrite.urlMap[url]; } var dataURI = window.localStorage.getItem("olCache_" + url); if (dataURI) { tile.url = dataURI; if (evt.type === "tileerror") { tile.setImgSrc(dataURI); } } } }, /** * 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.layers || this.map) { var i, layers = this.layers || this.map.layers; for (i=layers.length-1; i>=0; --i) { this.removeLayer({layer: layers[i]}); } } if (this.map) { this.map.events.un({ addlayer: this.addLayer, removeLayer: this.removeLayer, scope: this }); } OpenLayers.Control.prototype.destroy.apply(this, arguments); }, CLASS_NAME: "OpenLayers.Control.CacheRead" }); /* ====================================================================== OpenLayers/Control/KeyboardDefaults.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/Keyboard.js * @requires OpenLayers/Events.js */ /** * Class: OpenLayers.Control.KeyboardDefaults * The KeyboardDefaults control adds panning and zooming functions, controlled * with the keyboard. By default arrow keys pan, +/- keys zoom & Page Up/Page * Down/Home/End scroll by three quarters of a page. * * This control has no visible appearance. * * Inherits from: * - <OpenLayers.Control> */ OpenLayers.Control.KeyboardDefaults = OpenLayers.Class(OpenLayers.Control, { /** * APIProperty: autoActivate * {Boolean} Activate the control when it is added to a map. Default is * true. */ autoActivate: true, /** * APIProperty: slideFactor * Pixels to slide by. */ slideFactor: 75, /** * APIProperty: observeElement * {DOMelement|String} The DOM element to handle keys for. You * can use the map div here, to have the navigation keys * work when the map div has the focus. If undefined the * document is used. */ observeElement: null, /** * Constructor: OpenLayers.Control.KeyboardDefaults */ /** * Method: draw * Create handler. */ draw: function() { var observeElement = this.observeElement || document; this.handler = new OpenLayers.Handler.Keyboard( this, {"keydown": this.defaultKeyPress}, {observeElement: observeElement} ); }, /** * Method: defaultKeyPress * When handling the key event, we only use evt.keyCode. This holds * some drawbacks, though we get around them below. When interpretting * the keycodes below (including the comments associated with them), * consult the URL below. For instance, the Safari browser returns * "IE keycodes", and so is supported by any keycode labeled "IE". * * Very informative URL: * http://unixpapa.com/js/key.html * * Parameters: * evt - {Event} */ defaultKeyPress: function (evt) { var size, handled = true; var target = OpenLayers.Event.element(evt); if (target && (target.tagName == 'INPUT' || target.tagName == 'TEXTAREA' || target.tagName == 'SELECT')) { return; } switch (evt.keyCode) { case OpenLayers.Event.KEY_LEFT: this.map.pan(-this.slideFactor, 0); break; case OpenLayers.Event.KEY_RIGHT: this.map.pan(this.slideFactor, 0); break; case OpenLayers.Event.KEY_UP: this.map.pan(0, -this.slideFactor); break; case OpenLayers.Event.KEY_DOWN: this.map.pan(0, this.slideFactor); break; case 33: // Page Up. Same in all browsers. size = this.map.getSize(); this.map.pan(0, -0.75*size.h); break; case 34: // Page Down. Same in all browsers. size = this.map.getSize(); this.map.pan(0, 0.75*size.h); break; case 35: // End. Same in all browsers. size = this.map.getSize(); this.map.pan(0.75*size.w, 0); break; case 36: // Home. Same in all browsers. size = this.map.getSize(); this.map.pan(-0.75*size.w, 0); break; case 43: // +/= (ASCII), keypad + (ASCII, Opera) case 61: // +/= (Mozilla, Opera, some ASCII) case 187: // +/= (IE) case 107: // keypad + (IE, Mozilla) this.map.zoomIn(); break; case 45: // -/_ (ASCII, Opera), keypad - (ASCII, Opera) case 109: // -/_ (Mozilla), keypad - (Mozilla, IE) case 189: // -/_ (IE) case 95: // -/_ (some ASCII) this.map.zoomOut(); break; default: handled = false; } if (handled) { // prevent browser default not to move the page // when moving the page with the keyboard OpenLayers.Event.stop(evt); } }, CLASS_NAME: "OpenLayers.Control.KeyboardDefaults" }); /* ====================================================================== Rico/license.js ====================================================================== */ /** * @license Apache 2 * * Contains portions of Rico <http://openrico.org/> * * Copyright 2005 Sabre Airline Solutions * * Licensed under the Apache License, Version 2.0 (the "License"); you * may not use this file except in compliance with the License. You * may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. See the License for the specific language governing * permissions and limitations under the License. */ /* ====================================================================== Rico/Color.js ====================================================================== */ /** * @requires Rico/license.js * @requires OpenLayers/Console.js * @requires OpenLayers/BaseTypes/Class.js * @requires OpenLayers/BaseTypes/Element.js */ /* * This file has been edited substantially from the Rico-released version by * the OpenLayers development team. */ OpenLayers.Console.warn("OpenLayers.Rico is deprecated"); OpenLayers.Rico = OpenLayers.Rico || {}; OpenLayers.Rico.Color = OpenLayers.Class({ initialize: function(red, green, blue) { this.rgb = { r: red, g : green, b : blue }; }, setRed: function(r) { this.rgb.r = r; }, setGreen: function(g) { this.rgb.g = g; }, setBlue: function(b) { this.rgb.b = b; }, setHue: function(h) { // get an HSB model, and set the new hue... var hsb = this.asHSB(); hsb.h = h; // convert back to RGB... this.rgb = OpenLayers.Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b); }, setSaturation: function(s) { // get an HSB model, and set the new hue... var hsb = this.asHSB(); hsb.s = s; // convert back to RGB and set values... this.rgb = OpenLayers.Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b); }, setBrightness: function(b) { // get an HSB model, and set the new hue... var hsb = this.asHSB(); hsb.b = b; // convert back to RGB and set values... this.rgb = OpenLayers.Rico.Color.HSBtoRGB( hsb.h, hsb.s, hsb.b ); }, darken: function(percent) { var hsb = this.asHSB(); this.rgb = OpenLayers.Rico.Color.HSBtoRGB(hsb.h, hsb.s, Math.max(hsb.b - percent,0)); }, brighten: function(percent) { var hsb = this.asHSB(); this.rgb = OpenLayers.Rico.Color.HSBtoRGB(hsb.h, hsb.s, Math.min(hsb.b + percent,1)); }, blend: function(other) { this.rgb.r = Math.floor((this.rgb.r + other.rgb.r)/2); this.rgb.g = Math.floor((this.rgb.g + other.rgb.g)/2); this.rgb.b = Math.floor((this.rgb.b + other.rgb.b)/2); }, isBright: function() { var hsb = this.asHSB(); return this.asHSB().b > 0.5; }, isDark: function() { return ! this.isBright(); }, asRGB: function() { return "rgb(" + this.rgb.r + "," + this.rgb.g + "," + this.rgb.b + ")"; }, asHex: function() { return "#" + this.rgb.r.toColorPart() + this.rgb.g.toColorPart() + this.rgb.b.toColorPart(); }, asHSB: function() { return OpenLayers.Rico.Color.RGBtoHSB(this.rgb.r, this.rgb.g, this.rgb.b); }, toString: function() { return this.asHex(); } }); OpenLayers.Rico.Color.createFromHex = function(hexCode) { if(hexCode.length==4) { var shortHexCode = hexCode; var hexCode = '#'; for(var i=1;i<4;i++) { hexCode += (shortHexCode.charAt(i) + shortHexCode.charAt(i)); } } if ( hexCode.indexOf('#') == 0 ) { hexCode = hexCode.substring(1); } var red = hexCode.substring(0,2); var green = hexCode.substring(2,4); var blue = hexCode.substring(4,6); return new OpenLayers.Rico.Color( parseInt(red,16), parseInt(green,16), parseInt(blue,16) ); }; /** * Factory method for creating a color from the background of * an HTML element. */ OpenLayers.Rico.Color.createColorFromBackground = function(elem) { var actualColor = OpenLayers.Element.getStyle(OpenLayers.Util.getElement(elem), "backgroundColor"); if ( actualColor == "transparent" && elem.parentNode ) { return OpenLayers.Rico.Color.createColorFromBackground(elem.parentNode); } if ( actualColor == null ) { return new OpenLayers.Rico.Color(255,255,255); } if ( actualColor.indexOf("rgb(") == 0 ) { var colors = actualColor.substring(4, actualColor.length - 1 ); var colorArray = colors.split(","); return new OpenLayers.Rico.Color( parseInt( colorArray[0] ), parseInt( colorArray[1] ), parseInt( colorArray[2] ) ); } else if ( actualColor.indexOf("#") == 0 ) { return OpenLayers.Rico.Color.createFromHex(actualColor); } else { return new OpenLayers.Rico.Color(255,255,255); } }; OpenLayers.Rico.Color.HSBtoRGB = function(hue, saturation, brightness) { var red = 0; var green = 0; var blue = 0; if (saturation == 0) { red = parseInt(brightness * 255.0 + 0.5); green = red; blue = red; } else { var h = (hue - Math.floor(hue)) * 6.0; var f = h - Math.floor(h); var p = brightness * (1.0 - saturation); var q = brightness * (1.0 - saturation * f); var t = brightness * (1.0 - (saturation * (1.0 - f))); switch (parseInt(h)) { case 0: red = (brightness * 255.0 + 0.5); green = (t * 255.0 + 0.5); blue = (p * 255.0 + 0.5); break; case 1: red = (q * 255.0 + 0.5); green = (brightness * 255.0 + 0.5); blue = (p * 255.0 + 0.5); break; case 2: red = (p * 255.0 + 0.5); green = (brightness * 255.0 + 0.5); blue = (t * 255.0 + 0.5); break; case 3: red = (p * 255.0 + 0.5); green = (q * 255.0 + 0.5); blue = (brightness * 255.0 + 0.5); break; case 4: red = (t * 255.0 + 0.5); green = (p * 255.0 + 0.5); blue = (brightness * 255.0 + 0.5); break; case 5: red = (brightness * 255.0 + 0.5); green = (p * 255.0 + 0.5); blue = (q * 255.0 + 0.5); break; } } return { r : parseInt(red), g : parseInt(green) , b : parseInt(blue) }; }; OpenLayers.Rico.Color.RGBtoHSB = function(r, g, b) { var hue; var saturation; var brightness; var cmax = (r > g) ? r : g; if (b > cmax) { cmax = b; } var cmin = (r < g) ? r : g; if (b < cmin) { cmin = b; } brightness = cmax / 255.0; if (cmax != 0) { saturation = (cmax - cmin)/cmax; } else { saturation = 0; } if (saturation == 0) { hue = 0; } else { var redc = (cmax - r)/(cmax - cmin); var greenc = (cmax - g)/(cmax - cmin); var bluec = (cmax - b)/(cmax - cmin); if (r == cmax) { hue = bluec - greenc; } else if (g == cmax) { hue = 2.0 + redc - bluec; } else { hue = 4.0 + greenc - redc; } hue = hue / 6.0; if (hue < 0) { hue = hue + 1.0; } } return { h : hue, s : saturation, b : brightness }; }; /* ====================================================================== Rico/Corner.js ====================================================================== */ /** * @requires OpenLayers/Console.js * @requires Rico/Color.js */ /* * This file has been edited substantially from the Rico-released * version by the OpenLayers development team. * * Copyright 2005 Sabre Airline Solutions * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the * License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, * either express or * implied. See the License for the specific language governing * permissions * and limitations under the License. * */ OpenLayers.Console.warn("OpenLayers.Rico is deprecated"); OpenLayers.Rico = OpenLayers.Rico || {}; OpenLayers.Rico.Corner = { round: function(e, options) { e = OpenLayers.Util.getElement(e); this._setOptions(options); var color = this.options.color; if ( this.options.color == "fromElement" ) { color = this._background(e); } var bgColor = this.options.bgColor; if ( this.options.bgColor == "fromParent" ) { bgColor = this._background(e.offsetParent); } this._roundCornersImpl(e, color, bgColor); }, /** This is a helper function to change the background * color of <div> that has had Rico rounded corners added. * * It seems we cannot just set the background color for the * outer <div> so each <span> element used to create the * corners must have its background color set individually. * * @param {DOM} theDiv - A child of the outer <div> that was * supplied to the `round` method. * * @param {String} newColor - The new background color to use. */ changeColor: function(theDiv, newColor) { theDiv.style.backgroundColor = newColor; var spanElements = theDiv.parentNode.getElementsByTagName("span"); for (var currIdx = 0; currIdx < spanElements.length; currIdx++) { spanElements[currIdx].style.backgroundColor = newColor; } }, /** This is a helper function to change the background * opacity of <div> that has had Rico rounded corners added. * * See changeColor (above) for algorithm explanation * * @param {DOM} theDiv A child of the outer <div> that was * supplied to the `round` method. * * @param {int} newOpacity The new opacity to use (0-1). */ changeOpacity: function(theDiv, newOpacity) { var mozillaOpacity = newOpacity; var ieOpacity = 'alpha(opacity=' + newOpacity * 100 + ')'; theDiv.style.opacity = mozillaOpacity; theDiv.style.filter = ieOpacity; var spanElements = theDiv.parentNode.getElementsByTagName("span"); for (var currIdx = 0; currIdx < spanElements.length; currIdx++) { spanElements[currIdx].style.opacity = mozillaOpacity; spanElements[currIdx].style.filter = ieOpacity; } }, /** this function takes care of redoing the rico cornering * * you can't just call updateRicoCorners() again and pass it a * new options string. you have to first remove the divs that * rico puts on top and below the content div. * * @param {DOM} theDiv - A child of the outer <div> that was * supplied to the `round` method. * * @param {Object} options - list of options */ reRound: function(theDiv, options) { var topRico = theDiv.parentNode.childNodes[0]; //theDiv would be theDiv.parentNode.childNodes[1] var bottomRico = theDiv.parentNode.childNodes[2]; theDiv.parentNode.removeChild(topRico); theDiv.parentNode.removeChild(bottomRico); this.round(theDiv.parentNode, options); }, _roundCornersImpl: function(e, color, bgColor) { if(this.options.border) { this._renderBorder(e,bgColor); } if(this._isTopRounded()) { this._roundTopCorners(e,color,bgColor); } if(this._isBottomRounded()) { this._roundBottomCorners(e,color,bgColor); } }, _renderBorder: function(el,bgColor) { var borderValue = "1px solid " + this._borderColor(bgColor); var borderL = "border-left: " + borderValue; var borderR = "border-right: " + borderValue; var style = "style='" + borderL + ";" + borderR + "'"; el.innerHTML = "<div " + style + ">" + el.innerHTML + "</div>"; }, _roundTopCorners: function(el, color, bgColor) { var corner = this._createCorner(bgColor); for(var i=0 ; i < this.options.numSlices ; i++ ) { corner.appendChild(this._createCornerSlice(color,bgColor,i,"top")); } el.style.paddingTop = 0; el.insertBefore(corner,el.firstChild); }, _roundBottomCorners: function(el, color, bgColor) { var corner = this._createCorner(bgColor); for(var i=(this.options.numSlices-1) ; i >= 0 ; i-- ) { corner.appendChild(this._createCornerSlice(color,bgColor,i,"bottom")); } el.style.paddingBottom = 0; el.appendChild(corner); }, _createCorner: function(bgColor) { var corner = document.createElement("div"); corner.style.backgroundColor = (this._isTransparent() ? "transparent" : bgColor); return corner; }, _createCornerSlice: function(color,bgColor, n, position) { var slice = document.createElement("span"); var inStyle = slice.style; inStyle.backgroundColor = color; inStyle.display = "block"; inStyle.height = "1px"; inStyle.overflow = "hidden"; inStyle.fontSize = "1px"; var borderColor = this._borderColor(color,bgColor); if ( this.options.border && n == 0 ) { inStyle.borderTopStyle = "solid"; inStyle.borderTopWidth = "1px"; inStyle.borderLeftWidth = "0px"; inStyle.borderRightWidth = "0px"; inStyle.borderBottomWidth = "0px"; inStyle.height = "0px"; // assumes css compliant box model inStyle.borderColor = borderColor; } else if(borderColor) { inStyle.borderColor = borderColor; inStyle.borderStyle = "solid"; inStyle.borderWidth = "0px 1px"; } if ( !this.options.compact && (n == (this.options.numSlices-1)) ) { inStyle.height = "2px"; } this._setMargin(slice, n, position); this._setBorder(slice, n, position); return slice; }, _setOptions: function(options) { this.options = { corners : "all", color : "fromElement", bgColor : "fromParent", blend : true, border : false, compact : false }; OpenLayers.Util.extend(this.options, options || {}); this.options.numSlices = this.options.compact ? 2 : 4; if ( this._isTransparent() ) { this.options.blend = false; } }, _whichSideTop: function() { if ( this._hasString(this.options.corners, "all", "top") ) { return ""; } if ( this.options.corners.indexOf("tl") >= 0 && this.options.corners.indexOf("tr") >= 0 ) { return ""; } if (this.options.corners.indexOf("tl") >= 0) { return "left"; } else if (this.options.corners.indexOf("tr") >= 0) { return "right"; } return ""; }, _whichSideBottom: function() { if ( this._hasString(this.options.corners, "all", "bottom") ) { return ""; } if ( this.options.corners.indexOf("bl")>=0 && this.options.corners.indexOf("br")>=0 ) { return ""; } if(this.options.corners.indexOf("bl") >=0) { return "left"; } else if(this.options.corners.indexOf("br")>=0) { return "right"; } return ""; }, _borderColor : function(color,bgColor) { if ( color == "transparent" ) { return bgColor; } else if ( this.options.border ) { return this.options.border; } else if ( this.options.blend ) { return this._blend( bgColor, color ); } else { return ""; } }, _setMargin: function(el, n, corners) { var marginSize = this._marginSize(n); var whichSide = corners == "top" ? this._whichSideTop() : this._whichSideBottom(); if ( whichSide == "left" ) { el.style.marginLeft = marginSize + "px"; el.style.marginRight = "0px"; } else if ( whichSide == "right" ) { el.style.marginRight = marginSize + "px"; el.style.marginLeft = "0px"; } else { el.style.marginLeft = marginSize + "px"; el.style.marginRight = marginSize + "px"; } }, _setBorder: function(el,n,corners) { var borderSize = this._borderSize(n); var whichSide = corners == "top" ? this._whichSideTop() : this._whichSideBottom(); if ( whichSide == "left" ) { el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = "0px"; } else if ( whichSide == "right" ) { el.style.borderRightWidth = borderSize + "px"; el.style.borderLeftWidth = "0px"; } else { el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = borderSize + "px"; } if (this.options.border != false) { el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = borderSize + "px"; } }, _marginSize: function(n) { if ( this._isTransparent() ) { return 0; } var marginSizes = [ 5, 3, 2, 1 ]; var blendedMarginSizes = [ 3, 2, 1, 0 ]; var compactMarginSizes = [ 2, 1 ]; var smBlendedMarginSizes = [ 1, 0 ]; if ( this.options.compact && this.options.blend ) { return smBlendedMarginSizes[n]; } else if ( this.options.compact ) { return compactMarginSizes[n]; } else if ( this.options.blend ) { return blendedMarginSizes[n]; } else { return marginSizes[n]; } }, _borderSize: function(n) { var transparentBorderSizes = [ 5, 3, 2, 1 ]; var blendedBorderSizes = [ 2, 1, 1, 1 ]; var compactBorderSizes = [ 1, 0 ]; var actualBorderSizes = [ 0, 2, 0, 0 ]; if ( this.options.compact && (this.options.blend || this._isTransparent()) ) { return 1; } else if ( this.options.compact ) { return compactBorderSizes[n]; } else if ( this.options.blend ) { return blendedBorderSizes[n]; } else if ( this.options.border ) { return actualBorderSizes[n]; } else if ( this._isTransparent() ) { return transparentBorderSizes[n]; } return 0; }, _hasString: function(str) { for(var i=1 ; i<arguments.length ; i++) if (str.indexOf(arguments[i]) >= 0) { return true; } return false; }, _blend: function(c1, c2) { var cc1 = OpenLayers.Rico.Color.createFromHex(c1); cc1.blend(OpenLayers.Rico.Color.createFromHex(c2)); return cc1; }, _background: function(el) { try { return OpenLayers.Rico.Color.createColorFromBackground(el).asHex(); } catch(err) { return "#ffffff"; } }, _isTransparent: function() { return this.options.color == "transparent"; }, _isTopRounded: function() { return this._hasString(this.options.corners, "all", "top", "tl", "tr"); }, _isBottomRounded: function() { return this._hasString(this.options.corners, "all", "bottom", "bl", "br"); }, _hasSingleTextChild: function(el) { return el.childNodes.length == 1 && el.childNodes[0].nodeType == 3; } };
Copyright © 2025 - UnknownSec