/* 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: ) */ /** * Contains XMLHttpRequest.js * Copyright 2007 Sergey Ilinsky (http://www.ilinsky.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 */ /** * 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 * * (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 -tag, * e.g.: * * (code) * * (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 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} A cached center location. This should not be * accessed directly. Use 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: * {} 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 - {} * * 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: * {} 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 object of the bounds. * * Returns: * {} The size of the bounds. */ getSize:function() { return new OpenLayers.Size(this.getWidth(), this.getHeight()); }, /** * APIMethod: getCenterPixel * Returns the object which represents the center of the * bounds. * * Returns: * {} 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 object which represents the center of the * bounds. * * Returns: * {} 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 - { or } * Default is center. * * Returns: * {} 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: * {} 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 , * or specified. * * Please note that this function assumes that left < right and * bottom < top. * * Parameters: * object - {, or * } 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 . * * Parameters: * ll - {|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 - {} 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 . * * Parameters: * px - {} * 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 - {} 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 - {} 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 . * * 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 * lies. * * Parameters: * 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 - {} Source projection. * dest - {} Destination projection. * * Returns: * {} 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 - {} * 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: * {} 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: * {} 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: * {} 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 - { or Object} or an object with * both 'w' and 'h' properties. * * Returns: * {} 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"lon=5,lat=42") */ toString:function() { return ("lon=" + this.lon + ",lat=" + this.lat); }, /** * APIMethod: toShortString * * Returns: * {String} Shortened String representation of OpenLayers.LonLat object. * (e.g. "5, 42") */ toShortString:function() { return (this.lon + ", " + this.lat); }, /** * APIMethod: clone * * Returns: * {} 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: * {} 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 - {} * * Returns: * {Boolean} Boolean value indicating whether the passed-in * 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 - {} Source projection. * dest - {} Destination projection. * * Returns: * {} 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 - {} * * Returns: * {} 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 from a * parameter string * * Parameters: * str - {String} Comma-separated Lon,Lat coordinate string. * (e.g. "5,40") * * Returns: * {} New 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 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: * {} New 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: * {} 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 - {|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 - {} * * 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: * {} 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 - {|Object} An OpenLayers.Pixel or an object with * a 'x' and 'y' properties. * * Returns: * {} 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. "w=55,h=66") */ toString:function() { return ("w=" + this.w + ",h=" + this.h); }, /** * APIMethod: clone * Create a clone of this size object * * Returns: * {} 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 - {|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 method to set this value and the 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 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 * dictionary exists for the code, the * 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 will be used to determine the appropriate * dictionary. Dictionaries are stored in . * * Parameters: * key - {String} The key for an i18n string value in the dictionary. * context - {Object} Optional context to be used with * . * * 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 . Looks up a key from a dictionary * based on the current language string. The value of * will be used to determine the appropriate * dictionary. Dictionaries are stored in . * * Parameters: * key - {String} The key for an i18n string value in the dictionary. * context - {Object} Optional context to be used with * . * * 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= 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 - {|Object} The element left and top position, * OpenLayers.Pixel or an object with * a 'x' and 'y' properties. * sz - {|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 - {|Object} The element left and top position, * OpenLayers.Pixel or an object with * a 'x' and 'y' properties. * sz - {|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 - {|Object} The element left and top position, * OpenLayers.Pixel or an object with * a 'x' and 'y' properties. * sz - {|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 - {|Object} OpenLayers.Pixel or an object with * a 'x' and 'y' properties. * sz - {|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 - {|Object} OpenLayers.Pixel or an object with * a 'x' and 'y' properties. * sz - {|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. "key1=value1&key2=value2&key3=value3") * 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} (or any object with both .lat, .lon properties) * p2 - {} (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 - {} (or any object with both .lat, .lon * properties) The start point. * brng - {Float} The bearing (degrees). * dist - {Float} The ground distance (meters). * * Returns: * {} 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 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 - {} 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.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= 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/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 * {} 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 * {} for more information on * custom transformations. */ externalProjection: null, /** * APIProperty: internalProjection * {} 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 * {} for more information on * custom transformations. */ internalProjection: null, /** * APIProperty: data * {Object} When is true, this is the parsed string sent to * . */ data: null, /** * APIProperty: keepData * {Object} Maintain a reference () 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 , the data property will be * set to the parsed object (e.g. the json or xml object). * * Returns: * An instance of OpenLayers.Format */ initialize: function(options) { OpenLayers.Util.extend(this, options); this.options = options; }, /** * APIMethod: destroy * Clean up. */ destroy: function() { }, /** * Method: read * Read data from a string, and return an object whose type depends on the * subclass. * * Parameters: * data - {string} Data to read/parse. * * Returns: * Depends on the subclass */ read: function(data) { throw new Error('Read not implemented.'); }, /** * Method: write * Accept an object, and return a string. * * Parameters: * object - {Object} Object to be serialized * * Returns: * {String} A string representation of the object. */ write: function(object) { throw new Error('Write not implemented.'); }, CLASS_NAME: "OpenLayers.Format" }); /* ====================================================================== OpenLayers/Format/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: * {} 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/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 * {} 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 * . */ 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 and * to change control state. */ active: null, /** * Property: handlerOptions * {Object} Used to set non-default properties on the control's handler */ handlerOptions: null, /** * Property: handler * {} null */ handler: null, /** * APIProperty: eventListeners * {Object} If set as an option at construction, the eventListeners * object will be registered with . Object * structure must be a listeners object as shown in the example for * the events.on method. */ eventListeners: null, /** * APIProperty: 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 - {} */ 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 - {} 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 - {} */ 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/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 namespace using * , and named after the event they provide. * The constructor receives the target instance as * argument. Extensions that need to capture browser events before they * propagate can register their listeners events using , 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 ), values are the * number of extension listeners for each event type. */ extensionCount: null, /** * Method: clearMouseListener * A version of that is bound to this instance so that * it can be used with and * . */ 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 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 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 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: * {} 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 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 . * * 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 * {} 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 * instances. * * Parameters: * target - {} The events instance that the buttonclick * event will be triggered on. */ initialize: function(target) { this.target = target; for (var i=this.events.length-1; i>=0; --i) { this.target.register(this.events[i], this, this.buttonClick, { extension: true }); } }, /** * Method: destroy */ destroy: function() { for (var i=this.events.length-1; i>=0; --i) { this.target.unregister(this.events[i], this, this.buttonClick); } delete this.target; }, /** * Method: getPressedButton * Get the pressed button, if any. Returns undefined if no button * was pressed. * * Arguments: * element - {DOMElement} The event target. * * Returns: * {DOMElement} The button element, or undefined. */ getPressedButton: function(element) { var depth = 3, // limit the search depth button; do { if(OpenLayers.Element.hasClass(element, "olButton")) { // hit! button = element; break; } element = element.parentNode; } while(--depth > 0 && element); return button; }, /** * Method: ignore * Check for event target elements that should be ignored by OpenLayers. * * Parameters: * element - {DOMElement} The event target. */ ignore: function(element) { var depth = 3, ignore = false; do { if (element.nodeName.toLowerCase() === 'a') { ignore = true; break; } element = element.parentNode; } while (--depth > 0 && element); return ignore; }, /** * Method: buttonClick * Check if a button was clicked, and fire the buttonclick event * * Parameters: * evt - {Event} */ buttonClick: function(evt) { var propagate = true, element = OpenLayers.Event.element(evt); if (element && (OpenLayers.Event.isLeftClick(evt) || !~evt.type.indexOf("mouse"))) { // was a button pressed? var button = this.getPressedButton(element); if (button) { if (evt.type === "keydown") { switch (evt.keyCode) { case OpenLayers.Event.KEY_RETURN: case OpenLayers.Event.KEY_SPACE: this.target.triggerEvent("buttonclick", { buttonElement: button }); OpenLayers.Event.stop(evt); propagate = false; break; } } else if (this.startEvt) { if (this.completeRegEx.test(evt.type)) { var pos = OpenLayers.Util.pagePosition(button); var viewportElement = OpenLayers.Util.getViewportElement(); var scrollTop = window.pageYOffset || viewportElement.scrollTop; var scrollLeft = window.pageXOffset || viewportElement.scrollLeft; pos[0] = pos[0] - scrollLeft; pos[1] = pos[1] - scrollTop; this.target.triggerEvent("buttonclick", { buttonElement: button, buttonXY: { x: this.startEvt.clientX - pos[0], y: this.startEvt.clientY - pos[1] } }); } if (this.cancelRegEx.test(evt.type)) { delete this.startEvt; } OpenLayers.Event.stop(evt); propagate = false; } if (this.startRegEx.test(evt.type)) { this.startEvt = evt; OpenLayers.Event.stop(evt); propagate = false; } } else { propagate = !this.ignore(OpenLayers.Event.element(evt)); delete this.startEvt; } } return propagate; } }); /* ====================================================================== OpenLayers/Util/vendorPrefix.js ====================================================================== */ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for * full list of contributors). Published under the 2-clause BSD license. * See license.txt in the OpenLayers distribution or repository for the * full text of the license. */ /** * @requires OpenLayers/SingleFile.js */ OpenLayers.Util = OpenLayers.Util || {}; /** * Namespace: OpenLayers.Util.vendorPrefix * A collection of utility functions to detect vendor prefixed features */ OpenLayers.Util.vendorPrefix = (function() { "use strict"; var VENDOR_PREFIXES = ["", "O", "ms", "Moz", "Webkit"], divStyle = document.createElement("div").style, cssCache = {}, jsCache = {}; /** * Function: domToCss * Converts a upper camel case DOM style property name to a CSS property * i.e. transformOrigin -> transform-origin * or WebkitTransformOrigin -> -webkit-transform-origin * * Parameters: * prefixedDom - {String} The property to convert * * Returns: * {String} The CSS property */ function domToCss(prefixedDom) { if (!prefixedDom) { return null; } return prefixedDom. replace(/([A-Z])/g, function(c) { return "-" + c.toLowerCase(); }). replace(/^ms-/, "-ms-"); } /** * APIMethod: css * Detect which property is used for a CSS property * * Parameters: * property - {String} The standard (unprefixed) CSS property name * * Returns: * {String} The standard CSS property, prefixed property or null if not * supported */ function css(property) { if (cssCache[property] === undefined) { var domProperty = property. replace(/(-[\s\S])/g, function(c) { return c.charAt(1).toUpperCase(); }); var prefixedDom = style(domProperty); cssCache[property] = domToCss(prefixedDom); } return cssCache[property]; } /** * APIMethod: js * Detect which property is used for a JS property/method * * Parameters: * obj - {Object} The object to test on * property - {String} The standard (unprefixed) JS property name * * Returns: * {String} The standard JS property, prefixed property or null if not * supported */ function js(obj, property) { if (jsCache[property] === undefined) { var tmpProp, i = 0, l = VENDOR_PREFIXES.length, prefix, isStyleObj = (typeof obj.cssText !== "undefined"); jsCache[property] = null; for(; i 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 * . */ 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 . * * Parameters: * id - {Number} Identifier returned from . */ 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 * {(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 - {(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, */ OpenLayers.Easing = { /** * Create empty functions for all easing methods. */ CLASS_NAME: "OpenLayers.Easing" }; /** * Namespace: OpenLayers.Easing.Linear */ OpenLayers.Easing.Linear = { /** * Function: easeIn * * Parameters: * t - {Float} time * b - {Float} beginning position * c - {Float} total change * d - {Float} duration of the transition * * Returns: * {Float} */ easeIn: function(t, b, c, d) { return c*t/d + b; }, /** * Function: easeOut * * Parameters: * t - {Float} time * b - {Float} beginning position * c - {Float} total change * d - {Float} duration of the transition * * Returns: * {Float} */ easeOut: function(t, b, c, d) { return c*t/d + b; }, /** * Function: easeInOut * * Parameters: * t - {Float} time * b - {Float} beginning position * c - {Float} total change * d - {Float} duration of the transition * * Returns: * {Float} */ easeInOut: function(t, b, c, d) { return c*t/d + b; }, CLASS_NAME: "OpenLayers.Easing.Linear" }; /** * Namespace: OpenLayers.Easing.Expo */ OpenLayers.Easing.Expo = { /** * Function: easeIn * * Parameters: * t - {Float} time * b - {Float} beginning position * c - {Float} total change * d - {Float} duration of the transition * * Returns: * {Float} */ easeIn: function(t, b, c, d) { return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b; }, /** * Function: easeOut * * Parameters: * t - {Float} time * b - {Float} beginning position * c - {Float} total change * d - {Float} duration of the transition * * Returns: * {Float} */ easeOut: function(t, b, c, d) { return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b; }, /** * Function: easeInOut * * Parameters: * t - {Float} time * b - {Float} beginning position * c - {Float} total change * d - {Float} duration of the transition * * Returns: * {Float} */ easeInOut: function(t, b, c, d) { if (t==0) return b; if (t==d) return b+c; if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b; return c/2 * (-Math.pow(2, -10 * --t) + 2) + b; }, CLASS_NAME: "OpenLayers.Easing.Expo" }; /** * Namespace: OpenLayers.Easing.Quad */ OpenLayers.Easing.Quad = { /** * Function: easeIn * * Parameters: * t - {Float} time * b - {Float} beginning position * c - {Float} total change * d - {Float} duration of the transition * * Returns: * {Float} */ easeIn: function(t, b, c, d) { return c*(t/=d)*t + b; }, /** * Function: easeOut * * Parameters: * t - {Float} time * b - {Float} beginning position * c - {Float} total change * d - {Float} duration of the transition * * Returns: * {Float} */ easeOut: function(t, b, c, d) { return -c *(t/=d)*(t-2) + b; }, /** * Function: easeInOut * * Parameters: * t - {Float} time * b - {Float} beginning position * c - {Float} total change * d - {Float} duration of the transition * * Returns: * {Float} */ easeInOut: function(t, b, c, d) { if ((t/=d/2) < 1) return c/2*t*t + b; return -c/2 * ((--t)*(t-2) - 1) + b; }, CLASS_NAME: "OpenLayers.Easing.Quad" }; /* ====================================================================== OpenLayers/Projection.js ====================================================================== */ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for * full list of contributors). Published under the 2-clause BSD license. * See license.txt in the OpenLayers distribution or repository for the * full text of the license. */ /** * @requires OpenLayers/BaseTypes/Class.js * @requires OpenLayers/Util.js */ /** * Namespace: OpenLayers.Projection * Methods for coordinate transforms between coordinate systems. By default, * OpenLayers ships with the ability to transform coordinates between * geographic (EPSG:4326) and web or spherical mercator (EPSG:900913 et al.) * coordinate reference systems. See the method for details * on usage. * * Additional transforms may be added by using the * library. If the proj4js library is included, the 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 * 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: * {} 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 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 - { | 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=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 constructor. * * On their own maps do not provide much functionality. To extend a map * it's necessary to add controls () and * layers () 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 * {} * * 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 - {} 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 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 * instead of layer.resolutions[zoom] as the * former works for non-integer zoom levels. */ fractionalZoom: false, /** * APIProperty: 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 or 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 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 method. * * Note: * If you are calling after map construction, do not use * auto. Instead, divide your by your * maximum expected dimension. */ div: null, /** * Property: dragging * {Boolean} The map is currently being dragged. */ dragging: false, /** * Property: size * {} Size of the main div (this.div) */ size: null, /** * Property: viewPortDiv * {HTMLDivElement} The element that represents the map viewport */ viewPortDiv: null, /** * Property: layerContainerOrigin * {} 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()} Ordered list of layers in the map */ layers: null, /** * APIProperty: controls * {Array()} 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: * - or * - or * - * - */ controls: null, /** * Property: popups * {Array()} List of popups associated with the map */ popups: null, /** * APIProperty: baseLayer * {} The currently selected base layer. This determines * min/max zoom level, projection, etc. */ baseLayer: null, /** * Property: center * {} 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 * {} 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 and ). */ 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 . */ maxResolution: null, /** * APIProperty: minResolution * {Float} */ minResolution: null, /** * APIProperty: maxScale * {Float} */ maxScale: null, /** * APIProperty: minScale * {Float} */ minScale: null, /** * APIProperty: maxExtent * {|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 instead. * The value for will change calculations for tile URLs. */ maxExtent: null, /** * APIProperty: minExtent * {|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 * {|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 * {} 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 * {|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 {}. */ /** * 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 . Object * structure must be a listeners object as shown in the example for * the events.on method. */ eventListeners: null, /** * Property: panTween * {} 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 * {} 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 * {} 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
option is * provided or if you intend to call the method later. * options - {Object} Optional object with properties to tag onto the map. * * Valid options (in addition to the listed API properties): * center - {|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 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 * 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 - {|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
and 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=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: * {} */ 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()} 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()} 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()} 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()} 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()} 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: * {} 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} * 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} * * 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()} */ addLayers: function (layers) { for (var i=0, len=layers.length; i} * 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} * * 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 - {} * 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} * 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 - {} */ 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 - {} * px - {} */ 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()} * pixels - {Array()} */ addControls: function (controls, pixels) { var pxs = (arguments.length === 1) ? [] : pixels; for (var i=0, len=controls.length; i} * px - {} */ 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: * {} 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} 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 - {} * 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 - {} */ 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: * {} An 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} A new 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 - {} Default is this.getCenter() * resolution - {float} Default is this.getResolution() * * Returns: * {} 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: * {} */ getCenter: function () { var center = null; var cachedCenter = this.getCachedCenter(); if (cachedCenter) { center = cachedCenter.clone(); } return center; }, /** * Method: getCachedCenter * * Returns: * {} */ 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 - {} */ 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 - {|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'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 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 * 's maxExtent. This is an Integer value, unless the map is * configured with set to true. */ getMinZoom: function() { return this.adjustZoom(0); }, /** * Method: moveTo * * Parameters: * 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} */ 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 - {} * * 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: * {} 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: * {} 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: * {} 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 - {} * 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 * without a lonlat argument. * * Parameters: * zoom - {Integer} */ zoomTo: function(zoom, xy) { // non-API arguments: // xy - {} 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 - {|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 - {|Object} An OpenLayers.Pixel or * an object with a 'x' * and 'y' properties. * * Returns: * {} An OpenLayers.LonLat which is the passed-in view * port , 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 - {} * * Returns: * {} An OpenLayers.Pixel which is the passed-in * , 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 - {} The zoom origin pixel location on the screen * resolution - {Float} The resolution we want to get the center for * * Returns: * {} 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 - {|Object} An OpenLayers.Pixel or an object with * a 'x' and 'y' properties. * * Returns: * {} 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 - {} A map location. * * Returns: * {} An OpenLayers.Pixel corresponding to the * 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 - {} The pixel to get the geodesic length for. If * not provided, the center pixel of the map viewport will be used. * * Returns: * {} 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 - {} * * Returns: * {} 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 - {} * * Returns: * {} 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 - {} * * Returns: * {} */ getLonLatFromLayerPx: function (px) { //adjust for displacement of layerContainerDiv px = this.getViewPortPxFromLayerPx(px); return this.getLonLatFromViewPortPx(px); }, /** * APIMethod: getLayerPxFromLonLat * * Parameters: * lonlat - {} lonlat * * Returns: * {} An OpenLayers.Pixel which is the passed-in * , 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 . 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 * y - {Number} y parameter for the translation. Defaults to the y value of * the map's * 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/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 * {}. 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 * {} */ 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 * . 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 - {} 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 is set, this always * returns true. If a 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 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, returns false if any key is down. */ OpenLayers.Handler.MOD_NONE = 0; /** * Constant: OpenLayers.Handler.MOD_SHIFT * If set as the , returns false if Shift is down. */ OpenLayers.Handler.MOD_SHIFT = 1; /** * Constant: OpenLayers.Handler.MOD_CTRL * If set as the , returns false if Ctrl is down. */ OpenLayers.Handler.MOD_CTRL = 2; /** * Constant: OpenLayers.Handler.MOD_ALT * If set as the , returns false if Alt is down. */ OpenLayers.Handler.MOD_ALT = 4; /** * Constant: OpenLayers.Handler.MOD_META * If set as the , returns false if Cmd is down. */ OpenLayers.Handler.MOD_META = 8; /* ====================================================================== 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 , controls can also ignore clicks * that include a drag. Create a new instance with the * constructor. * * Inherits from: * - */ 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 . */ 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 * . */ rightclickTimerId: null, /** * Constructor: OpenLayers.Handler.Click * Create a new click handler. * * Parameters: * 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 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 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 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 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 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 constructor. * * Inherits from: * - */ 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 * {} The last pixel location of the drag. */ last: null, /** * Property: start * {} 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 - {} 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/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 * constructor. * * Inherits from: * - */ OpenLayers.Control.OverviewMap = OpenLayers.Class(OpenLayers.Control, { /** * Property: element * {DOMElement} The DOM element that contains the overview map */ element: null, /** * APIProperty: ovmap * {} A reference to the overview map itself. */ ovmap: null, /** * APIProperty: 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()} 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 * property. Default is 15 pixels. */ minRectSize: 15, /** * APIProperty: minRectDisplayClass * {String} Replacement style class name for the extent rectangle when * 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 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 - {} 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 - {} 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 - {} */ 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 - {} */ 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 if 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 - {} */ 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 - {} * * Returns: * {}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 - {} * * Returns: * {} 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 - {|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 - {|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/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 * {} * * 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 ). 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 * {} 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 * 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 whenever the zoom * changes. */ inRange: false, /** * Propery: imageSize * {} 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 . 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 * {} or {} 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 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 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 * {|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 * is set to false, data will not be * requested that falls completely outside of these bounds. */ maxExtent: null, /** * APIProperty: minExtent * {|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 * 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 - {} The layer to be cloned * * Returns: * {} An exact clone of this */ 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 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 - {} * 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 - {} */ 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 - {} */ removeMap: function(map) { //to be overridden by subclasses }, /** * APIMethod: getImageSize * * Parameters: * 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: * {} 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 - {} */ 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} 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 - {} * 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: * {} */ 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= 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 minDiff) { break; } minDiff = diff; } else { if (this.resolutions[i] < resolution) { break; } } } zoom = Math.max(0, i-1); } return zoom; }, /** * APIMethod: getLonLatFromViewPortPx * * Parameters: * viewPortPx - {|Object} An OpenLayers.Pixel or * an object with a 'x' * and 'y' properties. * * Returns: * {} An OpenLayers.LonLat which is the passed-in * view port , 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 - {|Object} An OpenLayers.LonLat or * an object with a 'lon' * and 'lat' properties. * * Returns: * {} An 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 - {} */ adjustBounds: function (bounds) { if (this.gutter) { // Adjust the extent of a bounds in map units by the // layer's gutter in pixels. var mapGutter = this.gutter * this.map.getResolution(); bounds = new OpenLayers.Bounds(bounds.left - mapGutter, bounds.bottom - mapGutter, bounds.right + mapGutter, bounds.top + mapGutter); } if (this.wrapDateLine) { // wrap around the date line, within the limits of rounding error var wrappingOptions = { 'rightTolerance':this.getResolution(), 'leftTolerance':this.getResolution() }; bounds = bounds.wrapDateLine(this.maxExtent, wrappingOptions); } return bounds; }, CLASS_NAME: "OpenLayers.Layer" }); /* ====================================================================== OpenLayers/Layer/SphericalMercator.js ====================================================================== */ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for * full list of contributors). Published under the 2-clause BSD license. * See license.txt in the OpenLayers distribution or repository for the * full text of the license. */ /** * @requires OpenLayers/Layer.js * @requires OpenLayers/Projection.js */ /** * Class: OpenLayers.Layer.SphericalMercator * A mixin for layers that wraps up the pieces neccesary to have a coordinate * conversion for working with commercial APIs which use a spherical * mercator projection. Using this layer as a base layer, additional * layers can be used as overlays if they are in the same projection. * * A layer is given properties of this object by setting the sphericalMercator * property to true. * * More projection information: * - http://spatialreference.org/ref/user/google-projection/ * * Proj4 Text: * +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 * +k=1.0 +units=m +nadgrids=@null +no_defs * * WKT: * 900913=PROJCS["WGS84 / Simple Mercator", GEOGCS["WGS 84", * DATUM["WGS_1984", SPHEROID["WGS_1984", 6378137.0, 298.257223563]], * PRIMEM["Greenwich", 0.0], UNIT["degree", 0.017453292519943295], * AXIS["Longitude", EAST], AXIS["Latitude", NORTH]], * PROJECTION["Mercator_1SP_Google"], * PARAMETER["latitude_of_origin", 0.0], PARAMETER["central_meridian", 0.0], * PARAMETER["scale_factor", 1.0], PARAMETER["false_easting", 0.0], * PARAMETER["false_northing", 0.0], UNIT["m", 1.0], AXIS["x", EAST], * AXIS["y", NORTH], AUTHORITY["EPSG","900913"]] */ OpenLayers.Layer.SphericalMercator = { /** * Method: getExtent * Get the map's extent. * * Returns: * {} 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 - {} * * Returns: * {} 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 - {} * * Returns: * {} 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: * {} 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: * {} The coordinates transformed to EPSG:4326. */ inverseMercator: (function() { var gg = new OpenLayers.Projection("EPSG:4326"); var sm = new OpenLayers.Projection("EPSG:900913"); return function(x, y) { var point = OpenLayers.Projection.transform({x: x, y: y}, sm, gg); return new OpenLayers.LonLat(point.x, point.y); }; })() }; /* ====================================================================== OpenLayers/Layer/EventPane.js ====================================================================== */ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for * full list of contributors). Published under the 2-clause BSD license. * See license.txt in the OpenLayers distribution or repository for the * full text of the license. */ /** * @requires OpenLayers/Layer.js * @requires OpenLayers/Util.js */ /** * Class: OpenLayers.Layer.EventPane * Base class for 3rd party layers, providing a DOM element which isolates * the 3rd-party layer from mouse events. * Only used by Google layers. * * Automatically instantiated by the Google constructor, and not usually instantiated directly. * * Create a new event pane layer with the * constructor. * * Inherits from: * - */ 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 - {} */ 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 - {} */ 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 - {} * 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 - {} * * Returns: * {} 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 - {} * * Returns: * {} 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: * {} 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 - {} * * 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: * {} 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 - {} * * Returns: * {Object} A MapObject Pixel, translated from the passed in * OpenLayers.Pixel * Returns null if null value is passed in */ getMapObjectPixelFromOLPixel: function(olPixel) { var moPixel = null; if (olPixel != null) { moPixel = this.getMapObjectPixelFromXY(olPixel.x, olPixel.y); } return moPixel; }, CLASS_NAME: "OpenLayers.Layer.EventPane" }); /* ====================================================================== OpenLayers/Layer/FixedZoomLevels.js ====================================================================== */ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for * full list of contributors). Published under the 2-clause BSD license. * See license.txt in the OpenLayers distribution or repository for the * full text of the license. */ /** * @requires OpenLayers/Layer.js */ /** * Class: OpenLayers.Layer.FixedZoomLevels * Some Layers will already have established zoom levels (like google * or ve). Instead of trying to determine them and populate a resolutions[] * Array with those values, we will hijack the resolution functionality * here. * * When you subclass FixedZoomLevels: * * The initResolutions() call gets nullified, meaning no resolutions[] array * is set up. Which would be a big problem getResolution() in Layer, since * it merely takes map.zoom and indexes into resolutions[]... but.... * * The getResolution() call is also overridden. Instead of using the * resolutions[] array, we simply calculate the current resolution based * on the current extent and the current map size. But how will we be able * to calculate the current extent without knowing the resolution...? * * The getExtent() function is also overridden. Instead of calculating extent * based on the center point and the current resolution, we instead * calculate the extent by getting the lonlats at the top-left and * bottom-right by using the getLonLatFromViewPortPx() translation function, * taken from the pixel locations (0,0) and the size of the map. But how * will we be able to do lonlat-px translation without resolution....? * * The getZoomForResolution() method is overridden. Instead of indexing into * the resolutions[] array, we call OpenLayers.Layer.getExent(), passing in * the desired resolution. With this extent, we then call getZoomForExtent() * * * Whenever you implement a layer using OpenLayers.Layer.FixedZoomLevels, * it is your responsibility to provide the following three functions: * * - getLonLatFromViewPortPx * - getViewPortPxFromLonLat * - getZoomForExtent * * ...those three functions should generally be provided by any reasonable * API that you might be working from. * */ OpenLayers.Layer.FixedZoomLevels = OpenLayers.Class({ /********************************************************/ /* */ /* Baselayer Functions */ /* */ /* The following functions must all be implemented */ /* by all base layers */ /* */ /********************************************************/ /** * Constructor: OpenLayers.Layer.FixedZoomLevels * Create a new fixed zoom levels layer. */ initialize: function() { //this class is only just to add the following functions... // nothing to actually do here... but it is probably a good // idea to have layers that use these functions call this // inititalize() anyways, in case at some point we decide we // do want to put some functionality or state in here. }, /** * Method: initResolutions * Populate the resolutions array */ initResolutions: function() { var props = ['minZoomLevel', 'maxZoomLevel', 'numZoomLevels']; for(var i=0, len=props.length; i lonlat translation functions on tl and br * corners of viewport * * Returns: * {} A Bounds object which represents the lon/lat * bounds of the current viewPort. */ getExtent: function () { var size = this.map.getSize(); var tl = this.getLonLatFromViewPortPx({ x: 0, y: 0 }); var br = this.getLonLatFromViewPortPx({ x: size.w, y: size.h }); if ((tl != null) && (br != null)) { return new OpenLayers.Bounds(tl.lon, br.lat, br.lon, tl.lat); } else { return null; } }, /** * Method: getZoomForResolution * Get the zoom level for a given resolution * * Parameters: * resolution - {Float} * * Returns: * {Integer} A suitable zoom level for the specified resolution. * If no baselayer is set, returns null. */ getZoomForResolution: function(resolution) { if (this.resolutions != null) { return OpenLayers.Layer.prototype.getZoomForResolution.apply(this, arguments); } else { var extent = OpenLayers.Layer.prototype.getExtent.apply(this, []); return this.getZoomForExtent(extent); } }, /********************************************************/ /* */ /* Translation Functions */ /* */ /* The following functions translate GMaps and OL */ /* formats for Pixel, LonLat, Bounds, and Zoom */ /* */ /********************************************************/ // // TRANSLATION: MapObject Zoom <-> OpenLayers Zoom // /** * Method: getOLZoomFromMapObjectZoom * Get the OL zoom index from the map object zoom level * * Parameters: * moZoom - {Integer} * * Returns: * {Integer} An OpenLayers Zoom level, translated from the passed in zoom * Returns null if null value is passed in */ getOLZoomFromMapObjectZoom: function(moZoom) { var zoom = null; if (moZoom != null) { zoom = moZoom - this.minZoomLevel; if (this.map.baseLayer !== this) { zoom = this.map.baseLayer.getZoomForResolution( this.getResolutionForZoom(zoom) ); } } return zoom; }, /** * Method: getMapObjectZoomFromOLZoom * Get the map object zoom level from the OL zoom level * * Parameters: * olZoom - {Integer} * * Returns: * {Integer} A MapObject level, translated from the passed in olZoom * Returns null if null value is passed in */ getMapObjectZoomFromOLZoom: function(olZoom) { var zoom = null; if (olZoom != null) { zoom = olZoom + this.minZoomLevel; if (this.map.baseLayer !== this) { zoom = this.getZoomForResolution( this.map.baseLayer.getResolutionForZoom(zoom) ); } } return zoom; }, CLASS_NAME: "OpenLayers.Layer.FixedZoomLevels" }); /* ====================================================================== OpenLayers/Layer/Google.js ====================================================================== */ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for * full list of contributors). Published under the 2-clause BSD license. * See license.txt in the OpenLayers distribution or repository for the * full text of the license. */ /** * @requires OpenLayers/Layer/SphericalMercator.js * @requires OpenLayers/Layer/EventPane.js * @requires OpenLayers/Layer/FixedZoomLevels.js * @requires OpenLayers/Lang.js */ /** * Class: OpenLayers.Layer.Google * * Provides a wrapper for Google's Maps API * Normally the Terms of Use for this API do not allow wrapping, but Google * have provided written consent to OpenLayers for this - see email in * http://osgeo-org.1560.n6.nabble.com/Google-Maps-API-Terms-of-Use-changes-tp4910013p4911981.html * * Inherits from: * - * - * - */ OpenLayers.Layer.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 * 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: * {} 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 - {} * 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 - {} */ 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: * {} An , 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.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 - {} * * 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/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 document. * The DOM creation and traversing methods exposed here all mimic the * W3C XML DOM methods. Create a new parser with the * constructor. * * Inherits from: * - */ 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 * 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 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 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 document * 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 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), 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 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 on the instance. On other browsers, this will * either return an existing or create a new shared document (see * ). * * 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/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: * {} 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/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 * {} */ layer: null, /** * Property: id * {String} */ id: null, /** * Property: lonlat * {} */ lonlat: null, /** * Property: data * {Object} */ data: null, /** * Property: marker * {} */ marker: null, /** * APIProperty: popupClass * {} The class which will be used to instantiate * a new Popup. Default is . */ popupClass: null, /** * Property: popup * {} */ popup: null, /** * Constructor: OpenLayers.Feature * Constructor for features. * * Parameters: * layer - {} * lonlat - {} * data - {Object} * * Returns: * {} */ 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: * {} 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: * {} 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 . * */ 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 * objects. * * Inherits from: * - */ OpenLayers.Feature.Vector = OpenLayers.Class(OpenLayers.Feature, { /** * Property: fid * {String} */ fid: null, /** * APIProperty: geometry * {} */ geometry: null, /** * APIProperty: attributes * {Object} This object holds arbitrary, serializable properties that * describe the feature. */ attributes: null, /** * Property: bounds * {} The box bounding that feature's geometry, that * property can be set by an 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 * {} 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 , and written by * , 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 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 - {} The geometry that this feature * represents. * attributes - {Object} An optional object that will be mapped to the * 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: * {} 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: * {} 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: * {} For now just returns null */ createPopup: function() { return null; }, /** * Method: atPoint * Determins whether the feature intersects with the specified location. * * Parameters: * 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 - { or } the * location to which to move the feature. */ move: function(location) { if(!this.layer || !this.geometry.move){ //do nothing if no layer or immoveable geometry return undefined; } var pixel; if (location.CLASS_NAME == "OpenLayers.LonLat") { pixel = this.layer.getViewPortPxFromLonLat(location); } else { pixel = location; } var lastPixel = this.layer.getViewPortPxFromLonLat(this.geometry.getBounds().getCenterLonLat()); var res = this.layer.map.getResolution(); this.geometry.move(res * (pixel.x - lastPixel.x), res * (lastPixel.y - pixel.y)); this.layer.drawFeature(this); return lastPixel; }, /** * Method: toState * Sets the new state * * Parameters: * state - {String} */ toState: function(state) { if (state == OpenLayers.State.UPDATE) { switch (this.state) { case OpenLayers.State.UNKNOWN: case OpenLayers.State.DELETE: this.state = state; break; case OpenLayers.State.UPDATE: case OpenLayers.State.INSERT: break; } } else if (state == OpenLayers.State.INSERT) { switch (this.state) { case OpenLayers.State.UNKNOWN: break; default: this.state = state; break; } } else if (state == OpenLayers.State.DELETE) { switch (this.state) { case OpenLayers.State.INSERT: // the feature should be destroyed break; case OpenLayers.State.DELETE: break; case OpenLayers.State.UNKNOWN: case OpenLayers.State.UPDATE: this.state = state; break; } } else if (state == OpenLayers.State.UNKNOWN) { this.state = state; } }, CLASS_NAME: "OpenLayers.Feature.Vector" }); /** * Constant: OpenLayers.Feature.Vector.style * OpenLayers features can have a number of style attributes. The 'default' * style will typically be used if no other style is specified. These * styles correspond for the most part, to the styling properties defined * by the SVG standard. * Information on fill properties: http://www.w3.org/TR/SVG/painting.html#FillProperties * Information on stroke properties: http://www.w3.org/TR/SVG/painting.html#StrokeProperties * * Symbolizer properties: * fill - {Boolean} Set to false if no fill is desired. * fillColor - {String} Hex fill color. Default is "#ee9900". * fillOpacity - {Number} Fill opacity (0-1). Default is 0.4 * stroke - {Boolean} Set to false if no stroke is desired. * strokeColor - {String} Hex stroke color. Default is "#ee9900". * strokeOpacity - {Number} Stroke opacity (0-1). Default is 1. * strokeWidth - {Number} Pixel stroke width. Default is 1. * strokeLinecap - {String} Stroke cap type. Default is "round". [butt | round | square] * strokeDashstyle - {String} Stroke dash style. Default is "solid". [dot | dash | dashdot | longdash | longdashdot | solid] * graphic - {Boolean} Set to false if no graphic is desired. * pointRadius - {Number} Pixel point radius. Default is 6. * pointerEvents - {String} Default is "visiblePainted". * cursor - {String} Default is "". * externalGraphic - {String} Url to an external graphic that will be used for rendering points. * graphicWidth - {Number} Pixel width for sizing an external graphic. * graphicHeight - {Number} Pixel height for sizing an external graphic. * graphicOpacity - {Number} Opacity (0-1) for an external graphic. * graphicXOffset - {Number} Pixel offset along the positive x axis for displacing an external graphic. * graphicYOffset - {Number} Pixel offset along the positive y axis for displacing an external graphic. * rotation - {Number} For point symbolizers, this is the rotation of a graphic in the clockwise direction about its center point (or any point off center as specified by graphicXOffset and graphicYOffset). * graphicZIndex - {Number} The integer z-index value to use in rendering. * graphicName - {String} Named graphic to use when rendering points. Supported values include "circle" (default), * "square", "star", "x", "cross", "triangle". * graphicTitle - {String} Tooltip when hovering over a feature. *deprecated*, use title instead * title - {String} Tooltip when hovering over a feature. Not supported by the canvas renderer. * backgroundGraphic - {String} Url to a graphic to be used as the background under an externalGraphic. * backgroundGraphicZIndex - {Number} The integer z-index value to use in rendering the background graphic. * backgroundXOffset - {Number} The x offset (in pixels) for the background graphic. * backgroundYOffset - {Number} The y offset (in pixels) for the background graphic. * backgroundHeight - {Number} The height of the background graphic. If not provided, the graphicHeight will be used. * backgroundWidth - {Number} The width of the background width. If not provided, the graphicWidth will be used. * label - {String} The text for an optional label. For browsers that use the canvas renderer, this requires either * fillText or mozDrawText to be available. * labelAlign - {String} Label alignment. This specifies the insertion point relative to the text. It is a string * composed of two characters. The first character is for the horizontal alignment, the second for the vertical * alignment. Valid values for horizontal alignment: "l"=left, "c"=center, "r"=right. Valid values for vertical * alignment: "t"=top, "m"=middle, "b"=bottom. Example values: "lt", "cm", "rb". Default is "cm". * labelXOffset - {Number} Pixel offset along the positive x axis for displacing the label. Not supported by the canvas renderer. * labelYOffset - {Number} Pixel offset along the positive y axis for displacing the label. Not supported by the canvas renderer. * labelSelect - {Boolean} If set to true, labels will be selectable using SelectFeature or similar controls. * Default is false. * labelOutlineColor - {String} The color of the label outline. Default is 'white'. Only supported by the canvas & SVG renderers. * labelOutlineWidth - {Number} The width of the label outline. Default is 3, set to 0 or null to disable. Only supported by the SVG renderers. * labelOutlineOpacity - {Number} The opacity (0-1) of the label outline. Default is fontOpacity. Only supported by the canvas & SVG renderers. * fontColor - {String} The font color for the label, to be provided like CSS. * fontOpacity - {Number} Opacity (0-1) for the label * fontFamily - {String} The font family for the label, to be provided like in CSS. * fontSize - {String} The font size for the label, to be provided like in CSS. * fontStyle - {String} The font style for the label, to be provided like in CSS. * fontWeight - {String} The font weight for the label, to be provided like in CSS. * display - {String} Symbolizers will have no effect if display is set to "none". All other values have no effect. */ OpenLayers.Feature.Vector.style = { 'default': { fillColor: "#ee9900", fillOpacity: 0.4, hoverFillColor: "white", hoverFillOpacity: 0.8, strokeColor: "#ee9900", strokeOpacity: 1, strokeWidth: 1, strokeLinecap: "round", strokeDashstyle: "solid", hoverStrokeColor: "red", hoverStrokeOpacity: 1, hoverStrokeWidth: 0.2, pointRadius: 6, hoverPointRadius: 1, hoverPointUnit: "%", pointerEvents: "visiblePainted", cursor: "inherit", fontColor: "#000000", labelAlign: "cm", labelOutlineColor: "white", labelOutlineWidth: 3 }, 'select': { fillColor: "blue", fillOpacity: 0.4, hoverFillColor: "white", hoverFillOpacity: 0.8, strokeColor: "blue", strokeOpacity: 1, strokeWidth: 2, strokeLinecap: "round", strokeDashstyle: "solid", hoverStrokeColor: "red", hoverStrokeOpacity: 1, hoverStrokeWidth: 0.2, pointRadius: 6, hoverPointRadius: 1, hoverPointUnit: "%", pointerEvents: "visiblePainted", cursor: "pointer", fontColor: "#000000", labelAlign: "cm", labelOutlineColor: "white", labelOutlineWidth: 3 }, 'temporary': { fillColor: "#66cccc", fillOpacity: 0.2, hoverFillColor: "white", hoverFillOpacity: 0.8, strokeColor: "#66cccc", strokeOpacity: 1, strokeLinecap: "round", strokeWidth: 2, strokeDashstyle: "solid", hoverStrokeColor: "red", hoverStrokeOpacity: 1, hoverStrokeWidth: 0.2, pointRadius: 6, hoverPointRadius: 1, hoverPointUnit: "%", pointerEvents: "visiblePainted", cursor: "inherit", fontColor: "#000000", labelAlign: "cm", labelOutlineColor: "white", labelOutlineWidth: 3 }, 'delete': { display: "none" } }; /* ====================================================================== OpenLayers/Style.js ====================================================================== */ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for * full list of contributors). Published under the 2-clause BSD license. * See license.txt in the OpenLayers distribution or repository for the * full text of the license. */ /** * @requires OpenLayers/BaseTypes/Class.js * @requires OpenLayers/Util.js * @requires OpenLayers/Feature/Vector.js */ /** * Class: OpenLayers.Style * This class represents a UserStyle obtained * from a SLD, containing styling rules. */ OpenLayers.Style = OpenLayers.Class({ /** * Property: id * {String} A unique id for this session. */ id: null, /** * APIProperty: name * {String} */ name: null, /** * Property: title * {String} Title of this style (set if included in SLD) */ title: null, /** * Property: description * {String} Description of this style (set if abstract is included in SLD) */ description: null, /** * APIProperty: layerName * {} 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()} */ 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 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 will extend the symbolizer * of every rule. Properties of the 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()} List of rules to be added to the * style. * * Returns: * {} */ 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} 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 0) { appliedRules = true; for(var i=0, len=elseRules.length; i 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 - {} * style - {Object} * feature - {} * * 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 * . * * 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)} */ 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 - {} * * Returns: * {String} key of the according symbolizer */ getSymbolizerPrefix: function(geometry) { var prefixes = OpenLayers.Style.SYMBOLIZER_PREFIXES; for (var i=0, len=prefixes.length; i} 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} optional feature to pass to * 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: * {} */ 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: * {} Clone of this filter. */ clone: function() { return null; }, /** * APIMethod: toString * * Returns: * {String} Include 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.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 * { || } 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: * {} */ /** * Method: evaluate * Evaluates this filter for a specific feature. * * Parameters: * feature - {} 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: * {} 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.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: * {} */ initialize: function(options) { this.fids = []; OpenLayers.Filter.prototype.initialize.apply(this, [options]); }, /** * APIMethod: evaluate * evaluates this rule for a specific feature * * Parameters: * 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} 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.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 * or * 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()} 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} */ setFilterProperty: function(filter) { if(filter.filters) { for(var i=0, len=filter.filters.length; i */ 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.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: * {} */ 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/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.Logical = OpenLayers.Class(OpenLayers.Filter, { /** * APIProperty: filters * {Array()} 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: * {} */ 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} Clone of this filter. */ clone: function() { var filters = []; for(var i=0, len=this.filters.length; i */ 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: * {} */ 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 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: * {} 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 * constructor. * * Inherits from: * - */ 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 - {} 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: * {} 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.Function = OpenLayers.Class(OpenLayers.Filter, { /** * APIProperty: name * {String} Name of the function. */ name: null, /** * APIProperty: params * {Array( || 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: * {} */ 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.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 * 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: * {} 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 attribute"} 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 {} 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: * {} 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 * 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 * ( || String || Number) * * Parameters: * value - ( || 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 - {} 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": "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/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 constructor. This is a base class, * typical geometry types are described by subclasses of this class. * * Note that if you use the 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 * {}This is set when a Geometry is added as component * of another geometry */ parent: null, /** * Property: 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: * {} An exact clone of this geometry. */ clone: function() { return new OpenLayers.Geometry(); }, /** * Method: setBounds * Set the bounds for this Geometry. * * Parameters: * 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 - {} */ 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: * {} */ 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 - {} 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 - {|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: * {} 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: * {} 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= 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 | } The two segments intersect. * If the point argument is true, the return will be the intersection * point or false if none exists. If point is true and the segments * are coincident, return will be true (and the instersection is equal * to the shorter segment). */ OpenLayers.Geometry.segmentsIntersect = function(seg1, seg2, options) { var point = options && options.point; var tolerance = options && options.tolerance; var intersection = false; var x11_21 = seg1.x1 - seg2.x1; var y11_21 = seg1.y1 - seg2.y1; var x12_11 = seg1.x2 - seg1.x1; var y12_11 = seg1.y2 - seg1.y1; var y22_21 = seg2.y2 - seg2.y1; var x22_21 = seg2.x2 - seg2.x1; var d = (y22_21 * x12_11) - (x22_21 * y12_11); var n1 = (x22_21 * y11_21) - (y22_21 * x11_21); var n2 = (x12_11 * y11_21) - (y12_11 * x11_21); if(d == 0) { // parallel if(n1 == 0 && n2 == 0) { // coincident intersection = true; } } else { var along1 = n1 / d; var along2 = n2 / d; if(along1 >= 0 && along1 <= 1 && along2 >=0 && along2 <= 1) { // intersect if(!point) { intersection = true; } else { // calculate the intersection point var x = seg1.x1 + (along1 * x12_11); var y = seg1.y1 + (along1 * y12_11); intersection = new OpenLayers.Geometry.Point(x, y); } } } if(tolerance) { var dist; if(intersection) { if(point) { var segs = [seg1, seg2]; var seg, x, y; // check segment endpoints for proximity to intersection // set intersection to first endpoint within the tolerance outer: for(var i=0; i<2; ++i) { seg = segs[i]; for(var j=1; j<3; ++j) { x = seg["x" + j]; y = seg["y" + j]; dist = Math.sqrt( Math.pow(x - intersection.x, 2) + Math.pow(y - intersection.y, 2) ); if(dist < tolerance) { intersection.x = x; intersection.y = y; break outer; } } } } } else { // no calculated intersection, but segments could be within // the tolerance of one another var segs = [seg1, seg2]; var source, target, x, y, p, result; // check segment endpoints for proximity to intersection // set intersection to first endpoint within the tolerance outer: for(var i=0; i<2; ++i) { source = segs[i]; target = segs[(i+1)%2]; for(var j=1; j<3; ++j) { p = {x: source["x"+j], y: source["y"+j]}; result = OpenLayers.Geometry.distanceToSegment(p, target); if(result.distance < tolerance) { if(point) { intersection = new OpenLayers.Geometry.Point(p.x, p.y); } else { intersection = true; } break outer; } } } } } return intersection; }; /** * Function: OpenLayers.Geometry.distanceToSegment * * Parameters: * point - {Object} An object with x and y properties representing the * point coordinates. * segment - {Object} An object with x1, y1, x2, and y2 properties * representing endpoint coordinates. * * Returns: * {Object} An object with distance, along, x, and y properties. The distance * will be the shortest distance between the input point and segment. * The x and y properties represent the coordinates along the segment * where the shortest distance meets the segment. The along attribute * describes how far between the two segment points the given point is. */ OpenLayers.Geometry.distanceToSegment = function(point, segment) { var result = OpenLayers.Geometry.distanceSquaredToSegment(point, segment); result.distance = Math.sqrt(result.distance); return result; }; /** * Function: OpenLayers.Geometry.distanceSquaredToSegment * * Usually the distanceToSegment function should be used. This variant however * can be used for comparisons where the exact distance is not important. * * Parameters: * point - {Object} An object with x and y properties representing the * point coordinates. * segment - {Object} An object with x1, y1, x2, and y2 properties * representing endpoint coordinates. * * Returns: * {Object} An object with squared distance, along, x, and y properties. * The distance will be the shortest distance between the input point and * segment. The x and y properties represent the coordinates along the * segment where the shortest distance meets the segment. The along * attribute describes how far between the two segment points the given * point is. */ OpenLayers.Geometry.distanceSquaredToSegment = function(point, segment) { var x0 = point.x; var y0 = point.y; var x1 = segment.x1; var y1 = segment.y1; var x2 = segment.x2; var y2 = segment.y2; var dx = x2 - x1; var dy = y2 - y1; var along = ((dx * (x0 - x1)) + (dy * (y0 - y1))) / (Math.pow(dx, 2) + Math.pow(dy, 2)); var x, y; if(along <= 0.0) { x = x1; y = y1; } else if(along >= 1.0) { x = x2; y = y2; } else { x = x1 + along * dx; y = y1 + along * dy; } return { distance: Math.pow(x - x0, 2) + Math.pow(y - y0, 2), x: x, y: y, along: along }; }; /* ====================================================================== OpenLayers/Geometry/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.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: * {} 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 - {} 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 - {} 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. "5, 42") */ 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 - {} 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: * {} 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 - {} Point of origin for resizing * ratio - {Float} Optional x:y ratio for resizing. Default ratio is 1. * * Returns: * {} - 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 - {} 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 - {} * dest - {} * * Returns: * {} */ 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 (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 and functions here merely iterate through * the components, summing their respective areas and lengths. * * Create a new instance with the constructor. * * Inherits from: * - */ OpenLayers.Geometry.Collection = OpenLayers.Class(OpenLayers.Geometry, { /** * APIProperty: components * {Array()} 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()} 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: * {} An exact clone of this collection */ clone: function() { var geometry = eval("new " + this.CLASS_NAME + "()"); for(var i=0, len=this.components.length; i)} An array of geometries to add */ addComponents: function(components){ if(!(OpenLayers.Util.isArray(components))) { components = [components]; } for(var i=0, len=components.length; i} 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()} 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 - {} * * 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. * * 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} 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} 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 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} 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} Center point for the rotation */ rotate: function(angle, origin) { for(var i=0, len=this.components.length; i} Point of origin for resizing * ratio - {Float} Optional x:y ratio for resizing. Default ratio is 1. * * Returns: * {} - The current geometry. */ resize: function(scale, origin, ratio) { for(var i=0; i} 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} 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} * dest - {} * * Returns: * {} */ transform: function(source, dest) { if (source && dest) { for (var i=0, len=this.components.length; i} 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 constructor. * * Inherits from: * - * - */ 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()} * * Returns: * {} */ /** * APIMethod: addPoint * Wrapper for * * Parameters: * point - {} Point to be added * index - {Integer} Optional index */ addPoint: function(point, index) { this.addComponent(point, index); }, /** * APIMethod: removePoint * Wrapper for * * Parameters: * 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.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()} */ /** * 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} 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 */ OpenLayers.Geometry.LineString = OpenLayers.Class(OpenLayers.Geometry.Curve, { /** * Constructor: OpenLayers.Geometry.LineString * Create a new LineString geometry * * Parameters: * points - {Array()} 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 - {} 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 - {} * * 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 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 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 - {} 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 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 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 - {} 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 - {} 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 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 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 * components. * * Inherits from: * - * - */ 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()} * */ /** * Method: split * Use this geometry (the source) to attempt to split a target geometry. * * Parameters: * 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 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 - {} 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 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.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()} 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 - {} * 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 - {} * * 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} Center point for the rotation */ rotate: function(angle, origin) { for(var i=0, len=this.components.length; i} Point of origin for resizing * ratio - {Float} Optional x:y ratio for resizing. Default ratio is 1. * * Returns: * {} - The current geometry. */ resize: function(scale, origin, ratio) { for(var i=0, len=this.components.length; i} * dest - {} * * Returns: * {} */ transform: function(source, dest) { if (source && dest) { for (var i=0, len=this.components.length; i} 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} 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} * * 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= 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 - {} 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 * - */ 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()} */ /** * 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} 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} * * 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} 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} 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 - {} 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 * components. Create a new instance with the * constructor. * * Inherits from: * - */ 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()} An array of polygons * used to generate the MultiPolygon * */ CLASS_NAME: "OpenLayers.Geometry.MultiPolygon" }); /* ====================================================================== OpenLayers/Format/GML.js ====================================================================== */ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for * full list of contributors). Published under the 2-clause BSD license. * See license.txt in the OpenLayers distribution or repository for the * full text of the license. */ /** * @requires OpenLayers/Format/XML.js * @requires OpenLayers/Feature/Vector.js * @requires OpenLayers/Geometry/Point.js * @requires OpenLayers/Geometry/MultiPoint.js * @requires OpenLayers/Geometry/LineString.js * @requires OpenLayers/Geometry/MultiLineString.js * @requires OpenLayers/Geometry/Polygon.js * @requires OpenLayers/Geometry/MultiPolygon.js */ /** * Class: OpenLayers.Format.GML * Read/Write GML. Create a new instance with the * constructor. Supports the GML simple features profile. * * Inherits from: * - */ 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()} 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 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} A point geometry. */ point: function(node) { /** * Three coordinate variations to consider: * 1) x y z * 2) x, y, z * 3) xy */ var nodeList, coordString; var coords = []; // look for 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 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 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: * {} 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} A linestring geometry. */ linestring: function(node, ring) { /** * Two coordinate variations to consider: * 1) x0 y0 z0 x1 y1 z1 * 2) x0, y0, z0 x1, y1, z1 */ var nodeList, coordString; var coords = []; var points = []; // look for 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 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} 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} 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} 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 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: * {} 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 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()} 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} 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 - {} 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 - {} 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} 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 - {} 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} 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 - {} 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} 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} 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) * ... * (end) * * Parameters: * 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 */ 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 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 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 , * but auto-configured and during read. * Subclasses making use of auto-configuration should make * the first call to the 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 * or 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()} 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 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) | 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 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.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 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} * 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 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.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 * 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} filter and converts it into XML. * * Parameters: * filter - {} 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.js ====================================================================== */ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for * full list of contributors). Published 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 * constructor. * * Inherits from: * - */ 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.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_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_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 * constructor. * * Inherits from: * - * - */ 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 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} 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 * {} 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 - {} */ 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: * {} An * 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({})} or * {} * options - {Object} Optional object for configuring the request. * * Returns: * {} An * 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({})} or * {} * options - {Object} Optional object for configuring the request. * * Returns: * {} An * 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 - {} * options - {Object} Optional object for configuring the request. * * Returns: * {} An * 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({})} * 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({})} An array of * objects. */ commit: function() { }, /** * Method: abort * Abort an ongoing request. * * Parameters: * 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 - {} 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({})} or {} * 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({})} or {} * 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/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 * constructor. * * Inherits from: * - */ 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 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 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 * constructor. * * Inherits from: * - */ 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 . If type is "Geometry", the input json * must represent a single geometry, and the return will be an * . If type is "Feature", the input json must * represent a single feature, and the return will be an * . */ 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. * * Parameters: * obj - {Object} An object created from a GeoJSON object * * Returns: * {} 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 . * * Parameters: * obj - {Object} An object created from a GeoJSON object * * Returns: * {} 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. * * Parameters: * array - {Object} The coordinates array from the GeoJSON fragment. * * Returns: * {} 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 * . * * Parameters: * array - {Object} The coordinates array from the GeoJSON fragment. * * Returns: * {} A geometry. */ "multipoint": function(array) { var points = []; var p = null; for(var i=0, len=array.length; i. * * Parameters: * array - {Object} The coordinates array from the GeoJSON fragment. * * Returns: * {} A geometry. */ "linestring": function(array) { var points = []; var p = null; for(var i=0, len=array.length; i. * * Parameters: * array - {Object} The coordinates array from the GeoJSON fragment. * * Returns: * {} A geometry. */ "multilinestring": function(array) { var lines = []; var l = null; for(var i=0, len=array.length; i. * * Returns: * {} A geometry. */ "polygon": function(array) { var rings = []; var r, l; for(var i=0, len=array.length; i. * * Parameters: * array - {Object} The coordinates array from the GeoJSON fragment. * * Returns: * {} A geometry. */ "multipolygon": function(array) { var polys = []; var p = null; for(var i=0, len=array.length; i. * * Parameters: * array - {Object} The coordinates array from the GeoJSON fragment. * * Returns: * {} 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 , , * 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} * * 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 - {} * * 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 - {} * * 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 - {} * * 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 - {} * * 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} * * Returns: * {Array} An array of coordinate arrays representing * the linestring. */ 'linestring': function(linestring) { var array = []; for(var i=0, len=linestring.components.length; i} * * Returns: * {Array} An array of linestring arrays representing * the multilinestring. */ 'multilinestring': function(multilinestring) { var array = []; for(var i=0, len=multilinestring.components.length; i} * * 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} * * Returns: * {Array} An array of polygon arrays representing * the multipolygon */ 'multipolygon': function(multipolygon) { var array = []; for(var i=0, len=multipolygon.components.length; i} * * 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 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 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 property. * * Inherits from: * - */ 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 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 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 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 * {} Format for parsing features. Default is an * 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 - {} Filter to get serialized as a * query string. * * Returns: * {} 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 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 - {} 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 * 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 - {} 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 - {} 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({})} or * {} 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 - {} The response object returned * from a 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/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 constructor. * * Inherits from: * - */ 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: * {} 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: * {} 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 - {|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.} 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.} 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.} 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.} 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.} 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.} 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.} 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.} 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/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.Panel = OpenLayers.Class(OpenLayers.Control, { /** * Property: controls * {Array()} */ controls: null, /** * APIProperty: autoActivate * {Boolean} Activate the control when it is added to a map. Default is * true. */ autoActivate: true, /** * APIProperty: defaultControl * {} The control which is activated when the control is * activated (turned on), which also happens at instantiation. * If is true, 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 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 method is called. * * Specific properties for controls on a panel: * type - {Number} One of , * , . * If not provided, is assumed. * title - {string} Text displayed when mouse is over the icon that * represents the control. * * The of a control determines the behavior when * clicking its icon: * - The control is activated and other * controls of this type in the same panel are deactivated. This is * the default type. * - The active state of the control is * toggled. * - The * method of the control is called, * but its active state is not changed. * * If a control is , 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=0; i--) { this.div.removeChild(this.div.childNodes[i]); } this.div.innerHTML = ""; if (this.active) { for (var i=0, len=this.controls.length; i} */ 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} 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} 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()} Controls to add into map. */ addControlsToMap: function (controls) { var control; for (var i=0, len=controls.length; 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()} 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()} 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()} 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 * . * When clicked, the function trigger() is executed. * * Inherits from: * - * * 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.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.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 * . * * Inherits from: * - */ 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 * , a , and a * . 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) * * (end) * * Inherits from: * - */ 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/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.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: * {} An exact clone of this * */ 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 constructor, or a subclass. * * TBD 3.0 - remove reference to url in above paragraph * */ OpenLayers.Tile = OpenLayers.Class({ /** * APIProperty: 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 (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 . 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 * . 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 * {} 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 * {} null */ bounds: null, /** * Property: size * {} null */ size: null, /** * Property: position * {} 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 instance. * * Parameters: * layer - {} layer that the tile will go in. * position - {} * bounds - {} * url - {} * 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 and return the result from . * * 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 {} */ 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 - {} * position - {} * 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 * constructor. * * Inherits from: * - */ OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile, { /** * APIProperty: 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 events): * beforeload - Triggered before an image is prepared for loading, when the * url for the image is known already. Listeners may call 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 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 instance. * * Parameters: * layer - {} layer that the tile will go in. * position - {} * bounds - {} * url - {} Deprecated. Remove me in 3.0. * 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 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 constructor. * * Inherits from: * - */ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { /** * APIProperty: tileSize * {} */ tileSize: null, /** * Property: tileOriginCorner * {String} If the property is not provided, the tile origin * will be derived from the layer's . The corner of the * 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 * {} 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 * . Default is ``null``. */ tileOrigin: null, /** APIProperty: tileOptions * {Object} optional configuration options for instances * created by this Layer, if supported by the tile class. */ tileOptions: null, /** * APIProperty: tileClass * {} The tile class to use for this layer. * Defaults is OpenLayers.Tile.Image. */ tileClass: OpenLayers.Tile.Image, /** * Property: grid * {Array(Array())} 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 layers, * 2500 for tiled layers. See 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 ), * 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, * 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 - {} 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 - {} 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} 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 - {} * 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 - {} map location * * Returns: * {Object} Object with the following properties: 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 - {} */ 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 and . * * 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=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 - {} */ 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: * {} 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 - {} */ 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 - {|Object} OpenLayers.Bounds or an * object with a 'left' and 'top' properties. * origin - {|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 * property is supplied, that will be returned. Otherwise, the origin * will be derived from the layer's property. In this case, * the tile origin will be the corner of the given by the * property. * * Returns: * {} 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: * {} 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 - {} */ 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} */ getMaxExtent: function() { return this.maxExtent; }, /** * APIMethod: addTile * Create a tile, initialize it, and add it to the layer div. * * Parameters * bounds - {} * position - {} * * Returns: * {} 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 - {} */ 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 - {} */ 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 rows) { var row = this.grid.pop(); for (i=0, l=row.length; i 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 - {} The location in the viewport. * * Returns: * {} 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/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 * constructor. * * Inherits from: * - */ 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 - {} 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 {} 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: * {} 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 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 - {} 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 - {} An element to parse or arcxml data from. * * Returns: * {} 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/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 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 * {} An events object that handles all * events on the {} object. * * All event listeners will receive an event object with three properties: * request - {} 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 , , , , , or . * 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 * . * 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 * 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 . * 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 and requests. * Make sure to provide the appropriate "Content-Type" header for your * data. For and 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 method, with the method property set * to GET. * * Parameters: * config - {Object} Object with properties for configuring the request. * See the 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 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 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 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 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 method, with the method property set * to DELETE. * * Parameters: * config - {Object} Object with properties for configuring the request. * See the 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 method, with the method property set * to HEAD. * * Parameters: * config - {Object} Object with properties for configuring the request. * See the 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 method, with the method property set * to OPTIONS. * * Parameters: * config - {Object} Object with properties for configuring the request. * See the 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/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 * constructor. * * Inherits from: * - */ 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 - {} 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 - {} 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 - {} or {} 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: * {} 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/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 * and a . By * default it is drawn in the upper left corner of the map. * * Inherits from: * - */ 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 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 . 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 * {} */ 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 - {} */ setMap: function(map) { OpenLayers.Control.prototype.setMap.apply(this, arguments); this.map.events.register("buttonclick", this, this.onButtonClick); }, /** * Method: draw * * Parameters: * px - {} * * 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 - {} * sz - {} * * 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/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 * and a . * 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.PanZoomBar = OpenLayers.Class(OpenLayers.Control.PanZoom, { /** * APIProperty: zoomStopWidth */ zoomStopWidth: 18, /** * APIProperty: zoomStopHeight */ zoomStopHeight: 11, /** * Property: slider */ slider: null, /** * Property: sliderEvents * {} */ 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 * {} */ mouseDragStart: null, /** * Property: deltaY * {Number} The cumulative vertical pixel offset during a zoom bar drag. */ deltaY: null, /** * Property: zoomStart * {} */ 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 - {} */ 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 - {} */ 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 - {} 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 - {} */ passEventToSlider:function(evt) { this.sliderEvents.handleBrowserEvent(evt); }, /* * Method: zoomBarDown * event listener for clicks on the slider * * Parameters: * evt - {} */ 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 - {} */ 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 - {} */ 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/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.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/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.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_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.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/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 * constructor. * * Inherits from: * - */ 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 * {} 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 * {} The image size in pixels */ size: null, /** * Property: tile * {} */ 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 - {} The extent represented by the image * 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: * {} 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 - {} */ 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 - {} * 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 - {} */ 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 . * * Parameters: * 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 - {} */ getURL: function(bounds) { return this.url; }, CLASS_NAME: "OpenLayers.Layer.Image" }); /* ====================================================================== 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 * {} 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 property. * * Parameters: * layer - {} */ setLayer: function(layer) { this.layer = layer; }, /** * Method: activate * Activate the strategy. Register any listeners, do appropriate setup. * * Returns: * {Boolean} True if the strategy was successfully activated or false if * the strategy was already active. */ activate: function() { if (!this.active) { this.active = true; return true; } return false; }, /** * Method: deactivate * Deactivate the strategy. Unregister any listeners, do appropriate * tear-down. * * Returns: * {Boolean} True if the strategy was successfully deactivated or false if * the strategy was already inactive. */ deactivate: function() { if (this.active) { this.active = false; return true; } return false; }, CLASS_NAME: "OpenLayers.Strategy" }); /* ====================================================================== OpenLayers/Strategy/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 before persisting changes. By * configuring the strategy with the option, changes can be saved * automatically. * * Inherits from: * - */ OpenLayers.Strategy.Save = OpenLayers.Class(OpenLayers.Strategy, { /** * APIProperty: 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 * {} 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} 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 0) { this.layer.destroyFeatures(destroys); } this.events.triggerEvent("success", evt); } else { this.events.triggerEvent("fail", evt); } }, CLASS_NAME: "OpenLayers.Strategy.Save" }); /* ====================================================================== 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 * {} 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 - {} 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 - {} 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 - {} */ start: function(evt) { this.startEvt = evt; }, /** * Method: cancel * Deletes the start event. * * Parameters: * evt - {} */ cancel: function(evt) { delete this.startEvt; }, /** * Method: onClick * Listener for the click event. * * Parameters: * evt - {} */ 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} */ onMousemove: function(evt) { delete this.startEvt; var features = this.getFeatures(evt); var over = {}, newly = [], feature; for (var i=0, len=features.length; i)} 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=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/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 * constructor. * * Inherits from: * - */ 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({}) */ 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} 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 - {} * * Returns: * {Object} An attributes object. */ parseAttributes: function(node) { // node is either a wpt, trk or rte // attributes are children of the form value 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()} 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, and builds a node for it. * * Parameters: * feature - {} * * 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 - {} */ 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 - {} * * 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 - {} * * 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 - {} */ 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/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.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 {} 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/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.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 {} 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 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/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.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 is an alternative to * setting 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) can include * resolutions that the server supports and that you don't want to * provide with this layer; you can also look at , which is * an alternative to for that specific purpose. * (b) The map can work with resolutions that aren't supported by * the server, i.e. that aren't in . 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: * {} 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 - {} * * 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 - {} * * 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 - {} */ setMap: function(map) { OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments); if (!this.tileOrigin) { this.tileOrigin = new OpenLayers.LonLat(this.maxExtent.left, this.maxExtent.bottom); } }, CLASS_NAME: "OpenLayers.Layer.XYZ" }); /* ====================================================================== OpenLayers/Layer/OSM.js ====================================================================== */ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for * full list of contributors). Published under the 2-clause BSD license. * See license.txt in the OpenLayers distribution or repository for the * full text of the license. */ /** * @requires OpenLayers/Layer/XYZ.js */ /** * Class: OpenLayers.Layer.OSM * This layer allows accessing OpenStreetMap tiles. By default the OpenStreetMap * hosted tile.openstreetmap.org Mapnik tileset is used. If you wish to use * a different layer instead, you need to provide a different * URL to the constructor. Here's an example for using OpenCycleMap: * * (code) * new OpenLayers.Layer.OSM("OpenCycleMap", * ["http://a.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png", * "http://b.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png", * "http://c.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png"]); * (end) * * Inherits from: * - */ OpenLayers.Layer.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: "© OpenStreetMap contributors", /** * Property: sphericalMercator * {Boolean} */ sphericalMercator: true, /** * Property: wrapDateLine * {Boolean} */ wrapDateLine: true, /** APIProperty: tileOptions * {Object} optional configuration options for 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. * ). */ initialize: function(name, url, options) { OpenLayers.Layer.XYZ.prototype.initialize.apply(this, arguments); this.tileOptions = OpenLayers.Util.extend({ crossOriginKeyword: 'anonymous' }, this.options && this.options.tileOptions); }, /** * Method: clone */ clone: function(obj) { if (obj == null) { obj = new OpenLayers.Layer.OSM( this.name, this.url, this.getOptions()); } obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]); return obj; }, CLASS_NAME: "OpenLayers.Layer.OSM" }); /* ====================================================================== OpenLayers/Renderer.js ====================================================================== */ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for * full list of contributors). Published under the 2-clause BSD license. * See license.txt in the OpenLayers distribution or repository for the * full text of the license. */ /** * @requires OpenLayers/BaseTypes/Class.js */ /** * Class: OpenLayers.Renderer * This is the base class for all renderers. * * This is based on a merger code written by Paul Spencer and Bertil Chapuis. * It is largely composed of virtual functions that are to be implemented * in technology-specific subclasses, but there is some generic code too. * * The functions that *are* implemented here merely deal with the maintenance * of the size and extent variables, as well as the cached 'resolution' * value. * * A note to the user that all subclasses should use getResolution() instead * of directly accessing this.resolution in order to correctly use the * cacheing system. * */ OpenLayers.Renderer = OpenLayers.Class({ /** * Property: container * {DOMElement} */ container: null, /** * Property: root * {DOMElement} */ root: null, /** * Property: extent * {} */ 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 * {} */ size: null, /** * Property: resolution * {Float} cache of current map resolution */ resolution: null, /** * Property: 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 * ). */ featureDx: 0, /** * Constructor: OpenLayers.Renderer * * Parameters: * containerID - {} * 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 - {} * 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 - {} */ 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 - {} * style - {} * * 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 - {} Bounds of the feature * worldBounds - {} 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 - {} * style - {Object} * featureId - {} */ 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 - {} */ 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 - {} * * Returns: * {String} A feature id or undefined. */ getFeatureIdFromEvent: function(evt) {}, /** * Method: eraseFeatures * This is called by the layer to erase features * * Parameters: * features - {Array()} */ eraseFeatures: function(features) { if(!(OpenLayers.Util.isArray(features))) { features = [features]; } for(var i=0, len=features.length; i} * 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 - {} target renderer for the moved root */ moveRoot: function(renderer) {}, /** * Method: getRenderLayerId * Gets the layer that this renderer's output appears on. If moveRoot was * used, this will be different from the id of the layer containing the * features rendered by this renderer. * * Returns: * {String} the id of the output layer. */ getRenderLayerId: function() { return this.container.id; }, /** * Method: applyDefaultSymbolizer * * Parameters: * symbolizer - {Object} * * Returns: * {Object} */ applyDefaultSymbolizer: function(symbolizer) { var result = OpenLayers.Util.extend({}, OpenLayers.Renderer.defaultSymbolizer); if(symbolizer.stroke === false) { delete result.strokeWidth; delete result.strokeColor; } if(symbolizer.fill === false) { delete result.fillColor; } OpenLayers.Util.extend(result, symbolizer); return result; }, CLASS_NAME: "OpenLayers.Renderer" }); /** * Constant: OpenLayers.Renderer.defaultSymbolizer * {Object} Properties from this symbolizer will be applied to symbolizers * with missing properties. This can also be used to set a global * symbolizer default in OpenLayers. To be SLD 1.x compliant, add the * following code before rendering any vector features: * (code) * OpenLayers.Renderer.defaultSymbolizer = { * fillColor: "#808080", * fillOpacity: 1, * strokeColor: "#000000", * strokeOpacity: 1, * strokeWidth: 1, * pointRadius: 3, * graphicName: "square" * }; * (end) */ OpenLayers.Renderer.defaultSymbolizer = { fillColor: "#000000", strokeColor: "#000000", strokeWidth: 2, fillOpacity: 1, strokeOpacity: 1, pointRadius: 0, labelAlign: 'cm' }; /** * Constant: OpenLayers.Renderer.symbol * Coordinate arrays for well known (named) symbols. */ OpenLayers.Renderer.symbol = { "star": [350,75, 379,161, 469,161, 397,215, 423,301, 350,250, 277,301, 303,215, 231,161, 321,161, 350,75], "cross": [4,0, 6,0, 6,4, 10,4, 10,6, 6,6, 6,10, 4,10, 4,6, 0,6, 0,4, 4,4, 4,0], "x": [0,0, 25,0, 50,35, 75,0, 100,0, 65,50, 100,100, 75,100, 50,65, 25,100, 0,100, 35,50, 0,0], "square": [0,0, 0,1, 1,1, 1,0, 0,0], "triangle": [0,10, 10,10, 5,0, 0,10] }; /* ====================================================================== OpenLayers/Renderer/Canvas.js ====================================================================== */ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for * full list of contributors). Published under the 2-clause BSD license. * See license.txt in the OpenLayers distribution or repository for the * full text of the license. */ /** * @requires OpenLayers/Renderer.js */ /** * Class: OpenLayers.Renderer.Canvas * A renderer based on the 2D 'canvas' drawing element. * * Inherits: * - */ OpenLayers.Renderer.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 - {} * 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 - {} * 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 - {} * 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 - {} */ 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 - {} * style - {} * * 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 - {} * 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 - {} * 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 - {} * 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= 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 - {} 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 - {} * 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 - {} * 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 - {} * 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} * 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} * 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 (} */ 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 - {} * * Returns: * {)} */ eraseFeatures: function(features) { if(!(OpenLayers.Util.isArray(features))) { features = [features]; } for(var i=0; i constructor. * * Inherits from: * - */ 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({}) */ 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()} */ 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 - {} */ 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 - {} */ 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 - {} * 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 - {} * 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/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 * constructor. * * Inherits from: * - */ 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 - {} 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 constructor. * * Inherits From: * - */ 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 method with the target feature. * Note that you must call the method to finish * feature modification in standalone mode (before starting to modify * another feature). */ standalone: false, /** * Property: layer * {} */ layer: null, /** * Property: feature * {} Feature currently available for modification. */ feature: null, /** * Property: vertex * {} Vertex currently being modified. */ vertex: null, /** * Property: vertices * {Array()} Verticies currently available * for dragging. */ vertices: null, /** * Property: virtualVertices * {Array()} 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 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 * {} A handle for rotating/resizing a feature. */ radiusHandle: null, /** * Property: dragHandle * {} 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 - {} 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 - {} 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 - {} 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 - {} 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 - {} 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 - {} The vertex being dragged. * 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 - {} 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 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} The control's map. */ setMap: function(map) { this.handlers.drag.setMap(map); OpenLayers.Control.prototype.setMap.apply(this, arguments); }, /** * Method: handleMapEvents * * Parameters: * evt - {Object} */ handleMapEvents: function(evt) { if (evt.type == "removelayer" || evt.property == "order") { this.moveLayerToTop(); } }, /** * Method: moveLayerToTop * Moves the layer for this handler to the top, so mouse events can reach * it. */ moveLayerToTop: function() { var index = Math.max(this.map.Z_INDEX_BASE['Feature'] - 1, this.layer.getZIndex()) + 1; this.layer.setZIndex(index); }, /** * Method: moveLayerBack * Moves the layer back to the position determined by the map's layers * array. */ moveLayerBack: function() { var index = this.layer.getZIndex() - 1; if (index >= this.map.Z_INDEX_BASE['Feature']) { this.layer.setZIndex(index); } else { this.map.setLayerZIndex(this.layer, this.map.getLayerIndex(this.layer)); } }, CLASS_NAME: "OpenLayers.Control.ModifyFeature" }); /** * Constant: RESHAPE * {Integer} Constant used to make the control work in reshape mode */ OpenLayers.Control.ModifyFeature.RESHAPE = 1; /** * Constant: RESIZE * {Integer} Constant used to make the control work in resize mode */ OpenLayers.Control.ModifyFeature.RESIZE = 2; /** * Constant: ROTATE * {Integer} Constant used to make the control work in rotate mode */ OpenLayers.Control.ModifyFeature.ROTATE = 4; /** * Constant: DRAG * {Integer} Constant used to make the control work in drag mode */ OpenLayers.Control.ModifyFeature.DRAG = 8; /* ====================================================================== OpenLayers/Layer/Bing.js ====================================================================== */ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for * full list of contributors). Published under the 2-clause BSD license. * See license.txt in the OpenLayers distribution or repository for the * full text of the license. */ /** * @requires OpenLayers/Layer/XYZ.js */ /** * Class: OpenLayers.Layer.Bing * Bing layer using direct tile access as provided by Bing Maps REST Services. * See http://msdn.microsoft.com/en-us/library/ff701713.aspx for more * information. Note: Terms of Service compliant use requires the map to be * configured with an control and the * attribution placed on or near the map. * * Inherits from: * - */ 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: '' + '${copyrights}' + '' + 'Terms of Use', /** * 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 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} */ 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= 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: * {} An exact clone of this */ 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/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 {}, 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 - {} The feature to evaluate the rules * of the intended style against. * intent - {String} The intent determines the symbolizer that will be * used to draw the feature. Well known intents are "default" * (for just drawing the features), "select" (for selected * features) and "temporary" (for drawing features). * * Returns: * {Object} symbolizer hash */ createSymbolizer: function(feature, intent) { if(!feature) { feature = new OpenLayers.Feature.Vector(); } if(!this.styles[intent]) { intent = "default"; } feature.renderIntent = intent; var defaultSymbolizer = {}; if(this.extendDefault && intent != "default") { defaultSymbolizer = this.styles["default"].createSymbolizer(feature); } return OpenLayers.Util.extend(defaultSymbolizer, this.styles[intent].createSymbolizer(feature)); }, /** * Method: addUniqueValueRules * Convenience method to create comparison rules for unique values of a * property. The rules will be added to the style object for a specified * rendering intent. This method is a shortcut for creating something like * the "unique value legends" familiar from well known desktop GIS systems * * Parameters: * renderIntent - {String} rendering intent to add the rules to * property - {String} values of feature attributes to create the * rules for * symbolizers - {Object} Hash of symbolizers, keyed by the desired * property values * context - {Object} An optional object with properties that * symbolizers' property values should be evaluated * against. If no context is specified, feature.attributes * will be used */ addUniqueValueRules: function(renderIntent, property, symbolizers, context) { var rules = []; for (var value in symbolizers) { rules.push(new OpenLayers.Rule({ symbolizer: symbolizers[value], context: context, filter: new OpenLayers.Filter.Comparison({ type: OpenLayers.Filter.Comparison.EQUAL_TO, property: property, value: value }) })); } this.styles[renderIntent].addRules(rules); }, CLASS_NAME: "OpenLayers.StyleMap" }); /* ====================================================================== OpenLayers/Layer/Vector.js ====================================================================== */ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for * full list of contributors). Published under the 2-clause BSD license. * See license.txt in the OpenLayers distribution or repository for the * full text of the license. */ /** * @requires OpenLayers/Layer.js * @requires OpenLayers/Renderer.js * @requires OpenLayers/StyleMap.js * @requires OpenLayers/Feature/Vector.js * @requires OpenLayers/Console.js * @requires OpenLayers/Lang.js */ /** * Class: OpenLayers.Layer.Vector * Instances of OpenLayers.Layer.Vector are used to render vector data from * a variety of sources. Create a new vector layer with the * constructor. * * Inherits from: * - */ OpenLayers.Layer.Vector = OpenLayers.Class(OpenLayers.Layer, { /** * APIProperty: 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 ): * 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()} */ features: null, /** * Property: 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()} */ 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 * {} */ styleMap: null, /** * Property: strategies * {Array(})} Optional list of strategies for the layer. */ strategies: null, /** * Property: 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 * {} */ renderer: null, /** * APIProperty: rendererOptions * {Object} Options for the renderer. See {} 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: * {} 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} 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} */ 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} */ removeMap: function(map) { this.drawn = false; if(this.strategies) { var strategy, i, len; for(i=0, len=this.strategies.length; i} * 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)} * 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)} 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()} 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 - {} * 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()} */ 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: * {} 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: * {} 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} 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: * {} 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({}) 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 - {} */ 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 - {} */ preFeatureInsert: function(feature) { }, /** * APIMethod: getDataExtent * Calculates the max extent which includes all of the features. * * Returns: * {} 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 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.PointGrid = OpenLayers.Class(OpenLayers.Layer.Vector, { /** * APIProperty: dx * {Number} Point grid spacing in the x-axis direction (map units). * Read-only. Use the method to modify this value. */ dx: null, /** * APIProperty: dy * {Number} Point grid spacing in the y-axis direction (map units). * Read-only. Use the 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 setting has precedence when determining * grid size. Read-only. Use the 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 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 method to modify this * value. */ rotation: 0, /** * APIProperty: origin * {} 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 method to modify this * value. */ origin: null, /** * Property: gridBounds * {} 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 and 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 - {} */ 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 - {} */ removeMap: function(map) { map.events.unregister("moveend", this, this.onMoveEnd); OpenLayers.Layer.Vector.prototype.removeMap.apply(this, arguments); }, /** * APIMethod: setRatio * Set the grid 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 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 and properties and update the grid. If only one * argument is provided, it will be set as and . 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 property and update the grid. Can only be called * after the layer has been added to a map with a center/extent. * * Parameters: * origin - {} */ setOrigin: function(origin) { this.origin = origin; this.updateGrid(true); }, /** * APIMethod: getOrigin * Get the grid property. * * Returns: * {} The grid origin. */ getOrigin: function() { if (!this.origin) { this.origin = this.map.getExtent().getCenterLonLat(); } return this.origin; }, /** * APIMethod: setRotation * Set the grid 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: * {} */ 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 */ 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 - {} * 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/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/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/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 * {} 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: * {} */ 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 - {} 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 - {} 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: * {} 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 * constructor. * * Inherits from: * - */ 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/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/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.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} * 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 * - */ 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 * 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 {} filter and converts it into XML. * * Parameters: * filter - {} The filter. * name - {String} Name of the generated XML element. * * Returns: * {DOMElement} The created XML element. */ writeSpatial: function(filter, name) { var node = this.createElementNSPlus("ogc:"+name); this.writeNode("PropertyName", filter, node); if(filter.value instanceof OpenLayers.Filter.Function) { this.writeNode("Function", filter.value, node); } else { var child; if(filter.value instanceof OpenLayers.Geometry) { child = this.writeNode("feature:_geometry", filter.value).firstChild; } else { child = this.writeNode("gml:Box", filter.value); } if(filter.projection) { child.setAttribute("srsName", filter.projection); } node.appendChild(child); } return node; }, CLASS_NAME: "OpenLayers.Format.Filter.v1_0_0" }); /* ====================================================================== OpenLayers/Format/WFST/v1_0_0.js ====================================================================== */ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for * full list of contributors). Published under the 2-clause BSD license. * See license.txt in the OpenLayers distribution or repository for the * full text of the license. */ /** * @requires OpenLayers/Format/WFST/v1.js * @requires OpenLayers/Format/Filter/v1_0_0.js */ /** * Class: OpenLayers.Format.WFST.v1_0_0 * A format for creating WFS v1.0.0 transactions. Create a new instance with the * constructor. * * Inherits from: * - * - */ 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} 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 - {} * 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 - {} * 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 - {} * 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.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 method compares this value (which is the one * from the previous 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 * {} 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 - {} * 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 - {} * 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 - {} * 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} * 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 - {} * 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 - {} * 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 - {} * * 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 - {} * * 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 - {} * * 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 - {} * * 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 - {} * * 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 - {} * * 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 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 - {} * 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} target renderer for the moved root */ moveRoot: function(renderer) { var root = this.root; if(renderer.root.parentNode == this.rendererRoot) { root = renderer.root; } root.parentNode.removeChild(root); renderer.rendererRoot.appendChild(root); }, /** * Method: getRenderLayerId * Gets the layer that this renderer's output appears on. If moveRoot was * used, this will be different from the id of the layer containing the * features rendered by this renderer. * * Returns: * {String} the id of the output layer. */ getRenderLayerId: function() { return this.root.parentNode.parentNode.id; }, /** * Method: isComplexSymbol * Determines if a symbol cannot be rendered using drawCircle * * Parameters: * graphicName - {String} * * Returns * {Boolean} true if the symbol is complex, false if not */ isComplexSymbol: function(graphicName) { return (graphicName != "circle") && !!graphicName; }, CLASS_NAME: "OpenLayers.Renderer.Elements" }); /* ====================================================================== OpenLayers/Control/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.ArgParser = OpenLayers.Class(OpenLayers.Control, { /** * Property: center * {} */ 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 * {} 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 - {} */ 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 */ 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 * {} 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 - {} */ 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} 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()} 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 */ 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 as advertised by the service. * For example, if the service advertises a with * 'href="http://tms.osgeo.org/1.0.0/vmap0"', the 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 element as the * "extension" attribute. For example, if the service advertises a * with , * the 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 * {} 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 . 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) can include * resolutions that the server supports and that you don't want to * provide with this layer; you can also look at , which is * an alternative to for that specific purpose. * (b) The map can work with resolutions that aren't supported by * the server, i.e. that aren't in . 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 is an alternative to * setting 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 * 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 * and 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: * {} An exact clone of this */ 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 - {} * * 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 - {} */ 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/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.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/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.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_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/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.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 . * * 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 - {} 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 */ 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/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.PointTrack = OpenLayers.Class(OpenLayers.Layer.Vector, { /** * APIProperty: dataFrom * {} or * {} 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 * {} or * {} 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()} * 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 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 and * */ OpenLayers.Layer.PointTrack.SOURCE_NODE = -1; /** * Constant: OpenLayers.Layer.PointTrack.TARGET_NODE * {Number} value for and * */ 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/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: * {} 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 , , * and for WFS 1.1.0. Note that * srsName matching with the WMS layer will not work with WFS 1.0.0. * * Parameters: * layer - {} 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.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/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.Markers = OpenLayers.Class(OpenLayers.Layer, { /** * APIProperty: isBaseLayer * {Boolean} Markers layer is never a base layer. */ isBaseLayer: false, /** * APIProperty: markers * {Array()} 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} * 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} */ 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 - {} */ 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 - {} */ 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: * {} */ 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. * * Inherits from: * - */ 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 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 . 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/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: * {} 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/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 * constructor. * * Inherits from: * - */ 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 constructor. * * Inherits from: * - */ 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()} A collection of features. */ read: function(data) { var axl = new OpenLayers.Format.ArcXML(); var parsed = axl.read(data); return parsed.features.feature; } }); /* ====================================================================== 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.Snapping = OpenLayers.Class(OpenLayers.Control, { /** * APIProperty: 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 ): * 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 * {} The current editable layer. Set at * construction or after construction with . */ layer: null, /** * Property: feature * {} The current editable feature. */ feature: null, /** * Property: 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 - {} 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 * 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 ) 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 - {} 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, 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, 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, will be * used. * 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 - {} 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} 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 - {} 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 - {} The vertex to be snapped (or * unsnapped). * loc - {} 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} 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 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/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_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.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 * constructor. * * Inherits from: * - */ 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 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: 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()} 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()} 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 and // 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 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 node and builds the style hash * accordingly * * Parameters: * node - {DOMElement}