--- /srv/reproducible-results/rbuild-debian/r-b-build.59zYmMjR/b1/openlayers_2.13.1+ds2-10_amd64.changes +++ /srv/reproducible-results/rbuild-debian/r-b-build.59zYmMjR/b2/openlayers_2.13.1+ds2-10_amd64.changes ├── Files │ @@ -1,2 +1,2 @@ │ │ - b8f862d3ec74ba06200c117bb86bd490 715748 javascript optional libjs-openlayers_2.13.1+ds2-10_all.deb │ + ed0d68076b09c8e93a24f9653872d0fa 706992 javascript optional libjs-openlayers_2.13.1+ds2-10_all.deb ├── libjs-openlayers_2.13.1+ds2-10_all.deb │ ├── file list │ │ @@ -1,3 +1,3 @@ │ │ -rw-r--r-- 0 0 0 4 2023-01-14 13:27:41.000000 debian-binary │ │ --rw-r--r-- 0 0 0 3684 2023-01-14 13:27:41.000000 control.tar.xz │ │ --rw-r--r-- 0 0 0 711872 2023-01-14 13:27:41.000000 data.tar.xz │ │ +-rw-r--r-- 0 0 0 3680 2023-01-14 13:27:41.000000 control.tar.xz │ │ +-rw-r--r-- 0 0 0 703120 2023-01-14 13:27:41.000000 data.tar.xz │ ├── control.tar.xz │ │ ├── control.tar │ │ │ ├── ./md5sums │ │ │ │ ├── ./md5sums │ │ │ │ │┄ Files differ │ ├── data.tar.xz │ │ ├── data.tar │ │ │ ├── ./usr/share/javascript/openlayers/OpenLayers.js │ │ │ │ ├── js-beautify {} │ │ │ │ │ @@ -136,14 +136,394 @@ │ │ │ │ │ * (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 < l; i++) { │ │ │ │ │ + o = arguments[i]; │ │ │ │ │ + if (typeof o === "function") { │ │ │ │ │ + o = o.prototype; │ │ │ │ │ + } │ │ │ │ │ + OpenLayers.Util.extend(C.prototype, o); │ │ │ │ │ + } │ │ │ │ │ +}; │ │ │ │ │ + │ │ │ │ │ +/** │ │ │ │ │ + * APIFunction: extend │ │ │ │ │ + * Copy all properties of a source object to a destination object. Modifies │ │ │ │ │ + * the passed in destination object. Any properties on the source object │ │ │ │ │ + * that are set to undefined will not be (re)set on the destination object. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * destination - {Object} The object that will be modified │ │ │ │ │ + * source - {Object} The object with properties to be set on the destination │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Object} The destination object. │ │ │ │ │ + */ │ │ │ │ │ +OpenLayers.Util = OpenLayers.Util || {}; │ │ │ │ │ +OpenLayers.Util.extend = function(destination, source) { │ │ │ │ │ + destination = destination || {}; │ │ │ │ │ + if (source) { │ │ │ │ │ + for (var property in source) { │ │ │ │ │ + var value = source[property]; │ │ │ │ │ + if (value !== undefined) { │ │ │ │ │ + destination[property] = value; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * IE doesn't include the toString property when iterating over an object's │ │ │ │ │ + * properties with the for(property in object) syntax. Explicitly check if │ │ │ │ │ + * the source has its own toString property. │ │ │ │ │ + */ │ │ │ │ │ + │ │ │ │ │ + /* │ │ │ │ │ + * FF/Windows < 2.0.0.13 reports "Illegal operation on WrappedNative │ │ │ │ │ + * prototype object" when calling hawOwnProperty if the source object │ │ │ │ │ + * is an instance of window.Event. │ │ │ │ │ + */ │ │ │ │ │ + │ │ │ │ │ + var sourceIsEvt = typeof window.Event == "function" && │ │ │ │ │ + source instanceof window.Event; │ │ │ │ │ + │ │ │ │ │ + if (!sourceIsEvt && │ │ │ │ │ + source.hasOwnProperty && source.hasOwnProperty("toString")) { │ │ │ │ │ + destination.toString = source.toString; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + return destination; │ │ │ │ │ +}; │ │ │ │ │ +/* ====================================================================== │ │ │ │ │ + OpenLayers/Icon.js │ │ │ │ │ + ====================================================================== */ │ │ │ │ │ + │ │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ + * full text of the license. */ │ │ │ │ │ + │ │ │ │ │ +/** │ │ │ │ │ + * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ │ + */ │ │ │ │ │ + │ │ │ │ │ +/** │ │ │ │ │ + * Class: OpenLayers.Icon │ │ │ │ │ + * │ │ │ │ │ + * The icon represents a graphical icon on the screen. Typically used in │ │ │ │ │ + * conjunction with a to represent markers on a screen. │ │ │ │ │ + * │ │ │ │ │ + * An icon has a url, size and position. It also contains an offset which │ │ │ │ │ + * allows the center point to be represented correctly. This can be │ │ │ │ │ + * provided either as a fixed offset or a function provided to calculate │ │ │ │ │ + * the desired offset. │ │ │ │ │ + * │ │ │ │ │ + */ │ │ │ │ │ +OpenLayers.Icon = OpenLayers.Class({ │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Property: url │ │ │ │ │ + * {String} image url │ │ │ │ │ + */ │ │ │ │ │ + url: null, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Property: size │ │ │ │ │ + * {|Object} An OpenLayers.Size or │ │ │ │ │ + * an object with a 'w' and 'h' properties. │ │ │ │ │ + */ │ │ │ │ │ + size: null, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Property: offset │ │ │ │ │ + * {|Object} distance in pixels to offset the │ │ │ │ │ + * image when being rendered. An OpenLayers.Pixel or an object │ │ │ │ │ + * with a 'x' and 'y' properties. │ │ │ │ │ + */ │ │ │ │ │ + offset: null, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Property: calculateOffset │ │ │ │ │ + * {Function} Function to calculate the offset (based on the size) │ │ │ │ │ + */ │ │ │ │ │ + calculateOffset: null, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Property: imageDiv │ │ │ │ │ + * {DOMElement} │ │ │ │ │ + */ │ │ │ │ │ + imageDiv: null, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Property: px │ │ │ │ │ + * {|Object} An OpenLayers.Pixel or an object │ │ │ │ │ + * with a 'x' and 'y' properties. │ │ │ │ │ + */ │ │ │ │ │ + px: null, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Constructor: OpenLayers.Icon │ │ │ │ │ + * Creates an icon, which is an image tag in a div. │ │ │ │ │ + * │ │ │ │ │ + * url - {String} │ │ │ │ │ + * size - {|Object} An OpenLayers.Size or an │ │ │ │ │ + * object with a 'w' and 'h' │ │ │ │ │ + * properties. │ │ │ │ │ + * offset - {|Object} An OpenLayers.Pixel or an │ │ │ │ │ + * object with a 'x' and 'y' │ │ │ │ │ + * properties. │ │ │ │ │ + * calculateOffset - {Function} │ │ │ │ │ + */ │ │ │ │ │ + initialize: function(url, size, offset, calculateOffset) { │ │ │ │ │ + this.url = url; │ │ │ │ │ + this.size = size || { │ │ │ │ │ + w: 20, │ │ │ │ │ + h: 20 │ │ │ │ │ + }; │ │ │ │ │ + this.offset = offset || { │ │ │ │ │ + x: -(this.size.w / 2), │ │ │ │ │ + y: -(this.size.h / 2) │ │ │ │ │ + }; │ │ │ │ │ + this.calculateOffset = calculateOffset; │ │ │ │ │ + │ │ │ │ │ + var id = OpenLayers.Util.createUniqueID("OL_Icon_"); │ │ │ │ │ + this.imageDiv = OpenLayers.Util.createAlphaImageDiv(id); │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Method: destroy │ │ │ │ │ + * Nullify references and remove event listeners to prevent circular │ │ │ │ │ + * references and memory leaks │ │ │ │ │ + */ │ │ │ │ │ + destroy: function() { │ │ │ │ │ + // erase any drawn elements │ │ │ │ │ + this.erase(); │ │ │ │ │ + │ │ │ │ │ + OpenLayers.Event.stopObservingElement(this.imageDiv.firstChild); │ │ │ │ │ + this.imageDiv.innerHTML = ""; │ │ │ │ │ + this.imageDiv = null; │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Method: clone │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {} A fresh copy of the icon. │ │ │ │ │ + */ │ │ │ │ │ + clone: function() { │ │ │ │ │ + return new OpenLayers.Icon(this.url, │ │ │ │ │ + this.size, │ │ │ │ │ + this.offset, │ │ │ │ │ + this.calculateOffset); │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Method: setSize │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * size - {|Object} An OpenLayers.Size or │ │ │ │ │ + * an object with a 'w' and 'h' properties. │ │ │ │ │ + */ │ │ │ │ │ + setSize: function(size) { │ │ │ │ │ + if (size != null) { │ │ │ │ │ + this.size = size; │ │ │ │ │ + } │ │ │ │ │ + this.draw(); │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Method: setUrl │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * url - {String} │ │ │ │ │ + */ │ │ │ │ │ + setUrl: function(url) { │ │ │ │ │ + if (url != null) { │ │ │ │ │ + this.url = url; │ │ │ │ │ + } │ │ │ │ │ + this.draw(); │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Method: draw │ │ │ │ │ + * Move the div to the given pixel. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * px - {|Object} An OpenLayers.Pixel or an │ │ │ │ │ + * object with a 'x' and 'y' properties. │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {DOMElement} A new DOM Image of this icon set at the location passed-in │ │ │ │ │ + */ │ │ │ │ │ + draw: function(px) { │ │ │ │ │ + OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, │ │ │ │ │ + null, │ │ │ │ │ + null, │ │ │ │ │ + this.size, │ │ │ │ │ + this.url, │ │ │ │ │ + "absolute"); │ │ │ │ │ + this.moveTo(px); │ │ │ │ │ + return this.imageDiv; │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Method: erase │ │ │ │ │ + * Erase the underlying image element. │ │ │ │ │ + */ │ │ │ │ │ + erase: function() { │ │ │ │ │ + if (this.imageDiv != null && this.imageDiv.parentNode != null) { │ │ │ │ │ + OpenLayers.Element.remove(this.imageDiv); │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Method: setOpacity │ │ │ │ │ + * Change the icon's opacity │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * opacity - {float} │ │ │ │ │ + */ │ │ │ │ │ + setOpacity: function(opacity) { │ │ │ │ │ + OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, null, null, │ │ │ │ │ + null, null, null, null, opacity); │ │ │ │ │ + │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Method: moveTo │ │ │ │ │ + * move icon to passed in px. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * px - {|Object} the pixel position to move to. │ │ │ │ │ + * An OpenLayers.Pixel or an object with a 'x' and 'y' properties. │ │ │ │ │ + */ │ │ │ │ │ + moveTo: function(px) { │ │ │ │ │ + //if no px passed in, use stored location │ │ │ │ │ + if (px != null) { │ │ │ │ │ + this.px = px; │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + if (this.imageDiv != null) { │ │ │ │ │ + if (this.px == null) { │ │ │ │ │ + this.display(false); │ │ │ │ │ + } else { │ │ │ │ │ + if (this.calculateOffset) { │ │ │ │ │ + this.offset = this.calculateOffset(this.size); │ │ │ │ │ + } │ │ │ │ │ + OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, { │ │ │ │ │ + x: this.px.x + this.offset.x, │ │ │ │ │ + y: this.px.y + this.offset.y │ │ │ │ │ + }); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Method: display │ │ │ │ │ + * Hide or show the icon │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * display - {Boolean} │ │ │ │ │ + */ │ │ │ │ │ + display: function(display) { │ │ │ │ │ + this.imageDiv.style.display = (display) ? "" : "none"; │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: isDrawn │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Boolean} Whether or not the icon is drawn. │ │ │ │ │ + */ │ │ │ │ │ + isDrawn: function() { │ │ │ │ │ + // nodeType 11 for ie, whose nodes *always* have a parentNode │ │ │ │ │ + // (of type document fragment) │ │ │ │ │ + var isDrawn = (this.imageDiv && this.imageDiv.parentNode && │ │ │ │ │ + (this.imageDiv.parentNode.nodeType != 11)); │ │ │ │ │ + │ │ │ │ │ + return isDrawn; │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + CLASS_NAME: "OpenLayers.Icon" │ │ │ │ │ +}); │ │ │ │ │ +/* ====================================================================== │ │ │ │ │ OpenLayers/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. */ │ │ │ │ │ @@ -383,645 +763,377 @@ │ │ │ │ │ requestFrame: requestFrame, │ │ │ │ │ start: start, │ │ │ │ │ stop: stop │ │ │ │ │ }; │ │ │ │ │ │ │ │ │ │ })(window); │ │ │ │ │ /* ====================================================================== │ │ │ │ │ - OpenLayers/BaseTypes/Class.js │ │ │ │ │ + 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/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. │ │ │ │ │ + * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ │ + * @requires OpenLayers/Animation.js │ │ │ │ │ */ │ │ │ │ │ -OpenLayers.inherit = function(C, P) { │ │ │ │ │ - var F = function() {}; │ │ │ │ │ - F.prototype = P.prototype; │ │ │ │ │ - C.prototype = new F; │ │ │ │ │ - var i, l, o; │ │ │ │ │ - for (i = 2, l = arguments.length; i < l; i++) { │ │ │ │ │ - o = arguments[i]; │ │ │ │ │ - if (typeof o === "function") { │ │ │ │ │ - o = o.prototype; │ │ │ │ │ - } │ │ │ │ │ - OpenLayers.Util.extend(C.prototype, o); │ │ │ │ │ - } │ │ │ │ │ -}; │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIFunction: extend │ │ │ │ │ - * Copy all properties of a source object to a destination object. Modifies │ │ │ │ │ - * the passed in destination object. Any properties on the source object │ │ │ │ │ - * that are set to undefined will not be (re)set on the destination object. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * destination - {Object} The object that will be modified │ │ │ │ │ - * source - {Object} The object with properties to be set on the destination │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Object} The destination object. │ │ │ │ │ + * Namespace: OpenLayers.Tween │ │ │ │ │ */ │ │ │ │ │ -OpenLayers.Util = OpenLayers.Util || {}; │ │ │ │ │ -OpenLayers.Util.extend = function(destination, source) { │ │ │ │ │ - destination = destination || {}; │ │ │ │ │ - if (source) { │ │ │ │ │ - for (var property in source) { │ │ │ │ │ - var value = source[property]; │ │ │ │ │ - if (value !== undefined) { │ │ │ │ │ - destination[property] = value; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * IE doesn't include the toString property when iterating over an object's │ │ │ │ │ - * properties with the for(property in object) syntax. Explicitly check if │ │ │ │ │ - * the source has its own toString property. │ │ │ │ │ - */ │ │ │ │ │ - │ │ │ │ │ - /* │ │ │ │ │ - * FF/Windows < 2.0.0.13 reports "Illegal operation on WrappedNative │ │ │ │ │ - * prototype object" when calling hawOwnProperty if the source object │ │ │ │ │ - * is an instance of window.Event. │ │ │ │ │ - */ │ │ │ │ │ - │ │ │ │ │ - var sourceIsEvt = typeof window.Event == "function" && │ │ │ │ │ - source instanceof window.Event; │ │ │ │ │ +OpenLayers.Tween = OpenLayers.Class({ │ │ │ │ │ │ │ │ │ │ - if (!sourceIsEvt && │ │ │ │ │ - source.hasOwnProperty && source.hasOwnProperty("toString")) { │ │ │ │ │ - destination.toString = source.toString; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - return destination; │ │ │ │ │ -}; │ │ │ │ │ -/* ====================================================================== │ │ │ │ │ - OpenLayers/Geometry.js │ │ │ │ │ - ====================================================================== */ │ │ │ │ │ + /** │ │ │ │ │ + * APIProperty: easing │ │ │ │ │ + * {(Function)} Easing equation used for the animation │ │ │ │ │ + * Defaultly set to OpenLayers.Easing.Expo.easeOut │ │ │ │ │ + */ │ │ │ │ │ + easing: null, │ │ │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ - * full text of the license. */ │ │ │ │ │ + /** │ │ │ │ │ + * APIProperty: begin │ │ │ │ │ + * {Object} Values to start the animation with │ │ │ │ │ + */ │ │ │ │ │ + begin: null, │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ │ - */ │ │ │ │ │ + /** │ │ │ │ │ + * APIProperty: finish │ │ │ │ │ + * {Object} Values to finish the animation with │ │ │ │ │ + */ │ │ │ │ │ + finish: null, │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * 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({ │ │ │ │ │ + /** │ │ │ │ │ + * APIProperty: duration │ │ │ │ │ + * {int} duration of the tween (number of steps) │ │ │ │ │ + */ │ │ │ │ │ + duration: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: id │ │ │ │ │ - * {String} A unique identifier for this geometry. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - id: null, │ │ │ │ │ + callbacks: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: parent │ │ │ │ │ - * {}This is set when a Geometry is added as component │ │ │ │ │ - * of another geometry │ │ │ │ │ + * Property: time │ │ │ │ │ + * {int} Step counter │ │ │ │ │ */ │ │ │ │ │ - parent: null, │ │ │ │ │ + time: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: bounds │ │ │ │ │ - * {} The bounds of this geometry │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - bounds: null, │ │ │ │ │ + minFrameRate: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Constructor: OpenLayers.Geometry │ │ │ │ │ - * Creates a geometry object. │ │ │ │ │ + * Property: startTime │ │ │ │ │ + * {Number} The timestamp of the first execution step. Used for skipping │ │ │ │ │ + * frames │ │ │ │ │ */ │ │ │ │ │ - initialize: function() { │ │ │ │ │ - this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_"); │ │ │ │ │ - }, │ │ │ │ │ + startTime: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: destroy │ │ │ │ │ - * Destroy this geometry. │ │ │ │ │ + * Property: animationId │ │ │ │ │ + * {int} Loop id returned by OpenLayers.Animation.start │ │ │ │ │ */ │ │ │ │ │ - destroy: function() { │ │ │ │ │ - this.id = null; │ │ │ │ │ - this.bounds = null; │ │ │ │ │ - }, │ │ │ │ │ + animationId: 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. │ │ │ │ │ + * Property: playing │ │ │ │ │ + * {Boolean} Tells if the easing is currently playing │ │ │ │ │ */ │ │ │ │ │ - clone: function() { │ │ │ │ │ - return new OpenLayers.Geometry(); │ │ │ │ │ + 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; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: setBounds │ │ │ │ │ - * Set the bounds for this Geometry. │ │ │ │ │ + * APIMethod: start │ │ │ │ │ + * Plays the Tween, and calls the callback method on each step │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * bounds - {} │ │ │ │ │ + * 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) │ │ │ │ │ */ │ │ │ │ │ - setBounds: function(bounds) { │ │ │ │ │ - if (bounds) { │ │ │ │ │ - this.bounds = bounds.clone(); │ │ │ │ │ + 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) │ │ │ │ │ + ); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: clearBounds │ │ │ │ │ - * Nullify this components bounds and that of its parent as well. │ │ │ │ │ + * APIMethod: stop │ │ │ │ │ + * Stops the Tween, and calls the done callback │ │ │ │ │ + * Doesn't do anything if animation is already finished │ │ │ │ │ */ │ │ │ │ │ - clearBounds: function() { │ │ │ │ │ - this.bounds = null; │ │ │ │ │ - if (this.parent) { │ │ │ │ │ - this.parent.clearBounds(); │ │ │ │ │ + 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: 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 - {} │ │ │ │ │ + * Method: play │ │ │ │ │ + * Calls the appropriate easing method │ │ │ │ │ */ │ │ │ │ │ - extendBounds: function(newBounds) { │ │ │ │ │ - var bounds = this.getBounds(); │ │ │ │ │ - if (!bounds) { │ │ │ │ │ - this.setBounds(newBounds); │ │ │ │ │ - } else { │ │ │ │ │ - this.bounds.extend(newBounds); │ │ │ │ │ + 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(); │ │ │ │ │ } │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: getBounds │ │ │ │ │ - * Get the bounds for this Geometry. If bounds is not set, it │ │ │ │ │ - * is calculated again, this makes queries faster. │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {} │ │ │ │ │ + * Create empty functions for all easing methods. │ │ │ │ │ */ │ │ │ │ │ - getBounds: function() { │ │ │ │ │ - if (this.bounds == null) { │ │ │ │ │ - this.calculateBounds(); │ │ │ │ │ - } │ │ │ │ │ - return this.bounds; │ │ │ │ │ - }, │ │ │ │ │ + CLASS_NAME: "OpenLayers.Tween" │ │ │ │ │ +}); │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIMethod: calculateBounds │ │ │ │ │ - * Recalculate the bounds for the geometry. │ │ │ │ │ +/** │ │ │ │ │ + * Namespace: OpenLayers.Easing │ │ │ │ │ + * │ │ │ │ │ + * Credits: │ │ │ │ │ + * Easing Equations by Robert Penner, │ │ │ │ │ + */ │ │ │ │ │ +OpenLayers.Easing = { │ │ │ │ │ + /** │ │ │ │ │ + * Create empty functions for all easing methods. │ │ │ │ │ */ │ │ │ │ │ - calculateBounds: function() { │ │ │ │ │ - // │ │ │ │ │ - // This should be overridden by subclasses. │ │ │ │ │ - // │ │ │ │ │ - }, │ │ │ │ │ + CLASS_NAME: "OpenLayers.Easing" │ │ │ │ │ +}; │ │ │ │ │ + │ │ │ │ │ +/** │ │ │ │ │ + * Namespace: OpenLayers.Easing.Linear │ │ │ │ │ + */ │ │ │ │ │ +OpenLayers.Easing.Linear = { │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: distanceTo │ │ │ │ │ - * Calculate the closest distance between two geometries (on the x-y plane). │ │ │ │ │ - * │ │ │ │ │ + * Function: easeIn │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * geometry - {} The target geometry. │ │ │ │ │ - * options - {Object} Optional properties for configuring the distance │ │ │ │ │ - * calculation. │ │ │ │ │ + * t - {Float} time │ │ │ │ │ + * b - {Float} beginning position │ │ │ │ │ + * c - {Float} total change │ │ │ │ │ + * d - {Float} duration of the transition │ │ │ │ │ * │ │ │ │ │ - * 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. │ │ │ │ │ + * {Float} │ │ │ │ │ */ │ │ │ │ │ - distanceTo: function(geometry, options) {}, │ │ │ │ │ + easeIn: function(t, b, c, d) { │ │ │ │ │ + return c * t / d + b; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: getVertices │ │ │ │ │ - * Return a list of all points in this geometry. │ │ │ │ │ - * │ │ │ │ │ + * Function: easeOut │ │ │ │ │ + * │ │ │ │ │ * 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. │ │ │ │ │ + * t - {Float} time │ │ │ │ │ + * b - {Float} beginning position │ │ │ │ │ + * c - {Float} total change │ │ │ │ │ + * d - {Float} duration of the transition │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {Array} A list of all vertices in the geometry. │ │ │ │ │ + * {Float} │ │ │ │ │ */ │ │ │ │ │ - getVertices: function(nodes) {}, │ │ │ │ │ + easeOut: function(t, b, c, d) { │ │ │ │ │ + return c * t / d + b; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: atPoint │ │ │ │ │ - * Note - This is only an approximation based on the bounds of the │ │ │ │ │ - * geometry. │ │ │ │ │ + * Function: easeInOut │ │ │ │ │ * │ │ │ │ │ * 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 │ │ │ │ │ - * │ │ │ │ │ + * t - {Float} time │ │ │ │ │ + * b - {Float} beginning position │ │ │ │ │ + * c - {Float} total change │ │ │ │ │ + * d - {Float} duration of the transition │ │ │ │ │ + * │ │ │ │ │ * Returns: │ │ │ │ │ - * {Boolean} Whether or not the geometry is at the specified location │ │ │ │ │ + * {Float} │ │ │ │ │ */ │ │ │ │ │ - atPoint: function(lonlat, toleranceLon, toleranceLat) { │ │ │ │ │ - var atPoint = false; │ │ │ │ │ - var bounds = this.getBounds(); │ │ │ │ │ - if ((bounds != null) && (lonlat != null)) { │ │ │ │ │ + easeInOut: function(t, b, c, d) { │ │ │ │ │ + return c * t / d + b; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - var dX = (toleranceLon != null) ? toleranceLon : 0; │ │ │ │ │ - var dY = (toleranceLat != null) ? toleranceLat : 0; │ │ │ │ │ + CLASS_NAME: "OpenLayers.Easing.Linear" │ │ │ │ │ +}; │ │ │ │ │ │ │ │ │ │ - var toleranceBounds = │ │ │ │ │ - new OpenLayers.Bounds(this.bounds.left - dX, │ │ │ │ │ - this.bounds.bottom - dY, │ │ │ │ │ - this.bounds.right + dX, │ │ │ │ │ - this.bounds.top + dY); │ │ │ │ │ +/** │ │ │ │ │ + * Namespace: OpenLayers.Easing.Expo │ │ │ │ │ + */ │ │ │ │ │ +OpenLayers.Easing.Expo = { │ │ │ │ │ │ │ │ │ │ - atPoint = toleranceBounds.containsLonLat(lonlat); │ │ │ │ │ - } │ │ │ │ │ - return atPoint; │ │ │ │ │ + /** │ │ │ │ │ + * 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; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: getLength │ │ │ │ │ - * Calculate the length of this geometry. This method is defined in │ │ │ │ │ - * subclasses. │ │ │ │ │ + * Function: easeOut │ │ │ │ │ * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * t - {Float} time │ │ │ │ │ + * b - {Float} beginning position │ │ │ │ │ + * c - {Float} total change │ │ │ │ │ + * d - {Float} duration of the transition │ │ │ │ │ + * │ │ │ │ │ * Returns: │ │ │ │ │ - * {Float} The length of the collection by summing its parts │ │ │ │ │ + * {Float} │ │ │ │ │ */ │ │ │ │ │ - getLength: function() { │ │ │ │ │ - //to be overridden by geometries that actually have a length │ │ │ │ │ - // │ │ │ │ │ - return 0.0; │ │ │ │ │ + easeOut: function(t, b, c, d) { │ │ │ │ │ + return (t == d) ? b + c : c * (-Math.pow(2, -10 * t / d) + 1) + b; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: getArea │ │ │ │ │ - * Calculate the area of this geometry. This method is defined in subclasses. │ │ │ │ │ + * Function: easeInOut │ │ │ │ │ * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * t - {Float} time │ │ │ │ │ + * b - {Float} beginning position │ │ │ │ │ + * c - {Float} total change │ │ │ │ │ + * d - {Float} duration of the transition │ │ │ │ │ + * │ │ │ │ │ * Returns: │ │ │ │ │ - * {Float} The area of the collection by summing its parts │ │ │ │ │ + * {Float} │ │ │ │ │ */ │ │ │ │ │ - getArea: function() { │ │ │ │ │ - //to be overridden by geometries that actually have an area │ │ │ │ │ - // │ │ │ │ │ - return 0.0; │ │ │ │ │ + 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 = { │ │ │ │ │ + │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: getCentroid │ │ │ │ │ - * Calculate the centroid of this geometry. This method is defined in subclasses. │ │ │ │ │ + * Function: easeIn │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * t - {Float} time │ │ │ │ │ + * b - {Float} beginning position │ │ │ │ │ + * c - {Float} total change │ │ │ │ │ + * d - {Float} duration of the transition │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {} The centroid of the collection │ │ │ │ │ + * {Float} │ │ │ │ │ */ │ │ │ │ │ - getCentroid: function() { │ │ │ │ │ - return null; │ │ │ │ │ + easeIn: function(t, b, c, d) { │ │ │ │ │ + return c * (t /= d) * t + b; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * Function: easeOut │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * t - {Float} time │ │ │ │ │ + * b - {Float} beginning position │ │ │ │ │ + * c - {Float} total change │ │ │ │ │ + * d - {Float} duration of the transition │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {String} String representation of this geometry. │ │ │ │ │ + * {Float} │ │ │ │ │ */ │ │ │ │ │ - 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; │ │ │ │ │ + easeOut: function(t, b, c, d) { │ │ │ │ │ + return -c * (t /= d) * (t - 2) + b; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - 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 < len; ++i) { │ │ │ │ │ - components[i] = result[i].geometry; │ │ │ │ │ - } │ │ │ │ │ - geom = new OpenLayers.Geometry.Collection(components); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - return geom; │ │ │ │ │ -}; │ │ │ │ │ - │ │ │ │ │ -/** │ │ │ │ │ - * Method: OpenLayers.Geometry.segmentsIntersect │ │ │ │ │ - * Determine whether two line segments intersect. Optionally calculates │ │ │ │ │ - * and returns the intersection point. This function is optimized for │ │ │ │ │ - * cases where seg1.x2 >= seg2.x1 || seg2.x2 >= seg1.x1. In those │ │ │ │ │ - * obvious cases where there is no intersection, the function should │ │ │ │ │ - * not be called. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * seg1 - {Object} Object representing a segment with properties x1, y1, x2, │ │ │ │ │ - * and y2. The start point is represented by x1 and y1. The end point │ │ │ │ │ - * is represented by x2 and y2. Start and end are ordered so that x1 < x2. │ │ │ │ │ - * seg2 - {Object} Object representing a segment with properties x1, y1, x2, │ │ │ │ │ - * and y2. The start point is represented by x1 and y1. The end point │ │ │ │ │ - * is represented by x2 and y2. Start and end are ordered so that x1 < x2. │ │ │ │ │ - * options - {Object} Optional properties for calculating the intersection. │ │ │ │ │ - * │ │ │ │ │ - * Valid options: │ │ │ │ │ - * point - {Boolean} Return the intersection point. If false, the actual │ │ │ │ │ - * intersection point will not be calculated. If true and the segments │ │ │ │ │ - * intersect, the intersection point will be returned. If true and │ │ │ │ │ - * the segments do not intersect, false will be returned. If true and │ │ │ │ │ - * the segments are coincident, true will be returned. │ │ │ │ │ - * tolerance - {Number} If a non-null value is provided, if the segments are │ │ │ │ │ - * within the tolerance distance, this will be considered an intersection. │ │ │ │ │ - * In addition, if the point option is true and the calculated intersection │ │ │ │ │ - * is within the tolerance distance of an end point, the endpoint will be │ │ │ │ │ - * returned instead of the calculated intersection. Further, if the │ │ │ │ │ - * intersection is within the tolerance of endpoints on both segments, or │ │ │ │ │ - * if two segment endpoints are within the tolerance distance of eachother │ │ │ │ │ - * (but no intersection is otherwise calculated), an endpoint on the │ │ │ │ │ - * first segment provided will be returned. │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Boolean | } 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: 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; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * 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 │ │ │ │ │ - }; │ │ │ │ │ + CLASS_NAME: "OpenLayers.Easing.Quad" │ │ │ │ │ }; │ │ │ │ │ /* ====================================================================== │ │ │ │ │ OpenLayers/BaseTypes.js │ │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ @@ -5922,12905 +6034,10200 @@ │ │ │ │ │ │ │ │ │ │ }, │ │ │ │ │ 'delete': { │ │ │ │ │ display: "none" │ │ │ │ │ } │ │ │ │ │ }; │ │ │ │ │ /* ====================================================================== │ │ │ │ │ - OpenLayers/Format.js │ │ │ │ │ + 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.Format │ │ │ │ │ - * Base class for format reading/writing a variety of formats. Subclasses │ │ │ │ │ - * of OpenLayers.Format are expected to have read and write methods. │ │ │ │ │ + * Class: OpenLayers.Style │ │ │ │ │ + * This class represents a UserStyle obtained │ │ │ │ │ + * from a SLD, containing styling rules. │ │ │ │ │ */ │ │ │ │ │ -OpenLayers.Format = OpenLayers.Class({ │ │ │ │ │ +OpenLayers.Style = OpenLayers.Class({ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: options │ │ │ │ │ - * {Object} A reference to options passed to the constructor. │ │ │ │ │ + * Property: id │ │ │ │ │ + * {String} A unique id for this session. │ │ │ │ │ */ │ │ │ │ │ - options: null, │ │ │ │ │ + id: 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. │ │ │ │ │ + * APIProperty: name │ │ │ │ │ + * {String} │ │ │ │ │ */ │ │ │ │ │ - externalProjection: null, │ │ │ │ │ + name: 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. │ │ │ │ │ + * Property: title │ │ │ │ │ + * {String} Title of this style (set if included in SLD) │ │ │ │ │ */ │ │ │ │ │ - internalProjection: null, │ │ │ │ │ + title: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: data │ │ │ │ │ - * {Object} When is true, this is the parsed string sent to │ │ │ │ │ - * . │ │ │ │ │ + * Property: description │ │ │ │ │ + * {String} Description of this style (set if abstract is included in SLD) │ │ │ │ │ */ │ │ │ │ │ - data: null, │ │ │ │ │ + description: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: keepData │ │ │ │ │ - * {Object} Maintain a reference () to the most recently read data. │ │ │ │ │ - * Default is false. │ │ │ │ │ + * APIProperty: layerName │ │ │ │ │ + * {} name of the layer that this style belongs to, usually │ │ │ │ │ + * according to the NamedLayer attribute of an SLD document. │ │ │ │ │ */ │ │ │ │ │ - keepData: false, │ │ │ │ │ + layerName: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Constructor: OpenLayers.Format │ │ │ │ │ - * Instances of this class are not useful. See one of the subclasses. │ │ │ │ │ + * 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 │ │ │ │ │ - * format │ │ │ │ │ + * style. │ │ │ │ │ * │ │ │ │ │ * Valid options: │ │ │ │ │ - * keepData - {Boolean} If true, upon , the data property will be │ │ │ │ │ - * set to the parsed object (e.g. the json or xml object). │ │ │ │ │ - * │ │ │ │ │ + * rules - {Array()} List of rules to be added to the │ │ │ │ │ + * style. │ │ │ │ │ + * │ │ │ │ │ * Returns: │ │ │ │ │ - * An instance of OpenLayers.Format │ │ │ │ │ + * {} │ │ │ │ │ */ │ │ │ │ │ - initialize: function(options) { │ │ │ │ │ + initialize: function(style, options) { │ │ │ │ │ + │ │ │ │ │ OpenLayers.Util.extend(this, options); │ │ │ │ │ - this.options = 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 │ │ │ │ │ - * Clean up. │ │ │ │ │ + * nullify references to prevent circular references and memory leaks │ │ │ │ │ */ │ │ │ │ │ - destroy: function() {}, │ │ │ │ │ + destroy: function() { │ │ │ │ │ + for (var i = 0, len = this.rules.length; i < len; i++) { │ │ │ │ │ + this.rules[i].destroy(); │ │ │ │ │ + this.rules[i] = null; │ │ │ │ │ + } │ │ │ │ │ + this.rules = null; │ │ │ │ │ + this.defaultStyle = null; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: read │ │ │ │ │ - * Read data from a string, and return an object whose type depends on the │ │ │ │ │ - * subclass. │ │ │ │ │ + * Method: createSymbolizer │ │ │ │ │ + * creates a style by applying all feature-dependent rules to the base │ │ │ │ │ + * style. │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * data - {string} Data to read/parse. │ │ │ │ │ - * │ │ │ │ │ + * feature - {} feature to evaluate rules for │ │ │ │ │ + * │ │ │ │ │ * Returns: │ │ │ │ │ - * Depends on the subclass │ │ │ │ │ + * {Object} symbolizer hash │ │ │ │ │ */ │ │ │ │ │ - read: function(data) { │ │ │ │ │ - throw new Error('Read not implemented.'); │ │ │ │ │ + createSymbolizer: function(feature) { │ │ │ │ │ + var style = this.defaultsPerSymbolizer ? {} : this.createLiterals( │ │ │ │ │ + OpenLayers.Util.extend({}, this.defaultStyle), feature); │ │ │ │ │ + │ │ │ │ │ + var rules = this.rules; │ │ │ │ │ + │ │ │ │ │ + var rule, context; │ │ │ │ │ + var elseRules = []; │ │ │ │ │ + var appliedRules = false; │ │ │ │ │ + for (var i = 0, len = rules.length; i < len; i++) { │ │ │ │ │ + rule = rules[i]; │ │ │ │ │ + // does the rule apply? │ │ │ │ │ + var applies = rule.evaluate(feature); │ │ │ │ │ + │ │ │ │ │ + if (applies) { │ │ │ │ │ + if (rule instanceof OpenLayers.Rule && rule.elseFilter) { │ │ │ │ │ + elseRules.push(rule); │ │ │ │ │ + } else { │ │ │ │ │ + appliedRules = true; │ │ │ │ │ + this.applySymbolizer(rule, style, feature); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + // if no other rules apply, apply the rules with else filters │ │ │ │ │ + if (appliedRules == false && elseRules.length > 0) { │ │ │ │ │ + appliedRules = true; │ │ │ │ │ + for (var i = 0, len = elseRules.length; i < len; i++) { │ │ │ │ │ + this.applySymbolizer(elseRules[i], style, feature); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + // don't display if there were rules but none applied │ │ │ │ │ + if (rules.length > 0 && appliedRules == false) { │ │ │ │ │ + style.display = "none"; │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + if (style.label != null && typeof style.label !== "string") { │ │ │ │ │ + style.label = String(style.label); │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + return style; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: write │ │ │ │ │ - * Accept an object, and return a string. │ │ │ │ │ + * Method: applySymbolizer │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * object - {Object} Object to be serialized │ │ │ │ │ + * rule - {} │ │ │ │ │ + * style - {Object} │ │ │ │ │ + * feature - {} │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {String} A string representation of the object. │ │ │ │ │ + * {Object} A style with new symbolizer applied. │ │ │ │ │ */ │ │ │ │ │ - write: function(object) { │ │ │ │ │ - throw new Error('Write not implemented.'); │ │ │ │ │ - }, │ │ │ │ │ - │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format" │ │ │ │ │ -}); │ │ │ │ │ -/* ====================================================================== │ │ │ │ │ - OpenLayers/Geometry/Point.js │ │ │ │ │ - ====================================================================== */ │ │ │ │ │ + applySymbolizer: function(rule, style, feature) { │ │ │ │ │ + var symbolizerPrefix = feature.geometry ? │ │ │ │ │ + this.getSymbolizerPrefix(feature.geometry) : │ │ │ │ │ + OpenLayers.Style.SYMBOLIZER_PREFIXES[0]; │ │ │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ - * full list of 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 symbolizer = rule.symbolizer[symbolizerPrefix] || rule.symbolizer; │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * @requires OpenLayers/Geometry.js │ │ │ │ │ - */ │ │ │ │ │ + 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 │ │ │ │ │ + }); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * Class: OpenLayers.Geometry.Point │ │ │ │ │ - * Point geometry class. │ │ │ │ │ - * │ │ │ │ │ - * Inherits from: │ │ │ │ │ - * - │ │ │ │ │ - */ │ │ │ │ │ -OpenLayers.Geometry.Point = OpenLayers.Class(OpenLayers.Geometry, { │ │ │ │ │ + // merge the style with the current style │ │ │ │ │ + return this.createLiterals( │ │ │ │ │ + OpenLayers.Util.extend(style, symbolizer), feature); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIProperty: x │ │ │ │ │ - * {float} │ │ │ │ │ + /** │ │ │ │ │ + * 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 │ │ │ │ │ */ │ │ │ │ │ - x: null, │ │ │ │ │ + createLiterals: function(style, feature) { │ │ │ │ │ + var context = OpenLayers.Util.extend({}, feature.attributes || feature.data); │ │ │ │ │ + OpenLayers.Util.extend(context, this.context); │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIProperty: y │ │ │ │ │ - * {float} │ │ │ │ │ - */ │ │ │ │ │ - y: null, │ │ │ │ │ + for (var i in this.propertyStyles) { │ │ │ │ │ + style[i] = OpenLayers.Style.createLiteral(style[i], context, feature, i); │ │ │ │ │ + } │ │ │ │ │ + return style; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Constructor: OpenLayers.Geometry.Point │ │ │ │ │ - * Construct a point geometry. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * x - {float} │ │ │ │ │ - * y - {float} │ │ │ │ │ + * 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; │ │ │ │ │ */ │ │ │ │ │ - initialize: function(x, y) { │ │ │ │ │ - OpenLayers.Geometry.prototype.initialize.apply(this, arguments); │ │ │ │ │ + findPropertyStyles: function() { │ │ │ │ │ + var propertyStyles = {}; │ │ │ │ │ │ │ │ │ │ - this.x = parseFloat(x); │ │ │ │ │ - this.y = parseFloat(y); │ │ │ │ │ + // check the default style │ │ │ │ │ + var style = this.defaultStyle; │ │ │ │ │ + this.addPropertyStyles(propertyStyles, style); │ │ │ │ │ + │ │ │ │ │ + // walk through all rules to check for properties in their symbolizer │ │ │ │ │ + var rules = this.rules; │ │ │ │ │ + var symbolizer, value; │ │ │ │ │ + for (var i = 0, len = rules.length; i < len; i++) { │ │ │ │ │ + symbolizer = rules[i].symbolizer; │ │ │ │ │ + for (var key in symbolizer) { │ │ │ │ │ + value = symbolizer[key]; │ │ │ │ │ + if (typeof value == "object") { │ │ │ │ │ + // symbolizer key is "Point", "Line" or "Polygon" │ │ │ │ │ + this.addPropertyStyles(propertyStyles, value); │ │ │ │ │ + } else { │ │ │ │ │ + // symbolizer is a hash of style properties │ │ │ │ │ + this.addPropertyStyles(propertyStyles, symbolizer); │ │ │ │ │ + break; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + return propertyStyles; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: clone │ │ │ │ │ + * Method: addPropertyStyles │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * propertyStyles - {Object} hash to add new property styles to. Will be │ │ │ │ │ + * modified inline │ │ │ │ │ + * symbolizer - {Object} search this symbolizer for property styles │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {} An exact clone of this OpenLayers.Geometry.Point │ │ │ │ │ + * {Object} propertyStyles hash │ │ │ │ │ */ │ │ │ │ │ - clone: function(obj) { │ │ │ │ │ - if (obj == null) { │ │ │ │ │ - obj = new OpenLayers.Geometry.Point(this.x, this.y); │ │ │ │ │ + addPropertyStyles: function(propertyStyles, symbolizer) { │ │ │ │ │ + var property; │ │ │ │ │ + for (var key in symbolizer) { │ │ │ │ │ + property = symbolizer[key]; │ │ │ │ │ + if (typeof property == "string" && │ │ │ │ │ + property.match(/\$\{\w+\}/)) { │ │ │ │ │ + propertyStyles[key] = true; │ │ │ │ │ + } │ │ │ │ │ } │ │ │ │ │ + return propertyStyles; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - // catch any randomly tagged-on properties │ │ │ │ │ - OpenLayers.Util.applyDefaults(obj, this); │ │ │ │ │ - │ │ │ │ │ - return obj; │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: addRules │ │ │ │ │ + * Adds rules to this style. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * rules - {Array()} │ │ │ │ │ + */ │ │ │ │ │ + addRules: function(rules) { │ │ │ │ │ + Array.prototype.push.apply(this.rules, rules); │ │ │ │ │ + this.propertyStyles = this.findPropertyStyles(); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: calculateBounds │ │ │ │ │ - * Create a new Bounds based on the lon/lat │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: setDefaultStyle │ │ │ │ │ + * Sets the default style for this style object. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * style - {Object} Hash of style properties │ │ │ │ │ */ │ │ │ │ │ - calculateBounds: function() { │ │ │ │ │ - this.bounds = new OpenLayers.Bounds(this.x, this.y, │ │ │ │ │ - this.x, this.y); │ │ │ │ │ + setDefaultStyle: function(style) { │ │ │ │ │ + this.defaultStyle = style; │ │ │ │ │ + this.propertyStyles = this.findPropertyStyles(); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: distanceTo │ │ │ │ │ - * Calculate the closest distance between two geometries (on the x-y plane). │ │ │ │ │ - * │ │ │ │ │ + * Method: getSymbolizerPrefix │ │ │ │ │ + * Returns the correct symbolizer prefix according to the │ │ │ │ │ + * geometry type of the passed geometry │ │ │ │ │ + * │ │ │ │ │ * 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. │ │ │ │ │ - * │ │ │ │ │ + * geometry - {} │ │ │ │ │ + * │ │ │ │ │ * 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. │ │ │ │ │ + * {String} key of the according symbolizer │ │ │ │ │ */ │ │ │ │ │ - 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 │ │ │ │ │ - }; │ │ │ │ │ + getSymbolizerPrefix: function(geometry) { │ │ │ │ │ + var prefixes = OpenLayers.Style.SYMBOLIZER_PREFIXES; │ │ │ │ │ + for (var i = 0, len = prefixes.length; i < len; i++) { │ │ │ │ │ + if (geometry.CLASS_NAME.indexOf(prefixes[i]) != -1) { │ │ │ │ │ + return prefixes[i]; │ │ │ │ │ } │ │ │ │ │ } │ │ │ │ │ - return result; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIMethod: equals │ │ │ │ │ - * Determine whether another geometry is equivalent to this one. Geometries │ │ │ │ │ - * are considered equivalent if all components have the same coordinates. │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: clone │ │ │ │ │ + * Clones this style. │ │ │ │ │ * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * geom - {} The geometry to test. │ │ │ │ │ - * │ │ │ │ │ * Returns: │ │ │ │ │ - * {Boolean} The supplied geometry is equivalent to this geometry. │ │ │ │ │ + * {} Clone of this style. │ │ │ │ │ */ │ │ │ │ │ - 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))); │ │ │ │ │ + clone: function() { │ │ │ │ │ + var options = OpenLayers.Util.extend({}, this); │ │ │ │ │ + // clone rules │ │ │ │ │ + if (this.rules) { │ │ │ │ │ + options.rules = []; │ │ │ │ │ + for (var i = 0, len = this.rules.length; i < len; ++i) { │ │ │ │ │ + options.rules.push(this.rules[i].clone()); │ │ │ │ │ + } │ │ │ │ │ } │ │ │ │ │ - return equals; │ │ │ │ │ + // clone context │ │ │ │ │ + options.context = this.context && OpenLayers.Util.extend({}, this.context); │ │ │ │ │ + //clone default style │ │ │ │ │ + var defaultStyle = OpenLayers.Util.extend({}, this.defaultStyle); │ │ │ │ │ + return new OpenLayers.Style(defaultStyle, options); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ + CLASS_NAME: "OpenLayers.Style" │ │ │ │ │ +}); │ │ │ │ │ + │ │ │ │ │ + │ │ │ │ │ +/** │ │ │ │ │ + * Function: createLiteral │ │ │ │ │ + * converts a style value holding a combination of PropertyName and Literal │ │ │ │ │ + * into a Literal, taking the property values from the passed features. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * value - {String} value to parse. If this string contains a construct like │ │ │ │ │ + * "foo ${bar}", then "foo " will be taken as literal, and "${bar}" │ │ │ │ │ + * will be replaced by the value of the "bar" attribute of the passed │ │ │ │ │ + * feature. │ │ │ │ │ + * context - {Object} context to take attribute values from │ │ │ │ │ + * feature - {} 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/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({ │ │ │ │ │ + │ │ │ │ │ /** │ │ │ │ │ - * Method: toShortString │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {String} Shortened String representation of Point object. │ │ │ │ │ - * (ex. "5, 42") │ │ │ │ │ + * Property: id │ │ │ │ │ + * {String} A unique id for this session. │ │ │ │ │ */ │ │ │ │ │ - toShortString: function() { │ │ │ │ │ - return (this.x + ", " + this.y); │ │ │ │ │ - }, │ │ │ │ │ + id: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * APIProperty: name │ │ │ │ │ + * {String} name of this rule │ │ │ │ │ */ │ │ │ │ │ - move: function(x, y) { │ │ │ │ │ - this.x = this.x + x; │ │ │ │ │ - this.y = this.y + y; │ │ │ │ │ - this.clearBounds(); │ │ │ │ │ - }, │ │ │ │ │ + name: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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 │ │ │ │ │ + * Property: title │ │ │ │ │ + * {String} Title of this rule (set if included in SLD) │ │ │ │ │ */ │ │ │ │ │ - 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(); │ │ │ │ │ - }, │ │ │ │ │ + title: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: getCentroid │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {} The centroid of the collection │ │ │ │ │ + * Property: description │ │ │ │ │ + * {String} Description of this rule (set if abstract is included in SLD) │ │ │ │ │ */ │ │ │ │ │ - getCentroid: function() { │ │ │ │ │ - return new OpenLayers.Geometry.Point(this.x, this.y); │ │ │ │ │ - }, │ │ │ │ │ + description: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * 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: │ │ │ │ │ - * 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. │ │ │ │ │ + * options - {Object} An optional object with properties to set on the │ │ │ │ │ + * rule │ │ │ │ │ * │ │ │ │ │ * 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; │ │ │ │ │ + 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: intersects │ │ │ │ │ - * Determine if the input geometry intersects this one. │ │ │ │ │ - * │ │ │ │ │ + * APIMethod: evaluate │ │ │ │ │ + * evaluates this rule for a specific feature │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * geometry - {} Any type of geometry. │ │ │ │ │ - * │ │ │ │ │ + * feature - {} feature to apply the rule to. │ │ │ │ │ + * │ │ │ │ │ * Returns: │ │ │ │ │ - * {Boolean} The input geometry intersects this one. │ │ │ │ │ + * {Boolean} true if the rule applies, false if it does not. │ │ │ │ │ + * This rule is the default rule and always returns true. │ │ │ │ │ */ │ │ │ │ │ - intersects: function(geometry) { │ │ │ │ │ - var intersect = false; │ │ │ │ │ - if (geometry.CLASS_NAME == "OpenLayers.Geometry.Point") { │ │ │ │ │ - intersect = this.equals(geometry); │ │ │ │ │ - } else { │ │ │ │ │ - intersect = geometry.intersects(this); │ │ │ │ │ + evaluate: function(feature) { │ │ │ │ │ + var context = this.getContext(feature); │ │ │ │ │ + var applies = true; │ │ │ │ │ + │ │ │ │ │ + if (this.minScaleDenominator || this.maxScaleDenominator) { │ │ │ │ │ + var scale = feature.layer.map.getScale(); │ │ │ │ │ } │ │ │ │ │ - return intersect; │ │ │ │ │ + │ │ │ │ │ + // 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; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: transform │ │ │ │ │ - * Translate the x,y properties of the point from source to dest. │ │ │ │ │ + * Method: getContext │ │ │ │ │ + * Gets the context for evaluating this rule │ │ │ │ │ * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * source - {} │ │ │ │ │ - * dest - {} │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - transform: function(source, dest) { │ │ │ │ │ - if ((source && dest)) { │ │ │ │ │ - OpenLayers.Projection.transform( │ │ │ │ │ - this, source, dest); │ │ │ │ │ - this.bounds = null; │ │ │ │ │ + clone: function() { │ │ │ │ │ + var options = OpenLayers.Util.extend({}, this); │ │ │ │ │ + if (this.symbolizers) { │ │ │ │ │ + // clone symbolizers │ │ │ │ │ + var len = this.symbolizers.length; │ │ │ │ │ + options.symbolizers = new Array(len); │ │ │ │ │ + for (var i = 0; i < len; ++i) { │ │ │ │ │ + options.symbolizers[i] = this.symbolizers[i].clone(); │ │ │ │ │ + } │ │ │ │ │ + } else { │ │ │ │ │ + // clone symbolizer │ │ │ │ │ + options.symbolizer = {}; │ │ │ │ │ + var value, type; │ │ │ │ │ + for (var key in this.symbolizer) { │ │ │ │ │ + value = this.symbolizer[key]; │ │ │ │ │ + type = typeof value; │ │ │ │ │ + if (type === "object") { │ │ │ │ │ + options.symbolizer[key] = OpenLayers.Util.extend({}, value); │ │ │ │ │ + } else if (type === "string") { │ │ │ │ │ + options.symbolizer[key] = value; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ } │ │ │ │ │ - return this; │ │ │ │ │ + // clone filter │ │ │ │ │ + options.filter = this.filter && this.filter.clone(); │ │ │ │ │ + // clone context │ │ │ │ │ + options.context = this.context && OpenLayers.Util.extend({}, this.context); │ │ │ │ │ + return new OpenLayers.Rule(options); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ + CLASS_NAME: "OpenLayers.Rule" │ │ │ │ │ +}); │ │ │ │ │ +/* ====================================================================== │ │ │ │ │ + OpenLayers/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({ │ │ │ │ │ + │ │ │ │ │ + │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: getVertices │ │ │ │ │ - * Return a list of all points in this geometry. │ │ │ │ │ + * 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: │ │ │ │ │ - * 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. │ │ │ │ │ + * config - {Object} An object containing properties to be set on the │ │ │ │ │ + * symbolizer. Any documented symbolizer property can be set at │ │ │ │ │ + * construction. │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {Array} A list of all vertices in the geometry. │ │ │ │ │ + * A new symbolizer. │ │ │ │ │ */ │ │ │ │ │ - getVertices: function(nodes) { │ │ │ │ │ - return [this]; │ │ │ │ │ + initialize: function(config) { │ │ │ │ │ + OpenLayers.Util.extend(this, config); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Geometry.Point" │ │ │ │ │ + /** │ │ │ │ │ + * 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/Geometry/Collection.js │ │ │ │ │ + OpenLayers/Symbolizer/Point.js │ │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * @requires OpenLayers/Geometry.js │ │ │ │ │ + * @requires OpenLayers/Symbolizer.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: │ │ │ │ │ - * - │ │ │ │ │ + * Class: OpenLayers.Symbolizer.Point │ │ │ │ │ + * A symbolizer used to render point features. │ │ │ │ │ */ │ │ │ │ │ -OpenLayers.Geometry.Collection = OpenLayers.Class(OpenLayers.Geometry, { │ │ │ │ │ +OpenLayers.Symbolizer.Point = OpenLayers.Class(OpenLayers.Symbolizer, { │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: components │ │ │ │ │ - * {Array()} The component parts of this geometry │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - 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. │ │ │ │ │ + * APIProperty: strokeOpacity │ │ │ │ │ + * {Number} Stroke opacity (0-1). │ │ │ │ │ + * │ │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ */ │ │ │ │ │ - componentTypes: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Constructor: OpenLayers.Geometry.Collection │ │ │ │ │ - * Creates a Geometry Collection -- a list of geoms. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * components - {Array()} Optional array of geometries │ │ │ │ │ - * │ │ │ │ │ + * APIProperty: strokeWidth │ │ │ │ │ + * {Number} Pixel stroke width. │ │ │ │ │ + * │ │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ */ │ │ │ │ │ - initialize: function(components) { │ │ │ │ │ - OpenLayers.Geometry.prototype.initialize.apply(this, arguments); │ │ │ │ │ - this.components = []; │ │ │ │ │ - if (components != null) { │ │ │ │ │ - this.addComponents(components); │ │ │ │ │ - } │ │ │ │ │ - }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: destroy │ │ │ │ │ - * Destroy this geometry. │ │ │ │ │ + * APIProperty: strokeLinecap │ │ │ │ │ + * {String} Stroke cap type ("butt", "round", or "square"). │ │ │ │ │ + * │ │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ */ │ │ │ │ │ - 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 │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - clone: function() { │ │ │ │ │ - var geometry = eval("new " + this.CLASS_NAME + "()"); │ │ │ │ │ - for (var i = 0, len = this.components.length; i < len; i++) { │ │ │ │ │ - geometry.addComponent(this.components[i].clone()); │ │ │ │ │ - } │ │ │ │ │ │ │ │ │ │ - // catch any randomly tagged-on properties │ │ │ │ │ - OpenLayers.Util.applyDefaults(geometry, this); │ │ │ │ │ + /** │ │ │ │ │ + * APIProperty: fillColor │ │ │ │ │ + * {String} RGB hex fill color (e.g. "#ff0000" for red). │ │ │ │ │ + * │ │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ + */ │ │ │ │ │ │ │ │ │ │ - return geometry; │ │ │ │ │ - }, │ │ │ │ │ + /** │ │ │ │ │ + * APIProperty: fillOpacity │ │ │ │ │ + * {Number} Fill opacity (0-1). │ │ │ │ │ + * │ │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ + */ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: getComponentsString │ │ │ │ │ - * Get a string representing the components for this collection │ │ │ │ │ + * APIProperty: pointRadius │ │ │ │ │ + * {Number} Pixel point radius. │ │ │ │ │ * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {String} A string representation of the components of this geometry │ │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ */ │ │ │ │ │ - getComponentsString: function() { │ │ │ │ │ - var strings = []; │ │ │ │ │ - for (var i = 0, len = this.components.length; i < len; i++) { │ │ │ │ │ - strings.push(this.components[i].toShortString()); │ │ │ │ │ - } │ │ │ │ │ - return strings.join(","); │ │ │ │ │ - }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: calculateBounds │ │ │ │ │ - * Recalculate the bounds by iterating through the components and │ │ │ │ │ - * calling calling extendBounds() on each item. │ │ │ │ │ + * APIProperty: externalGraphic │ │ │ │ │ + * {String} Url to an external graphic that will be used for rendering │ │ │ │ │ + * points. │ │ │ │ │ + * │ │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ */ │ │ │ │ │ - calculateBounds: function() { │ │ │ │ │ - this.bounds = null; │ │ │ │ │ - var bounds = new OpenLayers.Bounds(); │ │ │ │ │ - var components = this.components; │ │ │ │ │ - if (components) { │ │ │ │ │ - for (var i = 0, len = components.length; i < len; i++) { │ │ │ │ │ - bounds.extend(components[i].getBounds()); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - // to preserve old behavior, we only set bounds if non-null │ │ │ │ │ - // in the future, we could add bounds.isEmpty() │ │ │ │ │ - if (bounds.left != null && bounds.bottom != null && │ │ │ │ │ - bounds.right != null && bounds.top != null) { │ │ │ │ │ - this.setBounds(bounds); │ │ │ │ │ - } │ │ │ │ │ - }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: addComponents │ │ │ │ │ - * Add components to this geometry. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * components - {Array()} An array of geometries to add │ │ │ │ │ + * APIProperty: graphicWidth │ │ │ │ │ + * {Number} Pixel width for sizing an external graphic. │ │ │ │ │ + * │ │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ */ │ │ │ │ │ - addComponents: function(components) { │ │ │ │ │ - if (!(OpenLayers.Util.isArray(components))) { │ │ │ │ │ - components = [components]; │ │ │ │ │ - } │ │ │ │ │ - for (var i = 0, len = components.length; i < len; i++) { │ │ │ │ │ - this.addComponent(components[i]); │ │ │ │ │ - } │ │ │ │ │ - }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: addComponent │ │ │ │ │ - * Add a new component (geometry) to the collection. If this.componentTypes │ │ │ │ │ - * is set, then the component class name must be in the componentTypes array. │ │ │ │ │ - * │ │ │ │ │ - * The bounds cache is reset. │ │ │ │ │ + * APIProperty: graphicHeight │ │ │ │ │ + * {Number} Pixel height for sizing an external graphic. │ │ │ │ │ * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * component - {} A geometry to add │ │ │ │ │ - * index - {int} Optional index into the array to insert the component │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Boolean} The component geometry was successfully added │ │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ */ │ │ │ │ │ - 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; │ │ │ │ │ - }, │ │ │ │ │ + /** │ │ │ │ │ + * APIProperty: graphicOpacity │ │ │ │ │ + * {Number} Opacity (0-1) for an external graphic. │ │ │ │ │ + * │ │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ + */ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: removeComponents │ │ │ │ │ - * Remove components from this geometry. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * components - {Array()} The components to be removed │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Boolean} A component was removed. │ │ │ │ │ + * APIProperty: graphicXOffset │ │ │ │ │ + * {Number} Pixel offset along the positive x axis for displacing an │ │ │ │ │ + * external graphic. │ │ │ │ │ + * │ │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ */ │ │ │ │ │ - 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; │ │ │ │ │ - }, │ │ │ │ │ + /** │ │ │ │ │ + * APIProperty: graphicYOffset │ │ │ │ │ + * {Number} Pixel offset along the positive y axis for displacing an │ │ │ │ │ + * external graphic. │ │ │ │ │ + * │ │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ + */ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: removeComponent │ │ │ │ │ - * Remove a component from this geometry. │ │ │ │ │ + * APIProperty: rotation │ │ │ │ │ + * {Number} The rotation of a graphic in the clockwise direction about its │ │ │ │ │ + * center point (or any point off center as specified by │ │ │ │ │ + * and ). │ │ │ │ │ + * │ │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ + */ │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * APIProperty: graphicName │ │ │ │ │ + * {String} Named graphic to use when rendering points. Supported values │ │ │ │ │ + * include "circle", "square", "star", "x", "cross", and "triangle". │ │ │ │ │ + * │ │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ + */ │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Constructor: OpenLayers.Symbolizer.Point │ │ │ │ │ + * Create a symbolizer for rendering points. │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * component - {} │ │ │ │ │ + * config - {Object} An object containing properties to be set on the │ │ │ │ │ + * symbolizer. Any documented symbolizer property can be set at │ │ │ │ │ + * construction. │ │ │ │ │ * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Boolean} The component was removed. │ │ │ │ │ + * Returns: │ │ │ │ │ + * A new point symbolizer. │ │ │ │ │ */ │ │ │ │ │ - removeComponent: function(component) { │ │ │ │ │ + initialize: function(config) { │ │ │ │ │ + OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - OpenLayers.Util.removeItem(this.components, component); │ │ │ │ │ + CLASS_NAME: "OpenLayers.Symbolizer.Point" │ │ │ │ │ │ │ │ │ │ - // clearBounds() so that it gets recalculated on the next call │ │ │ │ │ - // to this.getBounds(); │ │ │ │ │ - this.clearBounds(); │ │ │ │ │ - return true; │ │ │ │ │ - }, │ │ │ │ │ +}); │ │ │ │ │ + │ │ │ │ │ +/* ====================================================================== │ │ │ │ │ + OpenLayers/Symbolizer/Line.js │ │ │ │ │ + ====================================================================== */ │ │ │ │ │ + │ │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ + * full text of the license. */ │ │ │ │ │ + │ │ │ │ │ +/** │ │ │ │ │ + * @requires OpenLayers/Symbolizer.js │ │ │ │ │ + */ │ │ │ │ │ + │ │ │ │ │ +/** │ │ │ │ │ + * Class: OpenLayers.Symbolizer.Line │ │ │ │ │ + * A symbolizer used to render line features. │ │ │ │ │ + */ │ │ │ │ │ +OpenLayers.Symbolizer.Line = OpenLayers.Class(OpenLayers.Symbolizer, { │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: getLength │ │ │ │ │ - * Calculate the length of this geometry │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Float} The length of the geometry │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - getLength: function() { │ │ │ │ │ - var length = 0.0; │ │ │ │ │ - for (var i = 0, len = this.components.length; i < len; i++) { │ │ │ │ │ - length += this.components[i].getLength(); │ │ │ │ │ - } │ │ │ │ │ - return length; │ │ │ │ │ - }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: getArea │ │ │ │ │ - * Calculate the area of this geometry. Note how this function is overridden │ │ │ │ │ - * in . │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Float} The area of the collection by summing its parts │ │ │ │ │ + * APIProperty: strokeOpacity │ │ │ │ │ + * {Number} Stroke opacity (0-1). │ │ │ │ │ + * │ │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ */ │ │ │ │ │ - getArea: function() { │ │ │ │ │ - var area = 0.0; │ │ │ │ │ - for (var i = 0, len = this.components.length; i < len; i++) { │ │ │ │ │ - area += this.components[i].getArea(); │ │ │ │ │ - } │ │ │ │ │ - return area; │ │ │ │ │ - }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIMethod: getGeodesicArea │ │ │ │ │ - * Calculate the approximate area of the polygon were it projected onto │ │ │ │ │ - * the earth. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * projection - {} The spatial reference system │ │ │ │ │ - * for the geometry coordinates. If not provided, Geographic/WGS84 is │ │ │ │ │ - * assumed. │ │ │ │ │ + /** │ │ │ │ │ + * APIProperty: strokeWidth │ │ │ │ │ + * {Number} Pixel stroke width. │ │ │ │ │ * │ │ │ │ │ - * 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. │ │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ */ │ │ │ │ │ - getGeodesicArea: function(projection) { │ │ │ │ │ - var area = 0.0; │ │ │ │ │ - for (var i = 0, len = this.components.length; i < len; i++) { │ │ │ │ │ - area += this.components[i].getGeodesicArea(projection); │ │ │ │ │ - } │ │ │ │ │ - return area; │ │ │ │ │ - }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: getCentroid │ │ │ │ │ - * │ │ │ │ │ - * Compute the centroid for this geometry collection. │ │ │ │ │ + * APIProperty: strokeLinecap │ │ │ │ │ + * {String} Stroke cap type ("butt", "round", or "square"). │ │ │ │ │ + * │ │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ + */ │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Property: strokeDashstyle │ │ │ │ │ + * {String} Stroke dash style according to the SLD spec. Note that the │ │ │ │ │ + * OpenLayers values for strokeDashstyle ("dot", "dash", "dashdot", │ │ │ │ │ + * "longdash", "longdashdot", or "solid") will not work in SLD, but │ │ │ │ │ + * most SLD patterns will render correctly in OpenLayers. │ │ │ │ │ + * │ │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ + */ │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Constructor: OpenLayers.Symbolizer.Line │ │ │ │ │ + * Create a symbolizer for rendering lines. │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * weighted - {Boolean} Perform the getCentroid computation recursively, │ │ │ │ │ - * returning an area weighted average of all geometries in this collection. │ │ │ │ │ + * config - {Object} An object containing properties to be set on the │ │ │ │ │ + * symbolizer. Any documented symbolizer property can be set at │ │ │ │ │ + * construction. │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {} The centroid of the collection │ │ │ │ │ + * A new line symbolizer. │ │ │ │ │ */ │ │ │ │ │ - getCentroid: function(weighted) { │ │ │ │ │ - if (!weighted) { │ │ │ │ │ - return this.components.length && this.components[0].getCentroid(); │ │ │ │ │ - } │ │ │ │ │ - var len = this.components.length; │ │ │ │ │ - if (!len) { │ │ │ │ │ - return false; │ │ │ │ │ - } │ │ │ │ │ + initialize: function(config) { │ │ │ │ │ + OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - var areas = []; │ │ │ │ │ - var centroids = []; │ │ │ │ │ - var areaSum = 0; │ │ │ │ │ - var minArea = Number.MAX_VALUE; │ │ │ │ │ - var component; │ │ │ │ │ - for (var i = 0; i < len; ++i) { │ │ │ │ │ - component = this.components[i]; │ │ │ │ │ - var area = component.getArea(); │ │ │ │ │ - var centroid = component.getCentroid(true); │ │ │ │ │ - if (isNaN(area) || isNaN(centroid.x) || isNaN(centroid.y)) { │ │ │ │ │ - continue; │ │ │ │ │ - } │ │ │ │ │ - areas.push(area); │ │ │ │ │ - areaSum += area; │ │ │ │ │ - minArea = (area < minArea && area > 0) ? area : minArea; │ │ │ │ │ - centroids.push(centroid); │ │ │ │ │ - } │ │ │ │ │ - len = areas.length; │ │ │ │ │ - if (areaSum === 0) { │ │ │ │ │ - // all the components in this collection have 0 area │ │ │ │ │ - // probably a collection of points -- weight all the points the same │ │ │ │ │ - for (var i = 0; i < len; ++i) { │ │ │ │ │ - areas[i] = 1; │ │ │ │ │ - } │ │ │ │ │ - areaSum = areas.length; │ │ │ │ │ - } else { │ │ │ │ │ - // normalize all the areas where the smallest area will get │ │ │ │ │ - // a value of 1 │ │ │ │ │ - for (var i = 0; i < len; ++i) { │ │ │ │ │ - areas[i] /= minArea; │ │ │ │ │ - } │ │ │ │ │ - areaSum /= minArea; │ │ │ │ │ - } │ │ │ │ │ + CLASS_NAME: "OpenLayers.Symbolizer.Line" │ │ │ │ │ │ │ │ │ │ - var xSum = 0, │ │ │ │ │ - ySum = 0, │ │ │ │ │ - centroid, area; │ │ │ │ │ - for (var i = 0; i < len; ++i) { │ │ │ │ │ - centroid = centroids[i]; │ │ │ │ │ - area = areas[i]; │ │ │ │ │ - xSum += centroid.x * area; │ │ │ │ │ - ySum += centroid.y * area; │ │ │ │ │ - } │ │ │ │ │ +}); │ │ │ │ │ │ │ │ │ │ - return new OpenLayers.Geometry.Point(xSum / areaSum, ySum / areaSum); │ │ │ │ │ - }, │ │ │ │ │ +/* ====================================================================== │ │ │ │ │ + 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, { │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: getGeodesicLength │ │ │ │ │ - * Calculate the approximate length of the geometry were it projected onto │ │ │ │ │ - * the earth. │ │ │ │ │ - * │ │ │ │ │ - * projection - {} The spatial reference system │ │ │ │ │ - * for the geometry coordinates. If not provided, Geographic/WGS84 is │ │ │ │ │ - * assumed. │ │ │ │ │ + * APIProperty: strokeColor │ │ │ │ │ + * {String} Color for line stroke. This is a RGB hex value (e.g. "#ff0000" │ │ │ │ │ + * for red). │ │ │ │ │ * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Float} The appoximate geodesic length of the geometry in meters. │ │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ */ │ │ │ │ │ - getGeodesicLength: function(projection) { │ │ │ │ │ - var length = 0.0; │ │ │ │ │ - for (var i = 0, len = this.components.length; i < len; i++) { │ │ │ │ │ - length += this.components[i].getGeodesicLength(projection); │ │ │ │ │ - } │ │ │ │ │ - return length; │ │ │ │ │ - }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: move │ │ │ │ │ - * Moves a geometry by the given displacement along positive x and y axes. │ │ │ │ │ - * This modifies the position of the geometry and clears the cached │ │ │ │ │ - * bounds. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * x - {Float} Distance to move geometry in positive x direction. │ │ │ │ │ - * y - {Float} Distance to move geometry in positive y direction. │ │ │ │ │ + * APIProperty: strokeOpacity │ │ │ │ │ + * {Number} Stroke opacity (0-1). │ │ │ │ │ + * │ │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ */ │ │ │ │ │ - move: function(x, y) { │ │ │ │ │ - for (var i = 0, len = this.components.length; i < len; i++) { │ │ │ │ │ - this.components[i].move(x, y); │ │ │ │ │ - } │ │ │ │ │ - }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: rotate │ │ │ │ │ - * Rotate a geometry around some origin │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * angle - {Float} Rotation angle in degrees (measured counterclockwise │ │ │ │ │ - * from the positive x-axis) │ │ │ │ │ - * origin - {} Center point for the rotation │ │ │ │ │ + * APIProperty: strokeWidth │ │ │ │ │ + * {Number} Pixel stroke width. │ │ │ │ │ + * │ │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ */ │ │ │ │ │ - rotate: function(angle, origin) { │ │ │ │ │ - for (var i = 0, len = this.components.length; i < len; ++i) { │ │ │ │ │ - this.components[i].rotate(angle, origin); │ │ │ │ │ - } │ │ │ │ │ - }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: resize │ │ │ │ │ - * Resize a geometry relative to some origin. Use this method to apply │ │ │ │ │ - * a uniform scaling to a geometry. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * scale - {Float} Factor by which to scale the geometry. A scale of 2 │ │ │ │ │ - * doubles the size of the geometry in each dimension │ │ │ │ │ - * (lines, for example, will be twice as long, and polygons │ │ │ │ │ - * will have four times the area). │ │ │ │ │ - * origin - {} Point of origin for resizing │ │ │ │ │ - * ratio - {Float} Optional x:y ratio for resizing. Default ratio is 1. │ │ │ │ │ + * APIProperty: strokeLinecap │ │ │ │ │ + * {String} Stroke cap type ("butt", "round", or "square"). │ │ │ │ │ * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {} - The current geometry. │ │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ */ │ │ │ │ │ - resize: function(scale, origin, ratio) { │ │ │ │ │ - for (var i = 0; i < this.components.length; ++i) { │ │ │ │ │ - this.components[i].resize(scale, origin, ratio); │ │ │ │ │ - } │ │ │ │ │ - return this; │ │ │ │ │ - }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: distanceTo │ │ │ │ │ - * Calculate the closest distance between two geometries (on the x-y plane). │ │ │ │ │ + * 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: │ │ │ │ │ - * 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. │ │ │ │ │ + * config - {Object} An object containing properties to be set on the │ │ │ │ │ + * symbolizer. Any documented symbolizer property can be set at │ │ │ │ │ + * construction. │ │ │ │ │ * │ │ │ │ │ * 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. │ │ │ │ │ + * A new polygon symbolizer. │ │ │ │ │ */ │ │ │ │ │ - distanceTo: function(geometry, options) { │ │ │ │ │ - var edge = !(options && options.edge === false); │ │ │ │ │ - var details = edge && options && options.details; │ │ │ │ │ - var result, best, distance; │ │ │ │ │ - var min = Number.POSITIVE_INFINITY; │ │ │ │ │ - for (var i = 0, len = this.components.length; i < len; ++i) { │ │ │ │ │ - result = this.components[i].distanceTo(geometry, options); │ │ │ │ │ - distance = details ? result.distance : result; │ │ │ │ │ - if (distance < min) { │ │ │ │ │ - min = distance; │ │ │ │ │ - best = result; │ │ │ │ │ - if (min == 0) { │ │ │ │ │ - break; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - return best; │ │ │ │ │ + initialize: function(config) { │ │ │ │ │ + OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ + CLASS_NAME: "OpenLayers.Symbolizer.Polygon" │ │ │ │ │ + │ │ │ │ │ +}); │ │ │ │ │ + │ │ │ │ │ +/* ====================================================================== │ │ │ │ │ + OpenLayers/Symbolizer/Text.js │ │ │ │ │ + ====================================================================== */ │ │ │ │ │ + │ │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ + * full text of the license. */ │ │ │ │ │ + │ │ │ │ │ +/** │ │ │ │ │ + * @requires OpenLayers/Symbolizer.js │ │ │ │ │ + */ │ │ │ │ │ + │ │ │ │ │ +/** │ │ │ │ │ + * Class: OpenLayers.Symbolizer.Text │ │ │ │ │ + * A symbolizer used to render text labels for features. │ │ │ │ │ + */ │ │ │ │ │ +OpenLayers.Symbolizer.Text = OpenLayers.Class(OpenLayers.Symbolizer, { │ │ │ │ │ + │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: equals │ │ │ │ │ - * Determine whether another geometry is equivalent to this one. Geometries │ │ │ │ │ - * are considered equivalent if all components have the same coordinates. │ │ │ │ │ + * APIProperty: label │ │ │ │ │ + * {String} The text for the label. │ │ │ │ │ * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * geometry - {} The geometry to test. │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Boolean} The supplied geometry is equivalent to this geometry. │ │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ */ │ │ │ │ │ - equals: function(geometry) { │ │ │ │ │ - var equivalent = true; │ │ │ │ │ - if (!geometry || !geometry.CLASS_NAME || │ │ │ │ │ - (this.CLASS_NAME != geometry.CLASS_NAME)) { │ │ │ │ │ - equivalent = false; │ │ │ │ │ - } else if (!(OpenLayers.Util.isArray(geometry.components)) || │ │ │ │ │ - (geometry.components.length != this.components.length)) { │ │ │ │ │ - equivalent = false; │ │ │ │ │ - } else { │ │ │ │ │ - for (var i = 0, len = this.components.length; i < len; ++i) { │ │ │ │ │ - if (!this.components[i].equals(geometry.components[i])) { │ │ │ │ │ - equivalent = false; │ │ │ │ │ - break; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - return equivalent; │ │ │ │ │ - }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIMethod: transform │ │ │ │ │ - * Reproject the components geometry from source to dest. │ │ │ │ │ + /** │ │ │ │ │ + * APIProperty: fontFamily │ │ │ │ │ + * {String} The font family for the label. │ │ │ │ │ * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * source - {} │ │ │ │ │ - * dest - {} │ │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ + */ │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * APIProperty: fontSize │ │ │ │ │ + * {String} The font size for the label. │ │ │ │ │ * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {} │ │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ + */ │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * APIProperty: fontWeight │ │ │ │ │ + * {String} The font weight for the label. │ │ │ │ │ + * │ │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ */ │ │ │ │ │ - transform: function(source, dest) { │ │ │ │ │ - if (source && dest) { │ │ │ │ │ - for (var i = 0, len = this.components.length; i < len; i++) { │ │ │ │ │ - var component = this.components[i]; │ │ │ │ │ - component.transform(source, dest); │ │ │ │ │ - } │ │ │ │ │ - this.bounds = null; │ │ │ │ │ - } │ │ │ │ │ - return this; │ │ │ │ │ - }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: intersects │ │ │ │ │ - * Determine if the input geometry intersects this one. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * geometry - {} Any type of geometry. │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Boolean} The input geometry intersects this one. │ │ │ │ │ + * Property: fontStyle │ │ │ │ │ + * {String} The font style for the label. │ │ │ │ │ + * │ │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ */ │ │ │ │ │ - intersects: function(geometry) { │ │ │ │ │ - var intersect = false; │ │ │ │ │ - for (var i = 0, len = this.components.length; i < len; ++i) { │ │ │ │ │ - intersect = geometry.intersects(this.components[i]); │ │ │ │ │ - if (intersect) { │ │ │ │ │ - break; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - return intersect; │ │ │ │ │ - }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: getVertices │ │ │ │ │ - * Return a list of all points in this geometry. │ │ │ │ │ + * Constructor: OpenLayers.Symbolizer.Text │ │ │ │ │ + * Create a symbolizer for rendering text labels. │ │ │ │ │ * │ │ │ │ │ * 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. │ │ │ │ │ + * config - {Object} An object containing properties to be set on the │ │ │ │ │ + * symbolizer. Any documented symbolizer property can be set at │ │ │ │ │ + * construction. │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {Array} A list of all vertices in the geometry. │ │ │ │ │ + * A new text symbolizer. │ │ │ │ │ */ │ │ │ │ │ - getVertices: function(nodes) { │ │ │ │ │ - var vertices = []; │ │ │ │ │ - for (var i = 0, len = this.components.length; i < len; ++i) { │ │ │ │ │ - Array.prototype.push.apply( │ │ │ │ │ - vertices, this.components[i].getVertices(nodes) │ │ │ │ │ - ); │ │ │ │ │ - } │ │ │ │ │ - return vertices; │ │ │ │ │ + initialize: function(config) { │ │ │ │ │ + OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ + CLASS_NAME: "OpenLayers.Symbolizer.Text" │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Geometry.Collection" │ │ │ │ │ }); │ │ │ │ │ + │ │ │ │ │ /* ====================================================================== │ │ │ │ │ - OpenLayers/Geometry/MultiPoint.js │ │ │ │ │ + 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/Geometry/Collection.js │ │ │ │ │ - * @requires OpenLayers/Geometry/Point.js │ │ │ │ │ + * @requires OpenLayers/Symbolizer.js │ │ │ │ │ */ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Class: OpenLayers.Geometry.MultiPoint │ │ │ │ │ - * MultiPoint is a collection of Points. Create a new instance with the │ │ │ │ │ - * constructor. │ │ │ │ │ - * │ │ │ │ │ - * Inherits from: │ │ │ │ │ - * - │ │ │ │ │ - * - │ │ │ │ │ + * Class: OpenLayers.Symbolizer.Raster │ │ │ │ │ + * A symbolizer used to render raster images. │ │ │ │ │ */ │ │ │ │ │ -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: │ │ │ │ │ - * {} │ │ │ │ │ - */ │ │ │ │ │ +OpenLayers.Symbolizer.Raster = OpenLayers.Class(OpenLayers.Symbolizer, { │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIMethod: addPoint │ │ │ │ │ - * Wrapper for │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * point - {} Point to be added │ │ │ │ │ - * index - {Integer} Optional index │ │ │ │ │ - */ │ │ │ │ │ - addPoint: function(point, index) { │ │ │ │ │ - this.addComponent(point, index); │ │ │ │ │ - }, │ │ │ │ │ + /** │ │ │ │ │ + * 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); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIMethod: removePoint │ │ │ │ │ - * Wrapper for │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * point - {} Point to be removed │ │ │ │ │ - */ │ │ │ │ │ - removePoint: function(point) { │ │ │ │ │ - this.removeComponent(point); │ │ │ │ │ - }, │ │ │ │ │ + CLASS_NAME: "OpenLayers.Symbolizer.Raster" │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Geometry.MultiPoint" │ │ │ │ │ - }); │ │ │ │ │ +}); │ │ │ │ │ /* ====================================================================== │ │ │ │ │ - OpenLayers/Geometry/Curve.js │ │ │ │ │ + OpenLayers/Style2.js │ │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * @requires OpenLayers/Geometry/MultiPoint.js │ │ │ │ │ + * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ │ + * @requires OpenLayers/Rule.js │ │ │ │ │ + * @requires OpenLayers/Symbolizer/Point.js │ │ │ │ │ + * @requires OpenLayers/Symbolizer/Line.js │ │ │ │ │ + * @requires OpenLayers/Symbolizer/Polygon.js │ │ │ │ │ + * @requires OpenLayers/Symbolizer/Text.js │ │ │ │ │ + * @requires OpenLayers/Symbolizer/Raster.js │ │ │ │ │ */ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Class: OpenLayers.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: │ │ │ │ │ - * - │ │ │ │ │ + * Class: OpenLayers.Style2 │ │ │ │ │ + * This class represents a collection of rules for rendering features. │ │ │ │ │ */ │ │ │ │ │ -OpenLayers.Geometry.Curve = OpenLayers.Class(OpenLayers.Geometry.MultiPoint, { │ │ │ │ │ +OpenLayers.Style2 = OpenLayers.Class({ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * Property: id │ │ │ │ │ + * {String} A unique id for this session. │ │ │ │ │ */ │ │ │ │ │ - componentTypes: ["OpenLayers.Geometry.Point"], │ │ │ │ │ + id: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Constructor: OpenLayers.Geometry.Curve │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * point - {Array()} │ │ │ │ │ + * APIProperty: name │ │ │ │ │ + * {String} Style identifier. │ │ │ │ │ */ │ │ │ │ │ + name: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: getLength │ │ │ │ │ - * │ │ │ │ │ + * APIProperty: title │ │ │ │ │ + * {String} Title of this style. │ │ │ │ │ + */ │ │ │ │ │ + title: null, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * APIProperty: description │ │ │ │ │ + * {String} Description of this style. │ │ │ │ │ + */ │ │ │ │ │ + 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, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * APIProperty: rules │ │ │ │ │ + * {Array()} Collection of rendering rules. │ │ │ │ │ + */ │ │ │ │ │ + rules: null, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Constructor: OpenLayers.Style2 │ │ │ │ │ + * Creates a style representing a collection of rendering rules. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * config - {Object} An object containing properties to be set on the │ │ │ │ │ + * style. Any documented properties may be set at construction. │ │ │ │ │ + * │ │ │ │ │ * Returns: │ │ │ │ │ - * {Float} The length of the curve │ │ │ │ │ + * {} A new style object. │ │ │ │ │ */ │ │ │ │ │ - getLength: function() { │ │ │ │ │ - var length = 0.0; │ │ │ │ │ - if (this.components && (this.components.length > 1)) { │ │ │ │ │ - for (var i = 1, len = this.components.length; i < len; i++) { │ │ │ │ │ - length += this.components[i - 1].distanceTo(this.components[i]); │ │ │ │ │ - } │ │ │ │ │ + initialize: function(config) { │ │ │ │ │ + OpenLayers.Util.extend(this, config); │ │ │ │ │ + this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_"); │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: destroy │ │ │ │ │ + * nullify references to prevent circular references and memory leaks │ │ │ │ │ + */ │ │ │ │ │ + destroy: function() { │ │ │ │ │ + for (var i = 0, len = this.rules.length; i < len; i++) { │ │ │ │ │ + this.rules[i].destroy(); │ │ │ │ │ } │ │ │ │ │ - return length; │ │ │ │ │ + delete this.rules; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: getGeodesicLength │ │ │ │ │ - * Calculate the approximate length of the geometry were it projected onto │ │ │ │ │ - * the earth. │ │ │ │ │ - * │ │ │ │ │ - * projection - {} The spatial reference system │ │ │ │ │ - * for the geometry coordinates. If not provided, Geographic/WGS84 is │ │ │ │ │ - * assumed. │ │ │ │ │ + * APIMethod: clone │ │ │ │ │ + * Clones this style. │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {Float} The appoximate geodesic length of the geometry in meters. │ │ │ │ │ + * {} Clone of this style. │ │ │ │ │ */ │ │ │ │ │ - getGeodesicLength: function(projection) { │ │ │ │ │ - var geom = this; // so we can work with a clone if needed │ │ │ │ │ - if (projection) { │ │ │ │ │ - var gg = new OpenLayers.Projection("EPSG:4326"); │ │ │ │ │ - if (!gg.equals(projection)) { │ │ │ │ │ - geom = this.clone().transform(projection, gg); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - var length = 0.0; │ │ │ │ │ - if (geom.components && (geom.components.length > 1)) { │ │ │ │ │ - var p1, p2; │ │ │ │ │ - for (var i = 1, len = geom.components.length; i < len; i++) { │ │ │ │ │ - p1 = geom.components[i - 1]; │ │ │ │ │ - p2 = geom.components[i]; │ │ │ │ │ - // this returns km and requires lon/lat properties │ │ │ │ │ - length += OpenLayers.Util.distVincenty({ │ │ │ │ │ - lon: p1.x, │ │ │ │ │ - lat: p1.y │ │ │ │ │ - }, { │ │ │ │ │ - lon: p2.x, │ │ │ │ │ - lat: p2.y │ │ │ │ │ - }); │ │ │ │ │ + clone: function() { │ │ │ │ │ + var config = OpenLayers.Util.extend({}, this); │ │ │ │ │ + // clone rules │ │ │ │ │ + if (this.rules) { │ │ │ │ │ + config.rules = []; │ │ │ │ │ + for (var i = 0, len = this.rules.length; i < len; ++i) { │ │ │ │ │ + config.rules.push(this.rules[i].clone()); │ │ │ │ │ } │ │ │ │ │ } │ │ │ │ │ - // convert to m │ │ │ │ │ - return length * 1000; │ │ │ │ │ + return new OpenLayers.Style2(config); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Geometry.Curve" │ │ │ │ │ + CLASS_NAME: "OpenLayers.Style2" │ │ │ │ │ }); │ │ │ │ │ /* ====================================================================== │ │ │ │ │ - OpenLayers/Geometry/LineString.js │ │ │ │ │ + OpenLayers/Kinetic.js │ │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * @requires OpenLayers/Geometry/Curve.js │ │ │ │ │ + * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ │ + * @requires OpenLayers/Animation.js │ │ │ │ │ */ │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * Class: OpenLayers.Geometry.LineString │ │ │ │ │ - * A LineString is a Curve which, once two points have been added to it, can │ │ │ │ │ - * never be less than two points long. │ │ │ │ │ - * │ │ │ │ │ - * Inherits from: │ │ │ │ │ - * - │ │ │ │ │ - */ │ │ │ │ │ -OpenLayers.Geometry.LineString = OpenLayers.Class(OpenLayers.Geometry.Curve, { │ │ │ │ │ +OpenLayers.Kinetic = OpenLayers.Class({ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Constructor: OpenLayers.Geometry.LineString │ │ │ │ │ - * Create a new LineString geometry │ │ │ │ │ + * Property: threshold │ │ │ │ │ + * In most cases changing the threshold isn't needed. │ │ │ │ │ + * In px/ms, default to 0. │ │ │ │ │ + */ │ │ │ │ │ + threshold: 0, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Property: deceleration │ │ │ │ │ + * {Float} the deseleration in px/ms², default to 0.0035. │ │ │ │ │ + */ │ │ │ │ │ + deceleration: 0.0035, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Property: nbPoints │ │ │ │ │ + * {Integer} the number of points we use to calculate the kinetic │ │ │ │ │ + * initial values. │ │ │ │ │ + */ │ │ │ │ │ + nbPoints: 100, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Property: delay │ │ │ │ │ + * {Float} time to consider to calculate the kinetic initial values. │ │ │ │ │ + * In ms, default to 200. │ │ │ │ │ + */ │ │ │ │ │ + delay: 200, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Property: points │ │ │ │ │ + * List of points use to calculate the kinetic initial values. │ │ │ │ │ + */ │ │ │ │ │ + points: undefined, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Property: timerId │ │ │ │ │ + * ID of the timer. │ │ │ │ │ + */ │ │ │ │ │ + timerId: undefined, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Constructor: OpenLayers.Kinetic │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * points - {Array()} An array of points used to │ │ │ │ │ - * generate the linestring │ │ │ │ │ - * │ │ │ │ │ + * options - {Object} │ │ │ │ │ */ │ │ │ │ │ + initialize: function(options) { │ │ │ │ │ + OpenLayers.Util.extend(this, options); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * Method: begin │ │ │ │ │ + * Begins the dragging. │ │ │ │ │ */ │ │ │ │ │ - removeComponent: function(point) { │ │ │ │ │ - var removed = this.components && (this.components.length > 2); │ │ │ │ │ - if (removed) { │ │ │ │ │ - OpenLayers.Geometry.Collection.prototype.removeComponent.apply(this, │ │ │ │ │ - arguments); │ │ │ │ │ - } │ │ │ │ │ - return removed; │ │ │ │ │ + begin: function() { │ │ │ │ │ + OpenLayers.Animation.stop(this.timerId); │ │ │ │ │ + this.timerId = undefined; │ │ │ │ │ + this.points = []; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * Method: update │ │ │ │ │ + * Updates during the dragging. │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * geometry - {} │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Boolean} The input geometry intersects this geometry. │ │ │ │ │ + * xy - {} The new position. │ │ │ │ │ */ │ │ │ │ │ - intersects: function(geometry) { │ │ │ │ │ - var intersect = false; │ │ │ │ │ - var type = geometry.CLASS_NAME; │ │ │ │ │ - if (type == "OpenLayers.Geometry.LineString" || │ │ │ │ │ - type == "OpenLayers.Geometry.LinearRing" || │ │ │ │ │ - type == "OpenLayers.Geometry.Point") { │ │ │ │ │ - var segs1 = this.getSortedSegments(); │ │ │ │ │ - var segs2; │ │ │ │ │ - if (type == "OpenLayers.Geometry.Point") { │ │ │ │ │ - segs2 = [{ │ │ │ │ │ - x1: geometry.x, │ │ │ │ │ - y1: geometry.y, │ │ │ │ │ - x2: geometry.x, │ │ │ │ │ - y2: geometry.y │ │ │ │ │ - }]; │ │ │ │ │ - } else { │ │ │ │ │ - segs2 = geometry.getSortedSegments(); │ │ │ │ │ - } │ │ │ │ │ - var seg1, seg1x1, seg1x2, seg1y1, seg1y2, │ │ │ │ │ - seg2, seg2y1, seg2y2; │ │ │ │ │ - // sweep right │ │ │ │ │ - outer: for (var i = 0, len = segs1.length; i < len; ++i) { │ │ │ │ │ - seg1 = segs1[i]; │ │ │ │ │ - seg1x1 = seg1.x1; │ │ │ │ │ - seg1x2 = seg1.x2; │ │ │ │ │ - seg1y1 = seg1.y1; │ │ │ │ │ - seg1y2 = seg1.y2; │ │ │ │ │ - inner: for (var j = 0, jlen = segs2.length; j < jlen; ++j) { │ │ │ │ │ - seg2 = segs2[j]; │ │ │ │ │ - if (seg2.x1 > seg1x2) { │ │ │ │ │ - // seg1 still left of seg2 │ │ │ │ │ - break; │ │ │ │ │ - } │ │ │ │ │ - if (seg2.x2 < seg1x1) { │ │ │ │ │ - // seg2 still left of seg1 │ │ │ │ │ - continue; │ │ │ │ │ - } │ │ │ │ │ - seg2y1 = seg2.y1; │ │ │ │ │ - seg2y2 = seg2.y2; │ │ │ │ │ - if (Math.min(seg2y1, seg2y2) > Math.max(seg1y1, seg1y2)) { │ │ │ │ │ - // seg2 above seg1 │ │ │ │ │ - continue; │ │ │ │ │ - } │ │ │ │ │ - if (Math.max(seg2y1, seg2y2) < Math.min(seg1y1, seg1y2)) { │ │ │ │ │ - // seg2 below seg1 │ │ │ │ │ - continue; │ │ │ │ │ - } │ │ │ │ │ - if (OpenLayers.Geometry.segmentsIntersect(seg1, seg2)) { │ │ │ │ │ - intersect = true; │ │ │ │ │ - break outer; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - } else { │ │ │ │ │ - intersect = geometry.intersects(this); │ │ │ │ │ + update: function(xy) { │ │ │ │ │ + this.points.unshift({ │ │ │ │ │ + xy: xy, │ │ │ │ │ + tick: new Date().getTime() │ │ │ │ │ + }); │ │ │ │ │ + if (this.points.length > this.nbPoints) { │ │ │ │ │ + this.points.pop(); │ │ │ │ │ } │ │ │ │ │ - return intersect; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: getSortedSegments │ │ │ │ │ + * Method: end │ │ │ │ │ + * Ends the dragging, start the kinetic. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * xy - {} The last position. │ │ │ │ │ * │ │ │ │ │ * 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. │ │ │ │ │ + * {Object} An object with two properties: "speed", and "theta". The │ │ │ │ │ + * "speed" and "theta" values are to be passed to the move │ │ │ │ │ + * function when starting the animation. │ │ │ │ │ */ │ │ │ │ │ - getSortedSegments: function() { │ │ │ │ │ - var numSeg = this.components.length - 1; │ │ │ │ │ - var segments = new Array(numSeg), │ │ │ │ │ - point1, point2; │ │ │ │ │ - for (var i = 0; i < numSeg; ++i) { │ │ │ │ │ - point1 = this.components[i]; │ │ │ │ │ - point2 = this.components[i + 1]; │ │ │ │ │ - if (point1.x < point2.x) { │ │ │ │ │ - segments[i] = { │ │ │ │ │ - x1: point1.x, │ │ │ │ │ - y1: point1.y, │ │ │ │ │ - x2: point2.x, │ │ │ │ │ - y2: point2.y │ │ │ │ │ - }; │ │ │ │ │ - } else { │ │ │ │ │ - segments[i] = { │ │ │ │ │ - x1: point2.x, │ │ │ │ │ - y1: point2.y, │ │ │ │ │ - x2: point1.x, │ │ │ │ │ - y2: point1.y │ │ │ │ │ - }; │ │ │ │ │ + end: function(xy) { │ │ │ │ │ + var last, now = new Date().getTime(); │ │ │ │ │ + for (var i = 0, l = this.points.length, point; i < l; i++) { │ │ │ │ │ + point = this.points[i]; │ │ │ │ │ + if (now - point.tick > this.delay) { │ │ │ │ │ + break; │ │ │ │ │ } │ │ │ │ │ + last = point; │ │ │ │ │ } │ │ │ │ │ - // more efficient to define this somewhere static │ │ │ │ │ - function byX1(seg1, seg2) { │ │ │ │ │ - return seg1.x1 - seg2.x1; │ │ │ │ │ + if (!last) { │ │ │ │ │ + return; │ │ │ │ │ } │ │ │ │ │ - return segments.sort(byX1); │ │ │ │ │ + var time = new Date().getTime() - last.tick; │ │ │ │ │ + var dist = Math.sqrt(Math.pow(xy.x - last.xy.x, 2) + │ │ │ │ │ + Math.pow(xy.y - last.xy.y, 2)); │ │ │ │ │ + var speed = dist / time; │ │ │ │ │ + if (speed == 0 || speed < this.threshold) { │ │ │ │ │ + return; │ │ │ │ │ + } │ │ │ │ │ + var theta = Math.asin((xy.y - last.xy.y) / dist); │ │ │ │ │ + if (last.xy.x <= xy.x) { │ │ │ │ │ + theta = Math.PI - theta; │ │ │ │ │ + } │ │ │ │ │ + return { │ │ │ │ │ + speed: speed, │ │ │ │ │ + theta: theta │ │ │ │ │ + }; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: splitWithSegment │ │ │ │ │ - * Split this geometry with the given segment. │ │ │ │ │ + * Method: move │ │ │ │ │ + * Launch the kinetic move pan. │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * seg - {Object} An object with x1, y1, x2, and y2 properties referencing │ │ │ │ │ - * segment endpoint coordinates. │ │ │ │ │ - * options - {Object} Properties of this object will be used to determine │ │ │ │ │ - * how the split is conducted. │ │ │ │ │ - * │ │ │ │ │ - * Valid options: │ │ │ │ │ - * edge - {Boolean} Allow splitting when only edges intersect. Default is │ │ │ │ │ - * true. If false, a vertex on the source segment must be within the │ │ │ │ │ - * tolerance distance of the intersection to be considered a split. │ │ │ │ │ - * tolerance - {Number} If a non-null value is provided, intersections │ │ │ │ │ - * within the tolerance distance of one of the source segment's │ │ │ │ │ - * endpoints will be assumed to occur at the endpoint. │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Object} An object with *lines* and *points* properties. If the given │ │ │ │ │ - * segment intersects this linestring, the lines array will reference │ │ │ │ │ - * geometries that result from the split. The points array will contain │ │ │ │ │ - * all intersection points. Intersection points are sorted along the │ │ │ │ │ - * segment (in order from x1,y1 to x2,y2). │ │ │ │ │ + * info - {Object} An object with two properties, "speed", and "theta". │ │ │ │ │ + * These values are those returned from the "end" call. │ │ │ │ │ + * callback - {Function} Function called on every step of the animation, │ │ │ │ │ + * receives x, y (values to pan), end (is the last point). │ │ │ │ │ */ │ │ │ │ │ - splitWithSegment: function(seg, options) { │ │ │ │ │ - var edge = !(options && options.edge === false); │ │ │ │ │ - var tolerance = options && options.tolerance; │ │ │ │ │ - var lines = []; │ │ │ │ │ - var verts = this.getVertices(); │ │ │ │ │ - var points = []; │ │ │ │ │ - var intersections = []; │ │ │ │ │ - var split = false; │ │ │ │ │ - var vert1, vert2, point; │ │ │ │ │ - var node, vertex, target; │ │ │ │ │ - var interOptions = { │ │ │ │ │ - point: true, │ │ │ │ │ - tolerance: tolerance │ │ │ │ │ - }; │ │ │ │ │ - var result = null; │ │ │ │ │ - for (var i = 0, stop = verts.length - 2; i <= stop; ++i) { │ │ │ │ │ - vert1 = verts[i]; │ │ │ │ │ - points.push(vert1.clone()); │ │ │ │ │ - vert2 = verts[i + 1]; │ │ │ │ │ - target = { │ │ │ │ │ - x1: vert1.x, │ │ │ │ │ - y1: vert1.y, │ │ │ │ │ - x2: vert2.x, │ │ │ │ │ - y2: vert2.y │ │ │ │ │ - }; │ │ │ │ │ - point = OpenLayers.Geometry.segmentsIntersect( │ │ │ │ │ - seg, target, interOptions │ │ │ │ │ - ); │ │ │ │ │ - if (point instanceof OpenLayers.Geometry.Point) { │ │ │ │ │ - if ((point.x === seg.x1 && point.y === seg.y1) || │ │ │ │ │ - (point.x === seg.x2 && point.y === seg.y2) || │ │ │ │ │ - point.equals(vert1) || point.equals(vert2)) { │ │ │ │ │ - vertex = true; │ │ │ │ │ - } else { │ │ │ │ │ - vertex = false; │ │ │ │ │ - } │ │ │ │ │ - if (vertex || edge) { │ │ │ │ │ - // push intersections different than the previous │ │ │ │ │ - if (!point.equals(intersections[intersections.length - 1])) { │ │ │ │ │ - intersections.push(point.clone()); │ │ │ │ │ - } │ │ │ │ │ - if (i === 0) { │ │ │ │ │ - if (point.equals(vert1)) { │ │ │ │ │ - continue; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - if (point.equals(vert2)) { │ │ │ │ │ - continue; │ │ │ │ │ - } │ │ │ │ │ - split = true; │ │ │ │ │ - if (!point.equals(vert1)) { │ │ │ │ │ - points.push(point); │ │ │ │ │ - } │ │ │ │ │ - lines.push(new OpenLayers.Geometry.LineString(points)); │ │ │ │ │ - points = [point.clone()]; │ │ │ │ │ - } │ │ │ │ │ + move: function(info, callback) { │ │ │ │ │ + var v0 = info.speed; │ │ │ │ │ + var fx = Math.cos(info.theta); │ │ │ │ │ + var fy = -Math.sin(info.theta); │ │ │ │ │ + │ │ │ │ │ + var initialTime = new Date().getTime(); │ │ │ │ │ + │ │ │ │ │ + var lastX = 0; │ │ │ │ │ + var lastY = 0; │ │ │ │ │ + │ │ │ │ │ + var timerCallback = function() { │ │ │ │ │ + if (this.timerId == null) { │ │ │ │ │ + return; │ │ │ │ │ } │ │ │ │ │ - } │ │ │ │ │ - if (split) { │ │ │ │ │ - points.push(vert2.clone()); │ │ │ │ │ - lines.push(new OpenLayers.Geometry.LineString(points)); │ │ │ │ │ - } │ │ │ │ │ - if (intersections.length > 0) { │ │ │ │ │ - // sort intersections along segment │ │ │ │ │ - var xDir = seg.x1 < seg.x2 ? 1 : -1; │ │ │ │ │ - var yDir = seg.y1 < seg.y2 ? 1 : -1; │ │ │ │ │ - result = { │ │ │ │ │ - lines: lines, │ │ │ │ │ - points: intersections.sort(function(p1, p2) { │ │ │ │ │ - return (xDir * p1.x - xDir * p2.x) || (yDir * p1.y - yDir * p2.y); │ │ │ │ │ - }) │ │ │ │ │ - }; │ │ │ │ │ - } │ │ │ │ │ - return result; │ │ │ │ │ + │ │ │ │ │ + var t = new Date().getTime() - initialTime; │ │ │ │ │ + │ │ │ │ │ + var p = (-this.deceleration * Math.pow(t, 2)) / 2.0 + v0 * t; │ │ │ │ │ + var x = p * fx; │ │ │ │ │ + var y = p * fy; │ │ │ │ │ + │ │ │ │ │ + var args = {}; │ │ │ │ │ + args.end = false; │ │ │ │ │ + var v = -this.deceleration * t + v0; │ │ │ │ │ + │ │ │ │ │ + if (v <= 0) { │ │ │ │ │ + OpenLayers.Animation.stop(this.timerId); │ │ │ │ │ + this.timerId = null; │ │ │ │ │ + args.end = true; │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + args.x = x - lastX; │ │ │ │ │ + args.y = y - lastY; │ │ │ │ │ + lastX = x; │ │ │ │ │ + lastY = y; │ │ │ │ │ + callback(args.x, args.y, args.end); │ │ │ │ │ + }; │ │ │ │ │ + │ │ │ │ │ + this.timerId = OpenLayers.Animation.start( │ │ │ │ │ + OpenLayers.Function.bind(timerCallback, this) │ │ │ │ │ + ); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ + CLASS_NAME: "OpenLayers.Kinetic" │ │ │ │ │ +}); │ │ │ │ │ +/* ====================================================================== │ │ │ │ │ + OpenLayers/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, │ │ │ │ │ + │ │ │ │ │ /** │ │ │ │ │ - * Method: split │ │ │ │ │ - * Use this geometry (the source) to attempt to split a target geometry. │ │ │ │ │ + * 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: │ │ │ │ │ - * 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. │ │ │ │ │ + * event - {Event} │ │ │ │ │ * │ │ │ │ │ * 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. │ │ │ │ │ + * {DOMElement} The element that caused the event │ │ │ │ │ */ │ │ │ │ │ - split: function(target, options) { │ │ │ │ │ - var results = null; │ │ │ │ │ - var mutual = options && options.mutual; │ │ │ │ │ - var sourceSplit, targetSplit, sourceParts, targetParts; │ │ │ │ │ - if (target instanceof OpenLayers.Geometry.LineString) { │ │ │ │ │ - var verts = this.getVertices(); │ │ │ │ │ - var vert1, vert2, seg, splits, lines, point; │ │ │ │ │ - var points = []; │ │ │ │ │ - sourceParts = []; │ │ │ │ │ - for (var i = 0, stop = verts.length - 2; i <= stop; ++i) { │ │ │ │ │ - vert1 = verts[i]; │ │ │ │ │ - vert2 = verts[i + 1]; │ │ │ │ │ - seg = { │ │ │ │ │ - x1: vert1.x, │ │ │ │ │ - y1: vert1.y, │ │ │ │ │ - x2: vert2.x, │ │ │ │ │ - y2: vert2.y │ │ │ │ │ - }; │ │ │ │ │ - targetParts = targetParts || [target]; │ │ │ │ │ - if (mutual) { │ │ │ │ │ - points.push(vert1.clone()); │ │ │ │ │ - } │ │ │ │ │ - for (var j = 0; j < targetParts.length; ++j) { │ │ │ │ │ - splits = targetParts[j].splitWithSegment(seg, options); │ │ │ │ │ - if (splits) { │ │ │ │ │ - // splice in new features │ │ │ │ │ - lines = splits.lines; │ │ │ │ │ - if (lines.length > 0) { │ │ │ │ │ - lines.unshift(j, 1); │ │ │ │ │ - Array.prototype.splice.apply(targetParts, lines); │ │ │ │ │ - j += lines.length - 2; │ │ │ │ │ - } │ │ │ │ │ - if (mutual) { │ │ │ │ │ - for (var k = 0, len = splits.points.length; k < len; ++k) { │ │ │ │ │ - point = splits.points[k]; │ │ │ │ │ - if (!point.equals(vert1)) { │ │ │ │ │ - points.push(point); │ │ │ │ │ - sourceParts.push(new OpenLayers.Geometry.LineString(points)); │ │ │ │ │ - if (point.equals(vert2)) { │ │ │ │ │ - points = []; │ │ │ │ │ - } else { │ │ │ │ │ - points = [point.clone()]; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - if (mutual && sourceParts.length > 0 && points.length > 0) { │ │ │ │ │ - points.push(vert2.clone()); │ │ │ │ │ - sourceParts.push(new OpenLayers.Geometry.LineString(points)); │ │ │ │ │ - } │ │ │ │ │ - } else { │ │ │ │ │ - results = target.splitWith(this, options); │ │ │ │ │ - } │ │ │ │ │ - if (targetParts && targetParts.length > 1) { │ │ │ │ │ - targetSplit = true; │ │ │ │ │ - } else { │ │ │ │ │ - targetParts = []; │ │ │ │ │ - } │ │ │ │ │ - if (sourceParts && sourceParts.length > 1) { │ │ │ │ │ - sourceSplit = true; │ │ │ │ │ - } else { │ │ │ │ │ - sourceParts = []; │ │ │ │ │ - } │ │ │ │ │ - if (targetSplit || sourceSplit) { │ │ │ │ │ - if (mutual) { │ │ │ │ │ - results = [sourceParts, targetParts]; │ │ │ │ │ - } else { │ │ │ │ │ - results = targetParts; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - return results; │ │ │ │ │ + element: function(event) { │ │ │ │ │ + return event.target || event.srcElement; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: splitWith │ │ │ │ │ - * Split this geometry (the target) with the given geometry (the source). │ │ │ │ │ + * Method: isSingleTouch │ │ │ │ │ + * Determine whether event was caused by a single touch │ │ │ │ │ * │ │ │ │ │ * 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. │ │ │ │ │ + * event - {Event} │ │ │ │ │ * │ │ │ │ │ - * 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. │ │ │ │ │ + * {Boolean} │ │ │ │ │ */ │ │ │ │ │ - splitWith: function(geometry, options) { │ │ │ │ │ - return geometry.split(this, options); │ │ │ │ │ - │ │ │ │ │ + isSingleTouch: function(event) { │ │ │ │ │ + return event.touches && event.touches.length == 1; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: getVertices │ │ │ │ │ - * Return a list of all points in this geometry. │ │ │ │ │ + * Method: isMultiTouch │ │ │ │ │ + * Determine whether event was caused by a multi touch │ │ │ │ │ * │ │ │ │ │ * 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. │ │ │ │ │ + * event - {Event} │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {Array} A list of all vertices in the geometry. │ │ │ │ │ + * {Boolean} │ │ │ │ │ */ │ │ │ │ │ - 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; │ │ │ │ │ + isMultiTouch: function(event) { │ │ │ │ │ + return event.touches && event.touches.length > 1; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: distanceTo │ │ │ │ │ - * Calculate the closest distance between two geometries (on the x-y plane). │ │ │ │ │ + * Method: isLeftClick │ │ │ │ │ + * Determine whether event was caused by a left click. │ │ │ │ │ * │ │ │ │ │ * 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. │ │ │ │ │ + * 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: │ │ │ │ │ - * {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. │ │ │ │ │ + * {Boolean} │ │ │ │ │ */ │ │ │ │ │ - distanceTo: function(geometry, options) { │ │ │ │ │ - var edge = !(options && options.edge === false); │ │ │ │ │ - var details = edge && options && options.details; │ │ │ │ │ - var result, best = {}; │ │ │ │ │ - var min = Number.POSITIVE_INFINITY; │ │ │ │ │ - if (geometry instanceof OpenLayers.Geometry.Point) { │ │ │ │ │ - var segs = this.getSortedSegments(); │ │ │ │ │ - var x = geometry.x; │ │ │ │ │ - var y = geometry.y; │ │ │ │ │ - var seg; │ │ │ │ │ - for (var i = 0, len = segs.length; i < len; ++i) { │ │ │ │ │ - seg = segs[i]; │ │ │ │ │ - result = OpenLayers.Geometry.distanceToSegment(geometry, seg); │ │ │ │ │ - if (result.distance < min) { │ │ │ │ │ - min = result.distance; │ │ │ │ │ - best = result; │ │ │ │ │ - if (min === 0) { │ │ │ │ │ - break; │ │ │ │ │ - } │ │ │ │ │ - } else { │ │ │ │ │ - // if distance increases and we cross y0 to the right of x0, no need to keep looking. │ │ │ │ │ - if (seg.x2 > x && ((y > seg.y1 && y < seg.y2) || (y < seg.y1 && y > seg.y2))) { │ │ │ │ │ - break; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - if (details) { │ │ │ │ │ - best = { │ │ │ │ │ - distance: best.distance, │ │ │ │ │ - x0: best.x, │ │ │ │ │ - y0: best.y, │ │ │ │ │ - x1: x, │ │ │ │ │ - y1: y │ │ │ │ │ - }; │ │ │ │ │ - } else { │ │ │ │ │ - best = best.distance; │ │ │ │ │ - } │ │ │ │ │ - } else if (geometry instanceof OpenLayers.Geometry.LineString) { │ │ │ │ │ - var segs0 = this.getSortedSegments(); │ │ │ │ │ - var segs1 = geometry.getSortedSegments(); │ │ │ │ │ - var seg0, seg1, intersection, x0, y0; │ │ │ │ │ - var len1 = segs1.length; │ │ │ │ │ - var interOptions = { │ │ │ │ │ - point: true │ │ │ │ │ - }; │ │ │ │ │ - outer: for (var i = 0, len = segs0.length; i < len; ++i) { │ │ │ │ │ - seg0 = segs0[i]; │ │ │ │ │ - x0 = seg0.x1; │ │ │ │ │ - y0 = seg0.y1; │ │ │ │ │ - for (var j = 0; j < len1; ++j) { │ │ │ │ │ - seg1 = segs1[j]; │ │ │ │ │ - intersection = OpenLayers.Geometry.segmentsIntersect(seg0, seg1, interOptions); │ │ │ │ │ - if (intersection) { │ │ │ │ │ - min = 0; │ │ │ │ │ - best = { │ │ │ │ │ - distance: 0, │ │ │ │ │ - x0: intersection.x, │ │ │ │ │ - y0: intersection.y, │ │ │ │ │ - x1: intersection.x, │ │ │ │ │ - y1: intersection.y │ │ │ │ │ - }; │ │ │ │ │ - break outer; │ │ │ │ │ - } else { │ │ │ │ │ - result = OpenLayers.Geometry.distanceToSegment({ │ │ │ │ │ - x: x0, │ │ │ │ │ - y: y0 │ │ │ │ │ - }, seg1); │ │ │ │ │ - if (result.distance < min) { │ │ │ │ │ - min = result.distance; │ │ │ │ │ - best = { │ │ │ │ │ - distance: min, │ │ │ │ │ - x0: x0, │ │ │ │ │ - y0: y0, │ │ │ │ │ - x1: result.x, │ │ │ │ │ - y1: result.y │ │ │ │ │ - }; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - if (!details) { │ │ │ │ │ - best = best.distance; │ │ │ │ │ - } │ │ │ │ │ - if (min !== 0) { │ │ │ │ │ - // check the final vertex in this line's sorted segments │ │ │ │ │ - if (seg0) { │ │ │ │ │ - result = geometry.distanceTo( │ │ │ │ │ - new OpenLayers.Geometry.Point(seg0.x2, seg0.y2), │ │ │ │ │ - options │ │ │ │ │ - ); │ │ │ │ │ - var dist = details ? result.distance : result; │ │ │ │ │ - if (dist < min) { │ │ │ │ │ - if (details) { │ │ │ │ │ - best = { │ │ │ │ │ - distance: min, │ │ │ │ │ - x0: result.x1, │ │ │ │ │ - y0: result.y1, │ │ │ │ │ - x1: result.x0, │ │ │ │ │ - y1: result.y0 │ │ │ │ │ - }; │ │ │ │ │ - } else { │ │ │ │ │ - best = dist; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ + 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 { │ │ │ │ │ - best = geometry.distanceTo(this, options); │ │ │ │ │ - // swap since target comes from this line │ │ │ │ │ - if (details) { │ │ │ │ │ - best = { │ │ │ │ │ - distance: best.distance, │ │ │ │ │ - x0: best.x1, │ │ │ │ │ - y0: best.y1, │ │ │ │ │ - x1: best.x0, │ │ │ │ │ - y1: best.y0 │ │ │ │ │ - }; │ │ │ │ │ - } │ │ │ │ │ + event.cancelBubble = true; │ │ │ │ │ } │ │ │ │ │ - return best; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: simplify │ │ │ │ │ - * This function will return a simplified LineString. │ │ │ │ │ - * Simplification is based on the Douglas-Peucker algorithm. │ │ │ │ │ - * │ │ │ │ │ + * Method: preventDefault │ │ │ │ │ + * Cancels the event if it is cancelable, without stopping further │ │ │ │ │ + * propagation of the event. │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * tolerance - {number} threshhold for simplification in map units │ │ │ │ │ - * │ │ │ │ │ + * event - {Event} │ │ │ │ │ + */ │ │ │ │ │ + preventDefault: function(event) { │ │ │ │ │ + if (event.preventDefault) { │ │ │ │ │ + event.preventDefault(); │ │ │ │ │ + } else { │ │ │ │ │ + event.returnValue = false; │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Method: findElement │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * event - {Event} │ │ │ │ │ + * tagName - {String} │ │ │ │ │ + * │ │ │ │ │ * Returns: │ │ │ │ │ - * {OpenLayers.Geometry.LineString} the simplified LineString │ │ │ │ │ + * {DOMElement} The first node with the given tagName, starting from the │ │ │ │ │ + * node the event was triggered on and traversing the DOM upwards │ │ │ │ │ */ │ │ │ │ │ - simplify: function(tolerance) { │ │ │ │ │ - if (this && this !== null) { │ │ │ │ │ - var points = this.getVertices(); │ │ │ │ │ - if (points.length < 3) { │ │ │ │ │ - return this; │ │ │ │ │ + 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 compareNumbers = function(a, b) { │ │ │ │ │ - return (a - b); │ │ │ │ │ - }; │ │ │ │ │ + var cacheID = element._eventCacheID; │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Private function doing the Douglas-Peucker reduction │ │ │ │ │ - */ │ │ │ │ │ - var douglasPeuckerReduction = function(points, firstPoint, lastPoint, tolerance) { │ │ │ │ │ - var maxDistance = 0; │ │ │ │ │ - var indexFarthest = 0; │ │ │ │ │ + //if there is not yet a hash entry for this element, add one │ │ │ │ │ + if (!this.observers[cacheID]) { │ │ │ │ │ + this.observers[cacheID] = []; │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ - for (var index = firstPoint, distance; index < lastPoint; index++) { │ │ │ │ │ - distance = perpendicularDistance(points[firstPoint], points[lastPoint], points[index]); │ │ │ │ │ - if (distance > maxDistance) { │ │ │ │ │ - maxDistance = distance; │ │ │ │ │ - indexFarthest = index; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ + //add a new observer to this element's list │ │ │ │ │ + this.observers[cacheID].push({ │ │ │ │ │ + 'element': element, │ │ │ │ │ + 'name': name, │ │ │ │ │ + 'observer': observer, │ │ │ │ │ + 'useCapture': useCapture │ │ │ │ │ + }); │ │ │ │ │ │ │ │ │ │ - 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); │ │ │ │ │ - } │ │ │ │ │ - }; │ │ │ │ │ + //add the actual browser event listener │ │ │ │ │ + if (element.addEventListener) { │ │ │ │ │ + element.addEventListener(name, observer, useCapture); │ │ │ │ │ + } else if (element.attachEvent) { │ │ │ │ │ + element.attachEvent('on' + name, observer); │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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 │ │ │ │ │ + /** │ │ │ │ │ + * 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; │ │ │ │ │ │ │ │ │ │ - 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; │ │ │ │ │ + this._removeElementObservers(OpenLayers.Event.observers[cacheID]); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - return height; │ │ │ │ │ - }; │ │ │ │ │ + /** │ │ │ │ │ + * 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 │ │ │ │ │ + ]); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - var firstPoint = 0; │ │ │ │ │ - var lastPoint = points.length - 1; │ │ │ │ │ - var pointIndexsToKeep = []; │ │ │ │ │ + /** │ │ │ │ │ + * 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; │ │ │ │ │ │ │ │ │ │ - //Add the first and last index to the keepers │ │ │ │ │ - pointIndexsToKeep.push(firstPoint); │ │ │ │ │ - pointIndexsToKeep.push(lastPoint); │ │ │ │ │ + var element = OpenLayers.Util.getElement(elementParam); │ │ │ │ │ + var cacheID = element._eventCacheID; │ │ │ │ │ │ │ │ │ │ - //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); │ │ │ │ │ + if (name == 'keypress') { │ │ │ │ │ + if (navigator.appVersion.match(/Konqueror|Safari|KHTML/) || │ │ │ │ │ + element.detachEvent) { │ │ │ │ │ + name = 'keydown'; │ │ │ │ │ } │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ - douglasPeuckerReduction(points, firstPoint, lastPoint, tolerance); │ │ │ │ │ - var returnPoints = []; │ │ │ │ │ - pointIndexsToKeep.sort(compareNumbers); │ │ │ │ │ - for (var index = 0; index < pointIndexsToKeep.length; index++) { │ │ │ │ │ - returnPoints.push(points[pointIndexsToKeep[index]]); │ │ │ │ │ + // 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++; │ │ │ │ │ } │ │ │ │ │ - return new OpenLayers.Geometry.LineString(returnPoints); │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ - } else { │ │ │ │ │ - return this; │ │ │ │ │ + //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; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Geometry.LineString" │ │ │ │ │ -}); │ │ │ │ │ -/* ====================================================================== │ │ │ │ │ - OpenLayers/Geometry/MultiLineString.js │ │ │ │ │ - ====================================================================== */ │ │ │ │ │ + /** │ │ │ │ │ + * 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; │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ - * full text of the license. */ │ │ │ │ │ + CLASS_NAME: "OpenLayers.Event" │ │ │ │ │ +}; │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * @requires OpenLayers/Geometry/Collection.js │ │ │ │ │ - * @requires OpenLayers/Geometry/LineString.js │ │ │ │ │ - */ │ │ │ │ │ +/* prevent memory leaks in IE */ │ │ │ │ │ +OpenLayers.Event.observe(window, 'unload', OpenLayers.Event.unloadCache, false); │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Class: OpenLayers.Geometry.MultiLineString │ │ │ │ │ - * A MultiLineString is a geometry with multiple │ │ │ │ │ - * components. │ │ │ │ │ - * │ │ │ │ │ - * Inherits from: │ │ │ │ │ - * - │ │ │ │ │ - * - │ │ │ │ │ + * Class: OpenLayers.Events │ │ │ │ │ */ │ │ │ │ │ -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"], │ │ │ │ │ +OpenLayers.Events = OpenLayers.Class({ │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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 < len; ++i) { │ │ │ │ │ - sourceLine = this.components[i]; │ │ │ │ │ - sourceSplit = false; │ │ │ │ │ - for (var j = 0; j < targetParts.length; ++j) { │ │ │ │ │ - splits = sourceLine.split(targetParts[j], options); │ │ │ │ │ - if (splits) { │ │ │ │ │ - if (mutual) { │ │ │ │ │ - sourceLines = splits[0]; │ │ │ │ │ - for (var k = 0, klen = sourceLines.length; k < klen; ++k) { │ │ │ │ │ - if (k === 0 && sourceParts.length) { │ │ │ │ │ - sourceParts[sourceParts.length - 1].addComponent( │ │ │ │ │ - sourceLines[k] │ │ │ │ │ - ); │ │ │ │ │ - } else { │ │ │ │ │ - sourceParts.push( │ │ │ │ │ - new OpenLayers.Geometry.MultiLineString([ │ │ │ │ │ - sourceLines[k] │ │ │ │ │ - ]) │ │ │ │ │ - ); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - sourceSplit = true; │ │ │ │ │ - splits = splits[1]; │ │ │ │ │ - } │ │ │ │ │ - if (splits.length) { │ │ │ │ │ - // splice in new target parts │ │ │ │ │ - splits.unshift(j, 1); │ │ │ │ │ - Array.prototype.splice.apply(targetParts, splits); │ │ │ │ │ - break; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - if (!sourceSplit) { │ │ │ │ │ - // source line was not hit │ │ │ │ │ - if (sourceParts.length) { │ │ │ │ │ - // add line to existing multi │ │ │ │ │ - sourceParts[sourceParts.length - 1].addComponent( │ │ │ │ │ - sourceLine.clone() │ │ │ │ │ - ); │ │ │ │ │ - } else { │ │ │ │ │ - // create a fresh multi │ │ │ │ │ - sourceParts = [ │ │ │ │ │ - new OpenLayers.Geometry.MultiLineString( │ │ │ │ │ - sourceLine.clone() │ │ │ │ │ - ) │ │ │ │ │ - ]; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - if (sourceParts && sourceParts.length > 1) { │ │ │ │ │ - sourceSplit = true; │ │ │ │ │ - } else { │ │ │ │ │ - sourceParts = []; │ │ │ │ │ - } │ │ │ │ │ - if (targetParts && targetParts.length > 1) { │ │ │ │ │ - targetSplit = true; │ │ │ │ │ - } else { │ │ │ │ │ - targetParts = []; │ │ │ │ │ - } │ │ │ │ │ - if (sourceSplit || targetSplit) { │ │ │ │ │ - if (mutual) { │ │ │ │ │ - results = [sourceParts, targetParts]; │ │ │ │ │ - } else { │ │ │ │ │ - results = targetParts; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - return results; │ │ │ │ │ - }, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Method: splitWith │ │ │ │ │ - * Split this geometry (the target) with the given geometry (the source). │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * geometry - {} A geometry used to split this │ │ │ │ │ - * geometry (the source). │ │ │ │ │ - * options - {Object} Properties of this object will be used to determine │ │ │ │ │ - * how the split is conducted. │ │ │ │ │ - * │ │ │ │ │ - * Valid options: │ │ │ │ │ - * mutual - {Boolean} Split the source geometry in addition to the target │ │ │ │ │ - * geometry. Default is false. │ │ │ │ │ - * edge - {Boolean} Allow splitting when only edges intersect. Default is │ │ │ │ │ - * true. If false, a vertex on the source must be within the tolerance │ │ │ │ │ - * distance of the intersection to be considered a split. │ │ │ │ │ - * tolerance - {Number} If a non-null value is provided, intersections │ │ │ │ │ - * within the tolerance distance of an existing vertex on the source │ │ │ │ │ - * will be assumed to occur at the vertex. │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Array} A list of geometries (of this same type as the target) that │ │ │ │ │ - * result from splitting the target with the source geometry. The │ │ │ │ │ - * source and target geometry will remain unmodified. If no split │ │ │ │ │ - * results, null will be returned. If mutual is true and a split │ │ │ │ │ - * results, return will be an array of two arrays - the first will be │ │ │ │ │ - * all geometries that result from splitting the source geometry and │ │ │ │ │ - * the second will be all geometries that result from splitting the │ │ │ │ │ - * target geometry. │ │ │ │ │ - */ │ │ │ │ │ - splitWith: function(geometry, options) { │ │ │ │ │ - var results = null; │ │ │ │ │ - var mutual = options && options.mutual; │ │ │ │ │ - var splits, targetLine, sourceLines, sourceSplit, targetSplit, sourceParts, targetParts; │ │ │ │ │ - if (geometry instanceof OpenLayers.Geometry.LineString) { │ │ │ │ │ - targetParts = []; │ │ │ │ │ - sourceParts = [geometry]; │ │ │ │ │ - for (var i = 0, len = this.components.length; i < len; ++i) { │ │ │ │ │ - targetSplit = false; │ │ │ │ │ - targetLine = this.components[i]; │ │ │ │ │ - for (var j = 0; j < sourceParts.length; ++j) { │ │ │ │ │ - splits = sourceParts[j].split(targetLine, options); │ │ │ │ │ - if (splits) { │ │ │ │ │ - if (mutual) { │ │ │ │ │ - sourceLines = splits[0]; │ │ │ │ │ - if (sourceLines.length) { │ │ │ │ │ - // splice in new source parts │ │ │ │ │ - sourceLines.unshift(j, 1); │ │ │ │ │ - Array.prototype.splice.apply(sourceParts, sourceLines); │ │ │ │ │ - j += sourceLines.length - 2; │ │ │ │ │ - } │ │ │ │ │ - splits = splits[1]; │ │ │ │ │ - if (splits.length === 0) { │ │ │ │ │ - splits = [targetLine.clone()]; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - for (var k = 0, klen = splits.length; k < klen; ++k) { │ │ │ │ │ - if (k === 0 && targetParts.length) { │ │ │ │ │ - targetParts[targetParts.length - 1].addComponent( │ │ │ │ │ - splits[k] │ │ │ │ │ - ); │ │ │ │ │ - } else { │ │ │ │ │ - targetParts.push( │ │ │ │ │ - new OpenLayers.Geometry.MultiLineString([ │ │ │ │ │ - splits[k] │ │ │ │ │ - ]) │ │ │ │ │ - ); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - targetSplit = true; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - if (!targetSplit) { │ │ │ │ │ - // target component was not hit │ │ │ │ │ - if (targetParts.length) { │ │ │ │ │ - // add it to any existing multi-line │ │ │ │ │ - targetParts[targetParts.length - 1].addComponent( │ │ │ │ │ - targetLine.clone() │ │ │ │ │ - ); │ │ │ │ │ - } else { │ │ │ │ │ - // or start with a fresh multi-line │ │ │ │ │ - targetParts = [ │ │ │ │ │ - new OpenLayers.Geometry.MultiLineString([ │ │ │ │ │ - targetLine.clone() │ │ │ │ │ - ]) │ │ │ │ │ - ]; │ │ │ │ │ - } │ │ │ │ │ - │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - } else { │ │ │ │ │ - results = geometry.split(this); │ │ │ │ │ - } │ │ │ │ │ - if (sourceParts && sourceParts.length > 1) { │ │ │ │ │ - sourceSplit = true; │ │ │ │ │ - } else { │ │ │ │ │ - sourceParts = []; │ │ │ │ │ - } │ │ │ │ │ - if (targetParts && targetParts.length > 1) { │ │ │ │ │ - targetSplit = true; │ │ │ │ │ - } else { │ │ │ │ │ - targetParts = []; │ │ │ │ │ - } │ │ │ │ │ - if (sourceSplit || targetSplit) { │ │ │ │ │ - if (mutual) { │ │ │ │ │ - results = [sourceParts, targetParts]; │ │ │ │ │ - } else { │ │ │ │ │ - results = targetParts; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - return results; │ │ │ │ │ - }, │ │ │ │ │ - │ │ │ │ │ - CLASS_NAME: "OpenLayers.Geometry.MultiLineString" │ │ │ │ │ - }); │ │ │ │ │ -/* ====================================================================== │ │ │ │ │ - OpenLayers/Geometry/LinearRing.js │ │ │ │ │ - ====================================================================== */ │ │ │ │ │ + /** │ │ │ │ │ + * 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" │ │ │ │ │ + ], │ │ │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ - * full text of the license. */ │ │ │ │ │ + /** │ │ │ │ │ + * Property: listeners │ │ │ │ │ + * {Object} Hashtable of Array(Function): events listener functions │ │ │ │ │ + */ │ │ │ │ │ + listeners: null, │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * @requires OpenLayers/Geometry/LineString.js │ │ │ │ │ - */ │ │ │ │ │ + /** │ │ │ │ │ + * Property: object │ │ │ │ │ + * {Object} the code object issuing application events │ │ │ │ │ + */ │ │ │ │ │ + object: null, │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * 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: element │ │ │ │ │ + * {DOMElement} the DOM element receiving browser events │ │ │ │ │ + */ │ │ │ │ │ + element: 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: ["OpenLayers.Geometry.Point"], │ │ │ │ │ + /** │ │ │ │ │ + * Property: eventHandler │ │ │ │ │ + * {Function} bound event handler attached to elements │ │ │ │ │ + */ │ │ │ │ │ + eventHandler: null, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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 │ │ │ │ │ - */ │ │ │ │ │ + /** │ │ │ │ │ + * APIProperty: fallThrough │ │ │ │ │ + * {Boolean} │ │ │ │ │ + */ │ │ │ │ │ + fallThrough: null, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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; │ │ │ │ │ + /** │ │ │ │ │ + * 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, │ │ │ │ │ │ │ │ │ │ - //remove last point │ │ │ │ │ - var lastPoint = this.components.pop(); │ │ │ │ │ + /** │ │ │ │ │ + * 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, │ │ │ │ │ │ │ │ │ │ - // 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); │ │ │ │ │ - } │ │ │ │ │ + /** │ │ │ │ │ + * Property: extensionCount │ │ │ │ │ + * {Object} Keys are event types (like in ), values are the │ │ │ │ │ + * number of extension listeners for each event type. │ │ │ │ │ + */ │ │ │ │ │ + extensionCount: null, │ │ │ │ │ │ │ │ │ │ - //append copy of first point │ │ │ │ │ - var firstPoint = this.components[0]; │ │ │ │ │ - OpenLayers.Geometry.Collection.prototype.addComponent.apply(this, │ │ │ │ │ - [firstPoint]); │ │ │ │ │ + /** │ │ │ │ │ + * Method: clearMouseListener │ │ │ │ │ + * A version of that is bound to this instance so that │ │ │ │ │ + * it can be used with and │ │ │ │ │ + * . │ │ │ │ │ + */ │ │ │ │ │ + clearMouseListener: null, │ │ │ │ │ │ │ │ │ │ - return added; │ │ │ │ │ - }, │ │ │ │ │ + /** │ │ │ │ │ + * 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 = []; │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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(); │ │ │ │ │ + // 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); │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - //remove our point │ │ │ │ │ - OpenLayers.Geometry.Collection.prototype.removeComponent.apply(this, │ │ │ │ │ - arguments); │ │ │ │ │ - //append copy of first point │ │ │ │ │ - var firstPoint = this.components[0]; │ │ │ │ │ - OpenLayers.Geometry.Collection.prototype.addComponent.apply(this, │ │ │ │ │ - [firstPoint]); │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: destroy │ │ │ │ │ + */ │ │ │ │ │ + destroy: function() { │ │ │ │ │ + for (var e in this.extensions) { │ │ │ │ │ + if (typeof this.extensions[e] !== "boolean") { │ │ │ │ │ + this.extensions[e].destroy(); │ │ │ │ │ } │ │ │ │ │ - return removed; │ │ │ │ │ - }, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * APIMethod: move │ │ │ │ │ - * Moves a geometry by the given displacement along positive x and y axes. │ │ │ │ │ - * This modifies the position of the geometry and clears the cached │ │ │ │ │ - * bounds. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * x - {Float} Distance to move geometry in positive x direction. │ │ │ │ │ - * y - {Float} Distance to move geometry in positive y direction. │ │ │ │ │ - */ │ │ │ │ │ - move: function(x, y) { │ │ │ │ │ - for (var i = 0, len = this.components.length; i < len - 1; i++) { │ │ │ │ │ - this.components[i].move(x, y); │ │ │ │ │ + } │ │ │ │ │ + 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; │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIMethod: rotate │ │ │ │ │ - * Rotate a geometry around some origin │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * angle - {Float} Rotation angle in degrees (measured counterclockwise │ │ │ │ │ - * from the positive x-axis) │ │ │ │ │ - * origin - {} Center point for the rotation │ │ │ │ │ - */ │ │ │ │ │ - rotate: function(angle, origin) { │ │ │ │ │ - for (var i = 0, len = this.components.length; i < len - 1; ++i) { │ │ │ │ │ - this.components[i].rotate(angle, origin); │ │ │ │ │ - } │ │ │ │ │ - }, │ │ │ │ │ + this.listeners = null; │ │ │ │ │ + this.object = null; │ │ │ │ │ + this.fallThrough = null; │ │ │ │ │ + this.eventHandler = null; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIMethod: resize │ │ │ │ │ - * Resize a geometry relative to some origin. Use this method to apply │ │ │ │ │ - * a uniform scaling to a geometry. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * scale - {Float} Factor by which to scale the geometry. A scale of 2 │ │ │ │ │ - * doubles the size of the geometry in each dimension │ │ │ │ │ - * (lines, for example, will be twice as long, and polygons │ │ │ │ │ - * will have four times the area). │ │ │ │ │ - * origin - {} 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 < len - 1; ++i) { │ │ │ │ │ - this.components[i].resize(scale, origin, ratio); │ │ │ │ │ - } │ │ │ │ │ - return this; │ │ │ │ │ - }, │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: addEventType │ │ │ │ │ + * Deprecated. Any event can be triggered without adding it first. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * eventName - {String} │ │ │ │ │ + */ │ │ │ │ │ + addEventType: function(eventName) {}, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIMethod: transform │ │ │ │ │ - * Reproject the components geometry from source to dest. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * source - {} │ │ │ │ │ - * dest - {} │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {} │ │ │ │ │ - */ │ │ │ │ │ - transform: function(source, dest) { │ │ │ │ │ - if (source && dest) { │ │ │ │ │ - for (var i = 0, len = this.components.length; i < len - 1; i++) { │ │ │ │ │ - var component = this.components[i]; │ │ │ │ │ - component.transform(source, dest); │ │ │ │ │ - } │ │ │ │ │ - this.bounds = null; │ │ │ │ │ - } │ │ │ │ │ - return this; │ │ │ │ │ - }, │ │ │ │ │ + /** │ │ │ │ │ + * 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 │ │ │ │ │ + ); │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIMethod: getCentroid │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {} 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; │ │ │ │ │ - } │ │ │ │ │ + // 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: getArea │ │ │ │ │ - * Note - The area is positive if the ring is oriented CW, otherwise │ │ │ │ │ - * it will be negative. │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Float} The signed area for a ring. │ │ │ │ │ - */ │ │ │ │ │ - getArea: function() { │ │ │ │ │ - var area = 0.0; │ │ │ │ │ - if (this.components && (this.components.length > 2)) { │ │ │ │ │ - var sum = 0.0; │ │ │ │ │ - for (var i = 0, len = this.components.length; i < len - 1; i++) { │ │ │ │ │ - var b = this.components[i]; │ │ │ │ │ - var c = this.components[i + 1]; │ │ │ │ │ - sum += (b.x + c.x) * (c.y - b.y); │ │ │ │ │ - } │ │ │ │ │ - area = -sum / 2.0; │ │ │ │ │ + /** │ │ │ │ │ + * 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]); │ │ │ │ │ } │ │ │ │ │ - return area; │ │ │ │ │ - }, │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIMethod: getGeodesicArea │ │ │ │ │ - * Calculate the approximate area of the polygon were it projected onto │ │ │ │ │ - * the earth. Note that this area will be positive if ring is oriented │ │ │ │ │ - * clockwise, otherwise it will be negative. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * projection - {} The spatial reference system │ │ │ │ │ - * for the geometry coordinates. If not provided, Geographic/WGS84 is │ │ │ │ │ - * assumed. │ │ │ │ │ - * │ │ │ │ │ - * Reference: │ │ │ │ │ - * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for │ │ │ │ │ - * Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion │ │ │ │ │ - * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409 │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {float} The approximate signed geodesic area of the polygon in square │ │ │ │ │ - * meters. │ │ │ │ │ - */ │ │ │ │ │ - getGeodesicArea: function(projection) { │ │ │ │ │ - var ring = this; // so we can work with a clone if needed │ │ │ │ │ - if (projection) { │ │ │ │ │ - var gg = new OpenLayers.Projection("EPSG:4326"); │ │ │ │ │ - if (!gg.equals(projection)) { │ │ │ │ │ - ring = this.clone().transform(projection, gg); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - var area = 0.0; │ │ │ │ │ - var len = ring.components && ring.components.length; │ │ │ │ │ - if (len > 2) { │ │ │ │ │ - var p1, p2; │ │ │ │ │ - for (var i = 0; i < len - 1; i++) { │ │ │ │ │ - p1 = ring.components[i]; │ │ │ │ │ - p2 = ring.components[i + 1]; │ │ │ │ │ - area += OpenLayers.Util.rad(p2.x - p1.x) * │ │ │ │ │ - (2 + Math.sin(OpenLayers.Util.rad(p1.y)) + │ │ │ │ │ - Math.sin(OpenLayers.Util.rad(p2.y))); │ │ │ │ │ - } │ │ │ │ │ - area = area * 6378137.0 * 6378137.0 / 2.0; │ │ │ │ │ + /** │ │ │ │ │ + * 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; │ │ │ │ │ } │ │ │ │ │ - return area; │ │ │ │ │ - }, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Method: containsPoint │ │ │ │ │ - * Test if a point is inside a linear ring. For the case where a point │ │ │ │ │ - * is coincident with a linear ring edge, returns 1. Otherwise, │ │ │ │ │ - * returns boolean. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * point - {} │ │ │ │ │ - * │ │ │ │ │ - * 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 listeners = this.listeners[type]; │ │ │ │ │ + if (!listeners) { │ │ │ │ │ + listeners = []; │ │ │ │ │ + this.listeners[type] = listeners; │ │ │ │ │ + this.extensionCount[type] = 0; │ │ │ │ │ } │ │ │ │ │ - var numSeg = this.components.length - 1; │ │ │ │ │ - var start, end, x1, y1, x2, y2, cx, cy; │ │ │ │ │ - var crosses = 0; │ │ │ │ │ - for (var i = 0; i < numSeg; ++i) { │ │ │ │ │ - start = this.components[i]; │ │ │ │ │ - x1 = approx(start.x, digs); │ │ │ │ │ - y1 = approx(start.y, digs); │ │ │ │ │ - end = this.components[i + 1]; │ │ │ │ │ - x2 = approx(end.x, digs); │ │ │ │ │ - y2 = approx(end.y, digs); │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * The following conditions enforce five edge-crossing rules: │ │ │ │ │ - * 1. points coincident with edges are considered contained; │ │ │ │ │ - * 2. an upward edge includes its starting endpoint, and │ │ │ │ │ - * excludes its final endpoint; │ │ │ │ │ - * 3. a downward edge excludes its starting endpoint, and │ │ │ │ │ - * includes its final endpoint; │ │ │ │ │ - * 4. horizontal edges are excluded; and │ │ │ │ │ - * 5. the edge-ray intersection point must be strictly right │ │ │ │ │ - * of the point P. │ │ │ │ │ - */ │ │ │ │ │ - if (y1 == y2) { │ │ │ │ │ - // horizontal edge │ │ │ │ │ - if (py == y1) { │ │ │ │ │ - // point on horizontal line │ │ │ │ │ - if (x1 <= x2 && (px >= x1 && px <= x2) || // right or vert │ │ │ │ │ - x1 >= x2 && (px <= x1 && px >= x2)) { // left or vert │ │ │ │ │ - // point on edge │ │ │ │ │ - crosses = -1; │ │ │ │ │ - break; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - // ignore other horizontal edges │ │ │ │ │ - continue; │ │ │ │ │ - } │ │ │ │ │ - cx = approx(getX(py, x1, y1, x2, y2), digs); │ │ │ │ │ - if (cx == px) { │ │ │ │ │ - // point on line │ │ │ │ │ - if (y1 < y2 && (py >= y1 && py <= y2) || // upward │ │ │ │ │ - y1 > y2 && (py <= y1 && py >= y2)) { // downward │ │ │ │ │ - // point on edge │ │ │ │ │ - crosses = -1; │ │ │ │ │ - break; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - if (cx <= px) { │ │ │ │ │ - // no crossing to the right │ │ │ │ │ - continue; │ │ │ │ │ - } │ │ │ │ │ - if (x1 != x2 && (cx < Math.min(x1, x2) || cx > Math.max(x1, x2))) { │ │ │ │ │ - // no crossing │ │ │ │ │ - continue; │ │ │ │ │ - } │ │ │ │ │ - if (y1 < y2 && (py >= y1 && py < y2) || // upward │ │ │ │ │ - y1 > y2 && (py < y1 && py >= y2)) { // downward │ │ │ │ │ - ++crosses; │ │ │ │ │ + var listener = { │ │ │ │ │ + obj: obj, │ │ │ │ │ + func: func │ │ │ │ │ + }; │ │ │ │ │ + if (priority) { │ │ │ │ │ + listeners.splice(this.extensionCount[type], 0, listener); │ │ │ │ │ + if (typeof priority === "object" && priority.extension) { │ │ │ │ │ + this.extensionCount[type]++; │ │ │ │ │ } │ │ │ │ │ - } │ │ │ │ │ - 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 < len; ++i) { │ │ │ │ │ - intersect = geometry.components[i].intersects(this); │ │ │ │ │ - if (intersect) { │ │ │ │ │ - break; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - return intersect; │ │ │ │ │ - }, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * APIMethod: getVertices │ │ │ │ │ - * Return a list of all points in this geometry. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * nodes - {Boolean} For lines, only return vertices that are │ │ │ │ │ - * endpoints. If false, for lines, only vertices that are not │ │ │ │ │ - * endpoints will be returned. If not provided, all vertices will │ │ │ │ │ - * be returned. │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Array} A list of all vertices in the geometry. │ │ │ │ │ - */ │ │ │ │ │ - getVertices: function(nodes) { │ │ │ │ │ - return (nodes === true) ? [] : this.components.slice(0, this.components.length - 1); │ │ │ │ │ - }, │ │ │ │ │ - │ │ │ │ │ - CLASS_NAME: "OpenLayers.Geometry.LinearRing" │ │ │ │ │ - }); │ │ │ │ │ -/* ====================================================================== │ │ │ │ │ - OpenLayers/Geometry/Polygon.js │ │ │ │ │ - ====================================================================== */ │ │ │ │ │ - │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ - * full text of the license. */ │ │ │ │ │ - │ │ │ │ │ -/** │ │ │ │ │ - * @requires OpenLayers/Geometry/Collection.js │ │ │ │ │ - * @requires OpenLayers/Geometry/LinearRing.js │ │ │ │ │ - */ │ │ │ │ │ - │ │ │ │ │ -/** │ │ │ │ │ - * Class: OpenLayers.Geometry.Polygon │ │ │ │ │ - * Polygon is a collection of Geometry.LinearRings. │ │ │ │ │ - * │ │ │ │ │ - * Inherits from: │ │ │ │ │ - * - │ │ │ │ │ - * - │ │ │ │ │ - */ │ │ │ │ │ -OpenLayers.Geometry.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 < len; i++) { │ │ │ │ │ - area -= Math.abs(this.components[i].getArea()); │ │ │ │ │ - } │ │ │ │ │ + listeners.push(listener); │ │ │ │ │ } │ │ │ │ │ - return area; │ │ │ │ │ - }, │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIMethod: getGeodesicArea │ │ │ │ │ - * Calculate the approximate area of the polygon were it projected onto │ │ │ │ │ - * the earth. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * projection - {} The spatial reference system │ │ │ │ │ - * for the geometry coordinates. If not provided, Geographic/WGS84 is │ │ │ │ │ - * assumed. │ │ │ │ │ - * │ │ │ │ │ - * Reference: │ │ │ │ │ - * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for │ │ │ │ │ - * Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion │ │ │ │ │ - * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409 │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {float} The approximate geodesic area of the polygon in square meters. │ │ │ │ │ - */ │ │ │ │ │ - getGeodesicArea: function(projection) { │ │ │ │ │ - var area = 0.0; │ │ │ │ │ - if (this.components && (this.components.length > 0)) { │ │ │ │ │ - area += Math.abs(this.components[0].getGeodesicArea(projection)); │ │ │ │ │ - for (var i = 1, len = this.components.length; i < len; i++) { │ │ │ │ │ - area -= Math.abs(this.components[i].getGeodesicArea(projection)); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - return area; │ │ │ │ │ - }, │ │ │ │ │ + /** │ │ │ │ │ + * 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); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: containsPoint │ │ │ │ │ - * Test if a point is inside a polygon. Points on a polygon edge are │ │ │ │ │ - * considered inside. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * point - {} │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Boolean | Number} The point is inside the polygon. Returns 1 if the │ │ │ │ │ - * point is on an edge. Returns boolean otherwise. │ │ │ │ │ - */ │ │ │ │ │ - containsPoint: function(point) { │ │ │ │ │ - var numRings = this.components.length; │ │ │ │ │ - var contained = false; │ │ │ │ │ - if (numRings > 0) { │ │ │ │ │ - // check exterior ring - 1 means on edge, boolean otherwise │ │ │ │ │ - contained = this.components[0].containsPoint(point); │ │ │ │ │ - if (contained !== 1) { │ │ │ │ │ - if (contained && numRings > 1) { │ │ │ │ │ - // check interior rings │ │ │ │ │ - var hole; │ │ │ │ │ - for (var i = 1; i < numRings; ++i) { │ │ │ │ │ - hole = this.components[i].containsPoint(point); │ │ │ │ │ - if (hole) { │ │ │ │ │ - if (hole === 1) { │ │ │ │ │ - // on edge │ │ │ │ │ - contained = 1; │ │ │ │ │ - } else { │ │ │ │ │ - // in hole │ │ │ │ │ - contained = false; │ │ │ │ │ - } │ │ │ │ │ - break; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ + /** │ │ │ │ │ + * 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]); │ │ │ │ │ } │ │ │ │ │ - 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; │ │ │ │ │ - var i, len; │ │ │ │ │ - if (geometry.CLASS_NAME == "OpenLayers.Geometry.Point") { │ │ │ │ │ - intersect = this.containsPoint(geometry); │ │ │ │ │ - } else if (geometry.CLASS_NAME == "OpenLayers.Geometry.LineString" || │ │ │ │ │ - geometry.CLASS_NAME == "OpenLayers.Geometry.LinearRing") { │ │ │ │ │ - // check if rings/linestrings intersect │ │ │ │ │ - for (i = 0, len = this.components.length; i < len; ++i) { │ │ │ │ │ - intersect = geometry.intersects(this.components[i]); │ │ │ │ │ - if (intersect) { │ │ │ │ │ - break; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - if (!intersect) { │ │ │ │ │ - // check if this poly contains points of the ring/linestring │ │ │ │ │ - for (i = 0, len = geometry.components.length; i < len; ++i) { │ │ │ │ │ - intersect = this.containsPoint(geometry.components[i]); │ │ │ │ │ - if (intersect) { │ │ │ │ │ - break; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - } else { │ │ │ │ │ - for (i = 0, len = geometry.components.length; i < len; ++i) { │ │ │ │ │ - intersect = this.intersects(geometry.components[i]); │ │ │ │ │ - if (intersect) { │ │ │ │ │ - break; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - // check case where this poly is wholly contained by another │ │ │ │ │ - if (!intersect && geometry.CLASS_NAME == "OpenLayers.Geometry.Polygon") { │ │ │ │ │ - // exterior ring points will be contained in the other geometry │ │ │ │ │ - var ring = this.components[0]; │ │ │ │ │ - for (i = 0, len = ring.components.length; i < len; ++i) { │ │ │ │ │ - intersect = geometry.containsPoint(ring.components[i]); │ │ │ │ │ - if (intersect) { │ │ │ │ │ - break; │ │ │ │ │ - } │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: unregister │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * type - {String} │ │ │ │ │ + * obj - {Object} If none specified, defaults to this.object │ │ │ │ │ + * func - {Function} │ │ │ │ │ + */ │ │ │ │ │ + unregister: function(type, obj, func) { │ │ │ │ │ + if (obj == null) { │ │ │ │ │ + obj = this.object; │ │ │ │ │ + } │ │ │ │ │ + var listeners = this.listeners[type]; │ │ │ │ │ + if (listeners != null) { │ │ │ │ │ + for (var i = 0, len = listeners.length; i < len; i++) { │ │ │ │ │ + if (listeners[i].obj == obj && listeners[i].func == func) { │ │ │ │ │ + listeners.splice(i, 1); │ │ │ │ │ + break; │ │ │ │ │ } │ │ │ │ │ } │ │ │ │ │ - return intersect; │ │ │ │ │ - }, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * 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 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 < sides; ++i) { │ │ │ │ │ - rotatedAngle = angle + (i * 2 * Math.PI / sides); │ │ │ │ │ - x = origin.x + (radius * Math.cos(rotatedAngle)); │ │ │ │ │ - y = origin.y + (radius * Math.sin(rotatedAngle)); │ │ │ │ │ - points.push(new OpenLayers.Geometry.Point(x, y)); │ │ │ │ │ - } │ │ │ │ │ - var ring = new OpenLayers.Geometry.LinearRing(points); │ │ │ │ │ - return new OpenLayers.Geometry.Polygon([ring]); │ │ │ │ │ -}; │ │ │ │ │ -/* ====================================================================== │ │ │ │ │ - OpenLayers/Geometry/MultiPolygon.js │ │ │ │ │ - ====================================================================== */ │ │ │ │ │ - │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ - * full text of the license. */ │ │ │ │ │ - │ │ │ │ │ -/** │ │ │ │ │ - * @requires OpenLayers/Geometry/Collection.js │ │ │ │ │ - * @requires OpenLayers/Geometry/Polygon.js │ │ │ │ │ - */ │ │ │ │ │ - │ │ │ │ │ -/** │ │ │ │ │ - * Class: OpenLayers.Geometry.MultiPolygon │ │ │ │ │ - * MultiPolygon is a geometry with multiple │ │ │ │ │ - * 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"], │ │ │ │ │ + /** │ │ │ │ │ + * Method: remove │ │ │ │ │ + * Remove all listeners for a given event type. If type is not registered, │ │ │ │ │ + * does nothing. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * type - {String} │ │ │ │ │ + */ │ │ │ │ │ + remove: function(type) { │ │ │ │ │ + if (this.listeners[type] != null) { │ │ │ │ │ + this.listeners[type] = []; │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Constructor: OpenLayers.Geometry.MultiPolygon │ │ │ │ │ - * Create a new MultiPolygon geometry │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * components - {Array()} An array of polygons │ │ │ │ │ - * used to generate the MultiPolygon │ │ │ │ │ - * │ │ │ │ │ - */ │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: triggerEvent │ │ │ │ │ + * Trigger a specified registered event. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * type - {String} │ │ │ │ │ + * evt - {Event || Object} will be passed to the listeners. │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Boolean} The last listener return. If a listener returns false, the │ │ │ │ │ + * chain of listeners will stop getting called. │ │ │ │ │ + */ │ │ │ │ │ + triggerEvent: function(type, evt) { │ │ │ │ │ + var listeners = this.listeners[type]; │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Geometry.MultiPolygon" │ │ │ │ │ - }); │ │ │ │ │ -/* ====================================================================== │ │ │ │ │ - OpenLayers/Format/WKT.js │ │ │ │ │ - ====================================================================== */ │ │ │ │ │ + // fast path │ │ │ │ │ + if (!listeners || listeners.length == 0) { │ │ │ │ │ + return undefined; │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ - * full text of the license. */ │ │ │ │ │ + // prep evt object with object & div references │ │ │ │ │ + if (evt == null) { │ │ │ │ │ + evt = {}; │ │ │ │ │ + } │ │ │ │ │ + evt.object = this.object; │ │ │ │ │ + evt.element = this.element; │ │ │ │ │ + if (!evt.type) { │ │ │ │ │ + evt.type = type; │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * @requires OpenLayers/Format.js │ │ │ │ │ - * @requires OpenLayers/Feature/Vector.js │ │ │ │ │ - * @requires OpenLayers/Geometry/Point.js │ │ │ │ │ - * @requires OpenLayers/Geometry/MultiPoint.js │ │ │ │ │ - * @requires OpenLayers/Geometry/LineString.js │ │ │ │ │ - * @requires OpenLayers/Geometry/MultiLineString.js │ │ │ │ │ - * @requires OpenLayers/Geometry/Polygon.js │ │ │ │ │ - * @requires OpenLayers/Geometry/MultiPolygon.js │ │ │ │ │ - */ │ │ │ │ │ + // execute all callbacks registered for specified type │ │ │ │ │ + // get a clone of the listeners array to │ │ │ │ │ + // allow for splicing during callbacks │ │ │ │ │ + listeners = listeners.slice(); │ │ │ │ │ + var continueChain; │ │ │ │ │ + for (var i = 0, len = listeners.length; i < len; i++) { │ │ │ │ │ + var callback = listeners[i]; │ │ │ │ │ + // bind the context to callback.obj │ │ │ │ │ + continueChain = callback.func.apply(callback.obj, [evt]); │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * Class: OpenLayers.Format.WKT │ │ │ │ │ - * Class for reading and writing Well-Known Text. Create a new instance │ │ │ │ │ - * with the constructor. │ │ │ │ │ - * │ │ │ │ │ - * Inherits from: │ │ │ │ │ - * - │ │ │ │ │ - */ │ │ │ │ │ -OpenLayers.Format.WKT = OpenLayers.Class(OpenLayers.Format, { │ │ │ │ │ + if ((continueChain != undefined) && (continueChain == false)) { │ │ │ │ │ + // if callback returns false, execute no more callbacks. │ │ │ │ │ + break; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + // don't fall through to other DOM elements │ │ │ │ │ + if (!this.fallThrough) { │ │ │ │ │ + OpenLayers.Event.stop(evt, true); │ │ │ │ │ + } │ │ │ │ │ + return continueChain; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Constructor: OpenLayers.Format.WKT │ │ │ │ │ - * Create a new parser for WKT │ │ │ │ │ + * Method: handleBrowserEvent │ │ │ │ │ + * Basically just a wrapper to the triggerEvent() function, but takes │ │ │ │ │ + * care to set a property 'xy' on the event with the current mouse │ │ │ │ │ + * position. │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ │ - * this instance │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {} A new WKT parser. │ │ │ │ │ + * evt - {Event} │ │ │ │ │ */ │ │ │ │ │ - initialize: function(options) { │ │ │ │ │ - this.regExes = { │ │ │ │ │ - 'typeStr': /^\s*(\w+)\s*\(\s*(.*)\s*\)\s*$/, │ │ │ │ │ - 'spaces': /\s+/, │ │ │ │ │ - 'parenComma': /\)\s*,\s*\(/, │ │ │ │ │ - 'doubleParenComma': /\)\s*\)\s*,\s*\(\s*\(/, // can't use {2} here │ │ │ │ │ - 'trimParens': /^\s*\(?(.*?)\)?\s*$/ │ │ │ │ │ - }; │ │ │ │ │ - OpenLayers.Format.prototype.initialize.apply(this, [options]); │ │ │ │ │ + handleBrowserEvent: function(evt) { │ │ │ │ │ + var type = evt.type, │ │ │ │ │ + listeners = this.listeners[type]; │ │ │ │ │ + if (!listeners || listeners.length == 0) { │ │ │ │ │ + // noone's listening, bail out │ │ │ │ │ + return; │ │ │ │ │ + } │ │ │ │ │ + // add clientX & clientY to all events - corresponds to average x, y │ │ │ │ │ + var touches = evt.touches; │ │ │ │ │ + if (touches && touches[0]) { │ │ │ │ │ + var x = 0; │ │ │ │ │ + var y = 0; │ │ │ │ │ + var num = touches.length; │ │ │ │ │ + var touch; │ │ │ │ │ + for (var i = 0; i < num; ++i) { │ │ │ │ │ + touch = this.getTouchClientXY(touches[i]); │ │ │ │ │ + x += touch.clientX; │ │ │ │ │ + y += touch.clientY; │ │ │ │ │ + } │ │ │ │ │ + evt.clientX = x / num; │ │ │ │ │ + evt.clientY = y / num; │ │ │ │ │ + } │ │ │ │ │ + if (this.includeXY) { │ │ │ │ │ + evt.xy = this.getMousePosition(evt); │ │ │ │ │ + } │ │ │ │ │ + this.triggerEvent(type, evt); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: read │ │ │ │ │ - * Deserialize a WKT string and return a vector feature or an │ │ │ │ │ - * array of vector features. Supports WKT for POINT, MULTIPOINT, │ │ │ │ │ - * LINESTRING, MULTILINESTRING, POLYGON, MULTIPOLYGON, and │ │ │ │ │ - * GEOMETRYCOLLECTION. │ │ │ │ │ + * Method: getTouchClientXY │ │ │ │ │ + * WebKit has a few bugs for clientX/clientY. This method detects them │ │ │ │ │ + * and calculate the correct values. │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * wkt - {String} A WKT string │ │ │ │ │ - * │ │ │ │ │ + * evt - {Touch} a Touch object from a TouchEvent │ │ │ │ │ + * │ │ │ │ │ * Returns: │ │ │ │ │ - * {|Array} A feature or array of features for │ │ │ │ │ - * GEOMETRYCOLLECTION WKT. │ │ │ │ │ + * {Object} An object with only clientX and clientY properties with the │ │ │ │ │ + * calculated values. │ │ │ │ │ */ │ │ │ │ │ - read: function(wkt) { │ │ │ │ │ - var features, type, str; │ │ │ │ │ - wkt = wkt.replace(/[\n\r]/g, " "); │ │ │ │ │ - var matches = this.regExes.typeStr.exec(wkt); │ │ │ │ │ - if (matches) { │ │ │ │ │ - type = matches[1].toLowerCase(); │ │ │ │ │ - str = matches[2]; │ │ │ │ │ - if (this.parse[type]) { │ │ │ │ │ - features = this.parse[type].apply(this, [str]); │ │ │ │ │ - } │ │ │ │ │ - if (this.internalProjection && this.externalProjection) { │ │ │ │ │ - if (features && │ │ │ │ │ - features.CLASS_NAME == "OpenLayers.Feature.Vector") { │ │ │ │ │ - features.geometry.transform(this.externalProjection, │ │ │ │ │ - this.internalProjection); │ │ │ │ │ - } else if (features && │ │ │ │ │ - type != "geometrycollection" && │ │ │ │ │ - typeof features == "object") { │ │ │ │ │ - for (var i = 0, len = features.length; i < len; i++) { │ │ │ │ │ - var component = features[i]; │ │ │ │ │ - component.geometry.transform(this.externalProjection, │ │ │ │ │ - this.internalProjection); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ + getTouchClientXY: function(evt) { │ │ │ │ │ + // olMochWin is to override window, used for testing │ │ │ │ │ + var win = window.olMockWin || window, │ │ │ │ │ + winPageX = win.pageXOffset, │ │ │ │ │ + winPageY = win.pageYOffset, │ │ │ │ │ + x = evt.clientX, │ │ │ │ │ + y = evt.clientY; │ │ │ │ │ + │ │ │ │ │ + if (evt.pageY === 0 && Math.floor(y) > Math.floor(evt.pageY) || │ │ │ │ │ + evt.pageX === 0 && Math.floor(x) > Math.floor(evt.pageX)) { │ │ │ │ │ + // iOS4 include scroll offset in clientX/Y │ │ │ │ │ + x = x - winPageX; │ │ │ │ │ + y = y - winPageY; │ │ │ │ │ + } else if (y < (evt.pageY - winPageY) || x < (evt.pageX - winPageX)) { │ │ │ │ │ + // Some Android browsers have totally bogus values for clientX/Y │ │ │ │ │ + // when scrolling/zooming a page │ │ │ │ │ + x = evt.pageX - winPageX; │ │ │ │ │ + y = evt.pageY - winPageY; │ │ │ │ │ } │ │ │ │ │ - return features; │ │ │ │ │ + │ │ │ │ │ + evt.olClientX = x; │ │ │ │ │ + evt.olClientY = y; │ │ │ │ │ + │ │ │ │ │ + return { │ │ │ │ │ + clientX: x, │ │ │ │ │ + clientY: y │ │ │ │ │ + }; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: write │ │ │ │ │ - * Serialize a feature or array of features into a WKT string. │ │ │ │ │ - * │ │ │ │ │ + * 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: │ │ │ │ │ - * features - {|Array} A feature or array of │ │ │ │ │ - * features │ │ │ │ │ - * │ │ │ │ │ + * evt - {Event} │ │ │ │ │ + * │ │ │ │ │ * Returns: │ │ │ │ │ - * {String} The WKT string representation of the input geometries │ │ │ │ │ + * {} The current xy coordinate of the mouse, adjusted │ │ │ │ │ + * for offsets │ │ │ │ │ */ │ │ │ │ │ - write: function(features) { │ │ │ │ │ - var collection, geometry, isCollection; │ │ │ │ │ - if (features.constructor == Array) { │ │ │ │ │ - collection = features; │ │ │ │ │ - isCollection = true; │ │ │ │ │ - } else { │ │ │ │ │ - collection = [features]; │ │ │ │ │ - isCollection = false; │ │ │ │ │ + getMousePosition: function(evt) { │ │ │ │ │ + if (!this.includeXY) { │ │ │ │ │ + this.clearMouseCache(); │ │ │ │ │ + } else if (!this.element.hasScrollEvent) { │ │ │ │ │ + OpenLayers.Event.observe(window, "scroll", this.clearMouseListener); │ │ │ │ │ + this.element.hasScrollEvent = true; │ │ │ │ │ } │ │ │ │ │ - var pieces = []; │ │ │ │ │ - if (isCollection) { │ │ │ │ │ - pieces.push('GEOMETRYCOLLECTION('); │ │ │ │ │ + │ │ │ │ │ + if (!this.element.scrolls) { │ │ │ │ │ + var viewportElement = OpenLayers.Util.getViewportElement(); │ │ │ │ │ + this.element.scrolls = [ │ │ │ │ │ + window.pageXOffset || viewportElement.scrollLeft, │ │ │ │ │ + window.pageYOffset || viewportElement.scrollTop │ │ │ │ │ + ]; │ │ │ │ │ } │ │ │ │ │ - for (var i = 0, len = collection.length; i < len; ++i) { │ │ │ │ │ - if (isCollection && i > 0) { │ │ │ │ │ - pieces.push(','); │ │ │ │ │ - } │ │ │ │ │ - geometry = collection[i].geometry; │ │ │ │ │ - pieces.push(this.extractGeometry(geometry)); │ │ │ │ │ + │ │ │ │ │ + if (!this.element.lefttop) { │ │ │ │ │ + this.element.lefttop = [ │ │ │ │ │ + (document.documentElement.clientLeft || 0), │ │ │ │ │ + (document.documentElement.clientTop || 0) │ │ │ │ │ + ]; │ │ │ │ │ } │ │ │ │ │ - if (isCollection) { │ │ │ │ │ - pieces.push(')'); │ │ │ │ │ + │ │ │ │ │ + if (!this.element.offsets) { │ │ │ │ │ + this.element.offsets = OpenLayers.Util.pagePosition(this.element); │ │ │ │ │ } │ │ │ │ │ - return pieces.join(''); │ │ │ │ │ + │ │ │ │ │ + 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: extractGeometry │ │ │ │ │ - * Entry point to construct the WKT for a single Geometry object. │ │ │ │ │ + * Method: addMsTouchListener │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * geometry - {} │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {String} A WKT string of representing the geometry │ │ │ │ │ + * element - {DOMElement} The DOM element to register the listener on │ │ │ │ │ + * type - {String} The event type │ │ │ │ │ + * handler - {Function} the handler │ │ │ │ │ */ │ │ │ │ │ - extractGeometry: function(geometry) { │ │ │ │ │ - var type = geometry.CLASS_NAME.split('.')[2].toLowerCase(); │ │ │ │ │ - if (!this.extract[type]) { │ │ │ │ │ - return null; │ │ │ │ │ + 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)); │ │ │ │ │ } │ │ │ │ │ - if (this.internalProjection && this.externalProjection) { │ │ │ │ │ - geometry = geometry.clone(); │ │ │ │ │ - geometry.transform(this.internalProjection, this.externalProjection); │ │ │ │ │ + │ │ │ │ │ + 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'; │ │ │ │ │ } │ │ │ │ │ - var wktType = type == 'collection' ? 'GEOMETRYCOLLECTION' : type.toUpperCase(); │ │ │ │ │ - var data = wktType + '(' + this.extract[type].apply(this, [geometry]) + ')'; │ │ │ │ │ - return data; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Object with properties corresponding to the geometry types. │ │ │ │ │ - * Property values are functions that do the actual data extraction. │ │ │ │ │ + * Method: addMsTouchListenerStart │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * element - {DOMElement} The DOM element to register the listener on │ │ │ │ │ + * type - {String} The event type │ │ │ │ │ + * handler - {Function} the handler │ │ │ │ │ */ │ │ │ │ │ - extract: { │ │ │ │ │ - /** │ │ │ │ │ - * Return a space delimited string of point coordinates. │ │ │ │ │ - * @param {OpenLayers.Geometry.Point} point │ │ │ │ │ - * @returns {String} A string of coordinates representing the point │ │ │ │ │ - */ │ │ │ │ │ - 'point': function(point) { │ │ │ │ │ - return point.x + ' ' + point.y; │ │ │ │ │ - }, │ │ │ │ │ + addMsTouchListenerStart: function(element, type, handler) { │ │ │ │ │ + var touches = this._msTouches; │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Return a comma delimited string of point coordinates from a multipoint. │ │ │ │ │ - * @param {OpenLayers.Geometry.MultiPoint} multipoint │ │ │ │ │ - * @returns {String} A string of point coordinate strings representing │ │ │ │ │ - * the multipoint │ │ │ │ │ - */ │ │ │ │ │ - 'multipoint': function(multipoint) { │ │ │ │ │ - var array = []; │ │ │ │ │ - for (var i = 0, len = multipoint.components.length; i < len; ++i) { │ │ │ │ │ - array.push('(' + │ │ │ │ │ - this.extract.point.apply(this, [multipoint.components[i]]) + │ │ │ │ │ - ')'); │ │ │ │ │ - } │ │ │ │ │ - return array.join(','); │ │ │ │ │ - }, │ │ │ │ │ + var cb = function(e) { │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Return a comma delimited string of point coordinates from a line. │ │ │ │ │ - * @param {OpenLayers.Geometry.LineString} linestring │ │ │ │ │ - * @returns {String} A string of point coordinate strings representing │ │ │ │ │ - * the linestring │ │ │ │ │ - */ │ │ │ │ │ - 'linestring': function(linestring) { │ │ │ │ │ - var array = []; │ │ │ │ │ - for (var i = 0, len = linestring.components.length; i < len; ++i) { │ │ │ │ │ - array.push(this.extract.point.apply(this, [linestring.components[i]])); │ │ │ │ │ + var alreadyInArray = false; │ │ │ │ │ + for (var i = 0, ii = touches.length; i < ii; ++i) { │ │ │ │ │ + if (touches[i].pointerId == e.pointerId) { │ │ │ │ │ + alreadyInArray = true; │ │ │ │ │ + break; │ │ │ │ │ + } │ │ │ │ │ } │ │ │ │ │ - return array.join(','); │ │ │ │ │ - }, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Return a comma delimited string of linestring strings from a multilinestring. │ │ │ │ │ - * @param {OpenLayers.Geometry.MultiLineString} multilinestring │ │ │ │ │ - * @returns {String} A string of of linestring strings representing │ │ │ │ │ - * the multilinestring │ │ │ │ │ - */ │ │ │ │ │ - 'multilinestring': function(multilinestring) { │ │ │ │ │ - var array = []; │ │ │ │ │ - for (var i = 0, len = multilinestring.components.length; i < len; ++i) { │ │ │ │ │ - array.push('(' + │ │ │ │ │ - this.extract.linestring.apply(this, [multilinestring.components[i]]) + │ │ │ │ │ - ')'); │ │ │ │ │ + if (!alreadyInArray) { │ │ │ │ │ + touches.push(e); │ │ │ │ │ } │ │ │ │ │ - return array.join(','); │ │ │ │ │ - }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Return a comma delimited string of linear ring arrays from a polygon. │ │ │ │ │ - * @param {OpenLayers.Geometry.Polygon} polygon │ │ │ │ │ - * @returns {String} An array of linear ring arrays representing the polygon │ │ │ │ │ - */ │ │ │ │ │ - 'polygon': function(polygon) { │ │ │ │ │ - var array = []; │ │ │ │ │ - for (var i = 0, len = polygon.components.length; i < len; ++i) { │ │ │ │ │ - array.push('(' + │ │ │ │ │ - this.extract.linestring.apply(this, [polygon.components[i]]) + │ │ │ │ │ - ')'); │ │ │ │ │ - } │ │ │ │ │ - return array.join(','); │ │ │ │ │ - }, │ │ │ │ │ + e.touches = touches.slice(); │ │ │ │ │ + handler(e); │ │ │ │ │ + }; │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Return an array of polygon arrays from a multipolygon. │ │ │ │ │ - * @param {OpenLayers.Geometry.MultiPolygon} multipolygon │ │ │ │ │ - * @returns {String} An array of polygon arrays representing │ │ │ │ │ - * the multipolygon │ │ │ │ │ - */ │ │ │ │ │ - 'multipolygon': function(multipolygon) { │ │ │ │ │ - var array = []; │ │ │ │ │ - for (var i = 0, len = multipolygon.components.length; i < len; ++i) { │ │ │ │ │ - array.push('(' + │ │ │ │ │ - this.extract.polygon.apply(this, [multipolygon.components[i]]) + │ │ │ │ │ - ')'); │ │ │ │ │ - } │ │ │ │ │ - return array.join(','); │ │ │ │ │ - }, │ │ │ │ │ + OpenLayers.Event.observe(element, 'MSPointerDown', cb); │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Return the WKT portion between 'GEOMETRYCOLLECTION(' and ')' for an │ │ │ │ │ - * @param {OpenLayers.Geometry.Collection} collection │ │ │ │ │ - * @returns {String} internal WKT representation of the collection │ │ │ │ │ - */ │ │ │ │ │ - 'collection': function(collection) { │ │ │ │ │ - var array = []; │ │ │ │ │ - for (var i = 0, len = collection.components.length; i < len; ++i) { │ │ │ │ │ - array.push(this.extractGeometry.apply(this, [collection.components[i]])); │ │ │ │ │ + // Need to also listen for end events to keep the _msTouches list │ │ │ │ │ + // accurate │ │ │ │ │ + var internalCb = function(e) { │ │ │ │ │ + for (var i = 0, ii = touches.length; i < ii; ++i) { │ │ │ │ │ + if (touches[i].pointerId == e.pointerId) { │ │ │ │ │ + touches.splice(i, 1); │ │ │ │ │ + break; │ │ │ │ │ + } │ │ │ │ │ } │ │ │ │ │ - return array.join(','); │ │ │ │ │ - } │ │ │ │ │ - │ │ │ │ │ + }; │ │ │ │ │ + OpenLayers.Event.observe(element, 'MSPointerUp', internalCb); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Object with properties corresponding to the geometry types. │ │ │ │ │ - * Property values are functions that do the actual parsing. │ │ │ │ │ + * Method: addMsTouchListenerMove │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * element - {DOMElement} The DOM element to register the listener on │ │ │ │ │ + * type - {String} The event type │ │ │ │ │ + * handler - {Function} the handler │ │ │ │ │ */ │ │ │ │ │ - parse: { │ │ │ │ │ - /** │ │ │ │ │ - * Return point feature given a point WKT fragment. │ │ │ │ │ - * @param {String} str A WKT fragment representing the point │ │ │ │ │ - * @returns {OpenLayers.Feature.Vector} A point feature │ │ │ │ │ - * @private │ │ │ │ │ - */ │ │ │ │ │ - 'point': function(str) { │ │ │ │ │ - var coords = OpenLayers.String.trim(str).split(this.regExes.spaces); │ │ │ │ │ - return new OpenLayers.Feature.Vector( │ │ │ │ │ - new OpenLayers.Geometry.Point(coords[0], coords[1]) │ │ │ │ │ - ); │ │ │ │ │ - }, │ │ │ │ │ + addMsTouchListenerMove: function(element, type, handler) { │ │ │ │ │ + var touches = this._msTouches; │ │ │ │ │ + var cb = function(e) { │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Return a multipoint feature given a multipoint WKT fragment. │ │ │ │ │ - * @param {String} str A WKT fragment representing the multipoint │ │ │ │ │ - * @returns {OpenLayers.Feature.Vector} A multipoint feature │ │ │ │ │ - * @private │ │ │ │ │ - */ │ │ │ │ │ - 'multipoint': function(str) { │ │ │ │ │ - var point; │ │ │ │ │ - var points = OpenLayers.String.trim(str).split(','); │ │ │ │ │ - var components = []; │ │ │ │ │ - for (var i = 0, len = points.length; i < len; ++i) { │ │ │ │ │ - point = points[i].replace(this.regExes.trimParens, '$1'); │ │ │ │ │ - components.push(this.parse.point.apply(this, [point]).geometry); │ │ │ │ │ + //Don't fire touch moves when mouse isn't down │ │ │ │ │ + if (e.pointerType == e.MSPOINTER_TYPE_MOUSE && e.buttons == 0) { │ │ │ │ │ + return; │ │ │ │ │ } │ │ │ │ │ - return new OpenLayers.Feature.Vector( │ │ │ │ │ - new OpenLayers.Geometry.MultiPoint(components) │ │ │ │ │ - ); │ │ │ │ │ - }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Return a linestring feature given a linestring WKT fragment. │ │ │ │ │ - * @param {String} str A WKT fragment representing the linestring │ │ │ │ │ - * @returns {OpenLayers.Feature.Vector} A linestring feature │ │ │ │ │ - * @private │ │ │ │ │ - */ │ │ │ │ │ - 'linestring': function(str) { │ │ │ │ │ - var points = OpenLayers.String.trim(str).split(','); │ │ │ │ │ - var components = []; │ │ │ │ │ - for (var i = 0, len = points.length; i < len; ++i) { │ │ │ │ │ - components.push(this.parse.point.apply(this, [points[i]]).geometry); │ │ │ │ │ + if (touches.length == 1 && touches[0].pageX == e.pageX && │ │ │ │ │ + touches[0].pageY == e.pageY) { │ │ │ │ │ + // don't trigger event when pointer has not moved │ │ │ │ │ + return; │ │ │ │ │ } │ │ │ │ │ - return new OpenLayers.Feature.Vector( │ │ │ │ │ - new OpenLayers.Geometry.LineString(components) │ │ │ │ │ - ); │ │ │ │ │ - }, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Return a multilinestring feature given a multilinestring WKT fragment. │ │ │ │ │ - * @param {String} str A WKT fragment representing the multilinestring │ │ │ │ │ - * @returns {OpenLayers.Feature.Vector} A multilinestring feature │ │ │ │ │ - * @private │ │ │ │ │ - */ │ │ │ │ │ - 'multilinestring': function(str) { │ │ │ │ │ - var line; │ │ │ │ │ - var lines = OpenLayers.String.trim(str).split(this.regExes.parenComma); │ │ │ │ │ - var components = []; │ │ │ │ │ - for (var i = 0, len = lines.length; i < len; ++i) { │ │ │ │ │ - line = lines[i].replace(this.regExes.trimParens, '$1'); │ │ │ │ │ - components.push(this.parse.linestring.apply(this, [line]).geometry); │ │ │ │ │ + for (var i = 0, ii = touches.length; i < ii; ++i) { │ │ │ │ │ + if (touches[i].pointerId == e.pointerId) { │ │ │ │ │ + touches[i] = e; │ │ │ │ │ + break; │ │ │ │ │ + } │ │ │ │ │ } │ │ │ │ │ - return new OpenLayers.Feature.Vector( │ │ │ │ │ - new OpenLayers.Geometry.MultiLineString(components) │ │ │ │ │ - ); │ │ │ │ │ - }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Return a polygon feature given a polygon WKT fragment. │ │ │ │ │ - * @param {String} str A WKT fragment representing the polygon │ │ │ │ │ - * @returns {OpenLayers.Feature.Vector} A polygon feature │ │ │ │ │ - * @private │ │ │ │ │ - */ │ │ │ │ │ - 'polygon': function(str) { │ │ │ │ │ - var ring, linestring, linearring; │ │ │ │ │ - var rings = OpenLayers.String.trim(str).split(this.regExes.parenComma); │ │ │ │ │ - var components = []; │ │ │ │ │ - for (var i = 0, len = rings.length; i < len; ++i) { │ │ │ │ │ - ring = rings[i].replace(this.regExes.trimParens, '$1'); │ │ │ │ │ - linestring = this.parse.linestring.apply(this, [ring]).geometry; │ │ │ │ │ - linearring = new OpenLayers.Geometry.LinearRing(linestring.components); │ │ │ │ │ - components.push(linearring); │ │ │ │ │ - } │ │ │ │ │ - return new OpenLayers.Feature.Vector( │ │ │ │ │ - new OpenLayers.Geometry.Polygon(components) │ │ │ │ │ - ); │ │ │ │ │ - }, │ │ │ │ │ + e.touches = touches.slice(); │ │ │ │ │ + handler(e); │ │ │ │ │ + }; │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Return a multipolygon feature given a multipolygon WKT fragment. │ │ │ │ │ - * @param {String} str A WKT fragment representing the multipolygon │ │ │ │ │ - * @returns {OpenLayers.Feature.Vector} A multipolygon feature │ │ │ │ │ - * @private │ │ │ │ │ - */ │ │ │ │ │ - 'multipolygon': function(str) { │ │ │ │ │ - var polygon; │ │ │ │ │ - var polygons = OpenLayers.String.trim(str).split(this.regExes.doubleParenComma); │ │ │ │ │ - var components = []; │ │ │ │ │ - for (var i = 0, len = polygons.length; i < len; ++i) { │ │ │ │ │ - polygon = polygons[i].replace(this.regExes.trimParens, '$1'); │ │ │ │ │ - components.push(this.parse.polygon.apply(this, [polygon]).geometry); │ │ │ │ │ - } │ │ │ │ │ - return new OpenLayers.Feature.Vector( │ │ │ │ │ - new OpenLayers.Geometry.MultiPolygon(components) │ │ │ │ │ - ); │ │ │ │ │ - }, │ │ │ │ │ + OpenLayers.Event.observe(element, 'MSPointerMove', cb); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Return an array of features given a geometrycollection WKT fragment. │ │ │ │ │ - * @param {String} str A WKT fragment representing the geometrycollection │ │ │ │ │ - * @returns {Array} An array of OpenLayers.Feature.Vector │ │ │ │ │ - * @private │ │ │ │ │ - */ │ │ │ │ │ - 'geometrycollection': function(str) { │ │ │ │ │ - // separate components of the collection with | │ │ │ │ │ - str = str.replace(/,\s*([A-Za-z])/g, '|$1'); │ │ │ │ │ - var wktArray = OpenLayers.String.trim(str).split('|'); │ │ │ │ │ - var components = []; │ │ │ │ │ - for (var i = 0, len = wktArray.length; i < len; ++i) { │ │ │ │ │ - components.push(OpenLayers.Format.WKT.prototype.read.apply(this, [wktArray[i]])); │ │ │ │ │ + /** │ │ │ │ │ + * Method: addMsTouchListenerEnd │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * element - {DOMElement} The DOM element to register the listener on │ │ │ │ │ + * type - {String} The event type │ │ │ │ │ + * handler - {Function} the handler │ │ │ │ │ + */ │ │ │ │ │ + addMsTouchListenerEnd: function(element, type, handler) { │ │ │ │ │ + var touches = this._msTouches; │ │ │ │ │ + │ │ │ │ │ + var cb = function(e) { │ │ │ │ │ + │ │ │ │ │ + for (var i = 0, ii = touches.length; i < ii; ++i) { │ │ │ │ │ + if (touches[i].pointerId == e.pointerId) { │ │ │ │ │ + touches.splice(i, 1); │ │ │ │ │ + break; │ │ │ │ │ + } │ │ │ │ │ } │ │ │ │ │ - return components; │ │ │ │ │ - } │ │ │ │ │ │ │ │ │ │ + e.touches = touches.slice(); │ │ │ │ │ + handler(e); │ │ │ │ │ + }; │ │ │ │ │ + │ │ │ │ │ + OpenLayers.Event.observe(element, 'MSPointerUp', cb); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.WKT" │ │ │ │ │ + CLASS_NAME: "OpenLayers.Events" │ │ │ │ │ }); │ │ │ │ │ /* ====================================================================== │ │ │ │ │ - OpenLayers/Format/JSON.js │ │ │ │ │ + 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. */ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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 │ │ │ │ │ + * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ │ + * @requires OpenLayers/Util.js │ │ │ │ │ */ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Class: OpenLayers.Format.JSON │ │ │ │ │ - * A parser to read/write JSON safely. Create a new instance with the │ │ │ │ │ - * constructor. │ │ │ │ │ + * 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. │ │ │ │ │ * │ │ │ │ │ - * Inherits from: │ │ │ │ │ - * - │ │ │ │ │ + * 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.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", │ │ │ │ │ +OpenLayers.Projection = OpenLayers.Class({ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: level │ │ │ │ │ - * {Integer} For "pretty" printing, this is incremented/decremented during │ │ │ │ │ - * serialization. │ │ │ │ │ + * Property: proj │ │ │ │ │ + * {Object} Proj4js.Proj instance. │ │ │ │ │ */ │ │ │ │ │ - level: 0, │ │ │ │ │ + proj: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: pretty │ │ │ │ │ - * {Boolean} Serialize with extra whitespace for structure. This is set │ │ │ │ │ - * by the method. │ │ │ │ │ + * Property: projCode │ │ │ │ │ + * {String} │ │ │ │ │ */ │ │ │ │ │ - pretty: false, │ │ │ │ │ + projCode: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: nativeJSON │ │ │ │ │ - * {Boolean} Does the browser support native json? │ │ │ │ │ + * Property: titleRegEx │ │ │ │ │ + * {RegExp} regular expression to strip the title from a proj4js definition │ │ │ │ │ */ │ │ │ │ │ - nativeJSON: (function() { │ │ │ │ │ - return !!(window.JSON && typeof JSON.parse == "function" && typeof JSON.stringify == "function"); │ │ │ │ │ - })(), │ │ │ │ │ + titleRegEx: /\+title=[^\+]*/, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Constructor: OpenLayers.Format.JSON │ │ │ │ │ - * Create a new parser for JSON. │ │ │ │ │ + * Constructor: OpenLayers.Projection │ │ │ │ │ + * This class offers several methods for interacting with a wrapped │ │ │ │ │ + * pro4js projection object. │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ │ - * this instance. │ │ │ │ │ - */ │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * APIMethod: read │ │ │ │ │ - * Deserialize a json string. │ │ │ │ │ + * projCode - {String} A string identifying the Well Known Identifier for │ │ │ │ │ + * the projection. │ │ │ │ │ + * options - {Object} An optional object to set additional properties │ │ │ │ │ + * on the projection. │ │ │ │ │ * │ │ │ │ │ - * 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 . │ │ │ │ │ + * {} A projection object. │ │ │ │ │ */ │ │ │ │ │ - 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; │ │ │ │ │ + initialize: function(projCode, options) { │ │ │ │ │ + OpenLayers.Util.extend(this, options); │ │ │ │ │ + this.projCode = projCode; │ │ │ │ │ + if (typeof Proj4js == "object") { │ │ │ │ │ + this.proj = new Proj4js.Proj(projCode); │ │ │ │ │ } │ │ │ │ │ - │ │ │ │ │ - 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. │ │ │ │ │ + * APIMethod: getCode │ │ │ │ │ + * Get the string SRS code. │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {String} The JSON string representation of the input value. │ │ │ │ │ + * {String} The SRS code. │ │ │ │ │ */ │ │ │ │ │ - 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; │ │ │ │ │ + getCode: function() { │ │ │ │ │ + return this.proj ? this.proj.srsCode : this.projCode; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: writeIndent │ │ │ │ │ - * Output an indentation string depending on the indentation level. │ │ │ │ │ + * APIMethod: getUnits │ │ │ │ │ + * Get the units string for the projection -- returns null if │ │ │ │ │ + * proj4js is not available. │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {String} An appropriate indentation string. │ │ │ │ │ + * {String} The units abbreviation. │ │ │ │ │ */ │ │ │ │ │ - writeIndent: function() { │ │ │ │ │ - var pieces = []; │ │ │ │ │ - if (this.pretty) { │ │ │ │ │ - for (var i = 0; i < this.level; ++i) { │ │ │ │ │ - pieces.push(this.indent); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - return pieces.join(''); │ │ │ │ │ + getUnits: function() { │ │ │ │ │ + return this.proj ? this.proj.units : null; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: writeNewline │ │ │ │ │ - * Output a string representing a newline if in pretty printing mode. │ │ │ │ │ + * Method: toString │ │ │ │ │ + * Convert projection to string (getCode wrapper). │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {String} A string representing a new line. │ │ │ │ │ + * {String} The projection code. │ │ │ │ │ */ │ │ │ │ │ - writeNewline: function() { │ │ │ │ │ - return (this.pretty) ? this.newline : ''; │ │ │ │ │ + toString: function() { │ │ │ │ │ + return this.getCode(); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: writeSpace │ │ │ │ │ - * Output a string representing a space if in pretty printing mode. │ │ │ │ │ + * Method: equals │ │ │ │ │ + * Test equality of two projection instances. Determines equality based │ │ │ │ │ + * soley on the projection code. │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {String} A space. │ │ │ │ │ + * {Boolean} The two projections are equivalent. │ │ │ │ │ */ │ │ │ │ │ - writeSpace: function() { │ │ │ │ │ - return (this.pretty) ? this.space : ''; │ │ │ │ │ + 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; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: serialize │ │ │ │ │ - * Object with properties corresponding to the serializable data types. │ │ │ │ │ - * Property values are functions that do the actual serializing. │ │ │ │ │ + /* Method: destroy │ │ │ │ │ + * Destroy projection object. │ │ │ │ │ */ │ │ │ │ │ - serialize: { │ │ │ │ │ - /** │ │ │ │ │ - * Method: serialize.object │ │ │ │ │ - * Transform an object into a JSON string. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * object - {Object} The object to be serialized. │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {String} A JSON string representing the object. │ │ │ │ │ - */ │ │ │ │ │ - 'object': function(object) { │ │ │ │ │ - // three special objects that we want to treat differently │ │ │ │ │ - if (object == null) { │ │ │ │ │ - return "null"; │ │ │ │ │ - } │ │ │ │ │ - if (object.constructor == Date) { │ │ │ │ │ - return this.serialize.date.apply(this, [object]); │ │ │ │ │ - } │ │ │ │ │ - if (object.constructor == Array) { │ │ │ │ │ - return this.serialize.array.apply(this, [object]); │ │ │ │ │ - } │ │ │ │ │ - var pieces = ['{']; │ │ │ │ │ - this.level += 1; │ │ │ │ │ - var key, keyJSON, valueJSON; │ │ │ │ │ + destroy: function() { │ │ │ │ │ + delete this.proj; │ │ │ │ │ + delete this.projCode; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - var addComma = false; │ │ │ │ │ - for (key in object) { │ │ │ │ │ - if (object.hasOwnProperty(key)) { │ │ │ │ │ - // recursive calls need to allow for sub-classing │ │ │ │ │ - keyJSON = OpenLayers.Format.JSON.prototype.write.apply(this, │ │ │ │ │ - [key, this.pretty]); │ │ │ │ │ - valueJSON = OpenLayers.Format.JSON.prototype.write.apply(this, │ │ │ │ │ - [object[key], this.pretty]); │ │ │ │ │ - if (keyJSON != null && valueJSON != null) { │ │ │ │ │ - if (addComma) { │ │ │ │ │ - pieces.push(','); │ │ │ │ │ - } │ │ │ │ │ - pieces.push(this.writeNewline(), this.writeIndent(), │ │ │ │ │ - keyJSON, ':', this.writeSpace(), valueJSON); │ │ │ │ │ - addComma = true; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ + CLASS_NAME: "OpenLayers.Projection" │ │ │ │ │ +}); │ │ │ │ │ │ │ │ │ │ - this.level -= 1; │ │ │ │ │ - pieces.push(this.writeNewline(), this.writeIndent(), '}'); │ │ │ │ │ - return pieces.join(''); │ │ │ │ │ - }, │ │ │ │ │ +/** │ │ │ │ │ + * 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 = {}; │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: serialize.array │ │ │ │ │ - * Transform an array into a JSON string. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * array - {Array} The array to be serialized │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {String} A JSON string representing the array. │ │ │ │ │ - */ │ │ │ │ │ - 'array': function(array) { │ │ │ │ │ - var json; │ │ │ │ │ - var pieces = ['[']; │ │ │ │ │ - this.level += 1; │ │ │ │ │ +/** │ │ │ │ │ + * 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] │ │ │ │ │ + } │ │ │ │ │ +}; │ │ │ │ │ │ │ │ │ │ - for (var i = 0, len = array.length; i < len; ++i) { │ │ │ │ │ - // recursive calls need to allow for sub-classing │ │ │ │ │ - json = OpenLayers.Format.JSON.prototype.write.apply(this, │ │ │ │ │ - [array[i], this.pretty]); │ │ │ │ │ - if (json != null) { │ │ │ │ │ - if (i > 0) { │ │ │ │ │ - pieces.push(','); │ │ │ │ │ - } │ │ │ │ │ - pieces.push(this.writeNewline(), this.writeIndent(), json); │ │ │ │ │ - } │ │ │ │ │ +/** │ │ │ │ │ + * 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; │ │ │ │ │ +}; │ │ │ │ │ │ │ │ │ │ - this.level -= 1; │ │ │ │ │ - pieces.push(this.writeNewline(), this.writeIndent(), ']'); │ │ │ │ │ - return pieces.join(''); │ │ │ │ │ - }, │ │ │ │ │ +/** │ │ │ │ │ + * 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; │ │ │ │ │ +}; │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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 + '"'; │ │ │ │ │ - }, │ │ │ │ │ +/** │ │ │ │ │ + * 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() { │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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"; │ │ │ │ │ - }, │ │ │ │ │ + var pole = 20037508.34; │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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); │ │ │ │ │ - }, │ │ │ │ │ + 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; │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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; │ │ │ │ │ + function forwardMercator(xy) { │ │ │ │ │ + xy.x = xy.x * pole / 180; │ │ │ │ │ + var y = Math.log(Math.tan((90 + xy.y) * Math.PI / 360)) / Math.PI * pole; │ │ │ │ │ + xy.y = Math.max(-20037508.34, Math.min(y, 20037508.34)); │ │ │ │ │ + return xy; │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + function map(base, codes) { │ │ │ │ │ + var add = OpenLayers.Projection.addTransform; │ │ │ │ │ + var same = OpenLayers.Projection.nullTransform; │ │ │ │ │ + var i, len, code, other, j; │ │ │ │ │ + for (i = 0, len = codes.length; i < len; ++i) { │ │ │ │ │ + code = codes[i]; │ │ │ │ │ + add(base, code, forwardMercator); │ │ │ │ │ + add(code, base, inverseMercator); │ │ │ │ │ + for (j = i + 1; j < len; ++j) { │ │ │ │ │ + other = codes[j]; │ │ │ │ │ + add(code, other, same); │ │ │ │ │ + add(other, code, same); │ │ │ │ │ } │ │ │ │ │ - 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" │ │ │ │ │ + // list of equivalent codes for web mercator │ │ │ │ │ + var mercator = ["EPSG:900913", "EPSG:3857", "EPSG:102113", "EPSG:102100"], │ │ │ │ │ + geographic = ["CRS:84", "urn:ogc:def:crs:EPSG:6.6:4326", "EPSG:4326"], │ │ │ │ │ + i; │ │ │ │ │ + for (i = mercator.length - 1; i >= 0; --i) { │ │ │ │ │ + map(mercator[i], geographic); │ │ │ │ │ + } │ │ │ │ │ + for (i = geographic.length - 1; i >= 0; --i) { │ │ │ │ │ + map(geographic[i], mercator); │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ -}); │ │ │ │ │ +})(); │ │ │ │ │ /* ====================================================================== │ │ │ │ │ - OpenLayers/Format/GeoJSON.js │ │ │ │ │ + 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/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 │ │ │ │ │ + * @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.Format.GeoJSON │ │ │ │ │ - * Read and write GeoJSON. Create a new parser with the │ │ │ │ │ - * constructor. │ │ │ │ │ - * │ │ │ │ │ - * Inherits from: │ │ │ │ │ - * - │ │ │ │ │ + * 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.Format.GeoJSON = OpenLayers.Class(OpenLayers.Format.JSON, { │ │ │ │ │ +OpenLayers.Map = OpenLayers.Class({ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: ignoreExtraDims │ │ │ │ │ - * {Boolean} Ignore dimensions higher than 2 when reading geometry │ │ │ │ │ - * coordinates. │ │ │ │ │ + * Constant: Z_INDEX_BASE │ │ │ │ │ + * {Object} Base z-indexes for different classes of thing │ │ │ │ │ */ │ │ │ │ │ - ignoreExtraDims: false, │ │ │ │ │ + Z_INDEX_BASE: { │ │ │ │ │ + BaseLayer: 100, │ │ │ │ │ + Overlay: 325, │ │ │ │ │ + Feature: 725, │ │ │ │ │ + Popup: 750, │ │ │ │ │ + Control: 1000 │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Constructor: OpenLayers.Format.GeoJSON │ │ │ │ │ - * Create a new parser for GeoJSON. │ │ │ │ │ + * APIProperty: events │ │ │ │ │ + * {} │ │ │ │ │ * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ │ - * this instance. │ │ │ │ │ - */ │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * APIMethod: read │ │ │ │ │ - * Deserialize a GeoJSON string. │ │ │ │ │ + * Register a listener for a particular event with the following syntax: │ │ │ │ │ + * (code) │ │ │ │ │ + * map.events.register(type, obj, listener); │ │ │ │ │ + * (end) │ │ │ │ │ * │ │ │ │ │ - * 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. │ │ │ │ │ + * Listeners will be called with a reference to an event object. The │ │ │ │ │ + * properties of this event depends on exactly what happened. │ │ │ │ │ * │ │ │ │ │ - * 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 │ │ │ │ │ - * . │ │ │ │ │ + * 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 │ │ │ │ │ */ │ │ │ │ │ - read: function(json, type, filter) { │ │ │ │ │ - type = (type) ? type : "FeatureCollection"; │ │ │ │ │ - var results = null; │ │ │ │ │ - var obj = null; │ │ │ │ │ - if (typeof json == "string") { │ │ │ │ │ - obj = OpenLayers.Format.JSON.prototype.read.apply(this, │ │ │ │ │ - [json, filter]); │ │ │ │ │ - } else { │ │ │ │ │ - obj = json; │ │ │ │ │ - } │ │ │ │ │ - if (!obj) { │ │ │ │ │ - OpenLayers.Console.error("Bad JSON: " + json); │ │ │ │ │ - } else if (typeof(obj.type) != "string") { │ │ │ │ │ - OpenLayers.Console.error("Bad GeoJSON - no type: " + json); │ │ │ │ │ - } else if (this.isValidType(obj, type)) { │ │ │ │ │ - switch (type) { │ │ │ │ │ - case "Geometry": │ │ │ │ │ - try { │ │ │ │ │ - results = this.parseGeometry(obj); │ │ │ │ │ - } catch (err) { │ │ │ │ │ - OpenLayers.Console.error(err); │ │ │ │ │ - } │ │ │ │ │ - break; │ │ │ │ │ - case "Feature": │ │ │ │ │ - try { │ │ │ │ │ - results = this.parseFeature(obj); │ │ │ │ │ - results.type = "Feature"; │ │ │ │ │ - } catch (err) { │ │ │ │ │ - OpenLayers.Console.error(err); │ │ │ │ │ - } │ │ │ │ │ - break; │ │ │ │ │ - case "FeatureCollection": │ │ │ │ │ - // for type FeatureCollection, we allow input to be any type │ │ │ │ │ - results = []; │ │ │ │ │ - switch (obj.type) { │ │ │ │ │ - case "Feature": │ │ │ │ │ - try { │ │ │ │ │ - results.push(this.parseFeature(obj)); │ │ │ │ │ - } catch (err) { │ │ │ │ │ - results = null; │ │ │ │ │ - OpenLayers.Console.error(err); │ │ │ │ │ - } │ │ │ │ │ - break; │ │ │ │ │ - case "FeatureCollection": │ │ │ │ │ - for (var i = 0, len = obj.features.length; i < len; ++i) { │ │ │ │ │ - try { │ │ │ │ │ - results.push(this.parseFeature(obj.features[i])); │ │ │ │ │ - } catch (err) { │ │ │ │ │ - results = null; │ │ │ │ │ - OpenLayers.Console.error(err); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - break; │ │ │ │ │ - default: │ │ │ │ │ - try { │ │ │ │ │ - var geom = this.parseGeometry(obj); │ │ │ │ │ - results.push(new OpenLayers.Feature.Vector(geom)); │ │ │ │ │ - } catch (err) { │ │ │ │ │ - results = null; │ │ │ │ │ - OpenLayers.Console.error(err); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - break; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - return results; │ │ │ │ │ - }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: isValidType │ │ │ │ │ - * Check if a GeoJSON object is a valid representative of the given type. │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Boolean} The object is valid GeoJSON object of the given type. │ │ │ │ │ + * Property: id │ │ │ │ │ + * {String} Unique identifier for the map │ │ │ │ │ */ │ │ │ │ │ - isValidType: function(obj, type) { │ │ │ │ │ - var valid = false; │ │ │ │ │ - switch (type) { │ │ │ │ │ - case "Geometry": │ │ │ │ │ - if (OpenLayers.Util.indexOf( │ │ │ │ │ - ["Point", "MultiPoint", "LineString", "MultiLineString", │ │ │ │ │ - "Polygon", "MultiPolygon", "Box", "GeometryCollection" │ │ │ │ │ - ], │ │ │ │ │ - obj.type) == -1) { │ │ │ │ │ - // unsupported geometry type │ │ │ │ │ - OpenLayers.Console.error("Unsupported geometry type: " + │ │ │ │ │ - obj.type); │ │ │ │ │ - } else { │ │ │ │ │ - valid = true; │ │ │ │ │ - } │ │ │ │ │ - break; │ │ │ │ │ - case "FeatureCollection": │ │ │ │ │ - // allow for any type to be converted to a feature collection │ │ │ │ │ - valid = true; │ │ │ │ │ - break; │ │ │ │ │ - default: │ │ │ │ │ - // for Feature types must match │ │ │ │ │ - if (obj.type == type) { │ │ │ │ │ - valid = true; │ │ │ │ │ - } else { │ │ │ │ │ - OpenLayers.Console.error("Cannot convert types from " + │ │ │ │ │ - obj.type + " to " + type); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - return valid; │ │ │ │ │ - }, │ │ │ │ │ + id: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: parseFeature │ │ │ │ │ - * Convert a feature object from GeoJSON into an │ │ │ │ │ - * . │ │ │ │ │ + * 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. │ │ │ │ │ * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * obj - {Object} An object created from a GeoJSON object │ │ │ │ │ + * 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. │ │ │ │ │ * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {} A feature. │ │ │ │ │ + * If you are using fractionalZoom, then you should also use │ │ │ │ │ + * instead of layer.resolutions[zoom] as the │ │ │ │ │ + * former works for non-integer zoom levels. │ │ │ │ │ */ │ │ │ │ │ - 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; │ │ │ │ │ - }, │ │ │ │ │ + fractionalZoom: false, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: parseGeometry │ │ │ │ │ - * Convert a geometry object from GeoJSON into an . │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * obj - {Object} An object created from a GeoJSON object │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {} A geometry. │ │ │ │ │ + * APIProperty: events │ │ │ │ │ + * {} An events object that handles all │ │ │ │ │ + * events on the map │ │ │ │ │ */ │ │ │ │ │ - parseGeometry: function(obj) { │ │ │ │ │ - if (obj == null) { │ │ │ │ │ - return null; │ │ │ │ │ - } │ │ │ │ │ - var geometry, collection = false; │ │ │ │ │ - if (obj.type == "GeometryCollection") { │ │ │ │ │ - if (!(OpenLayers.Util.isArray(obj.geometries))) { │ │ │ │ │ - throw "GeometryCollection must have geometries array: " + obj; │ │ │ │ │ - } │ │ │ │ │ - var numGeom = obj.geometries.length; │ │ │ │ │ - var components = new Array(numGeom); │ │ │ │ │ - for (var i = 0; i < numGeom; ++i) { │ │ │ │ │ - components[i] = this.parseGeometry.apply( │ │ │ │ │ - this, [obj.geometries[i]] │ │ │ │ │ - ); │ │ │ │ │ - } │ │ │ │ │ - geometry = new OpenLayers.Geometry.Collection(components); │ │ │ │ │ - collection = true; │ │ │ │ │ - } else { │ │ │ │ │ - if (!(OpenLayers.Util.isArray(obj.coordinates))) { │ │ │ │ │ - throw "Geometry must have coordinates array: " + obj; │ │ │ │ │ - } │ │ │ │ │ - if (!this.parseCoords[obj.type.toLowerCase()]) { │ │ │ │ │ - throw "Unsupported geometry type: " + obj.type; │ │ │ │ │ - } │ │ │ │ │ - try { │ │ │ │ │ - geometry = this.parseCoords[obj.type.toLowerCase()].apply( │ │ │ │ │ - this, [obj.coordinates] │ │ │ │ │ - ); │ │ │ │ │ - } catch (err) { │ │ │ │ │ - // deal with bad coordinates │ │ │ │ │ - throw err; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - // We don't reproject collections because the children are reprojected │ │ │ │ │ - // for us when they are created. │ │ │ │ │ - if (this.internalProjection && this.externalProjection && !collection) { │ │ │ │ │ - geometry.transform(this.externalProjection, │ │ │ │ │ - this.internalProjection); │ │ │ │ │ - } │ │ │ │ │ - return geometry; │ │ │ │ │ - }, │ │ │ │ │ + events: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: parseCoords │ │ │ │ │ - * Object with properties corresponding to the GeoJSON geometry types. │ │ │ │ │ - * Property values are functions that do the actual parsing. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - parseCoords: { │ │ │ │ │ - /** │ │ │ │ │ - * Method: parseCoords.point │ │ │ │ │ - * Convert a coordinate array from GeoJSON into an │ │ │ │ │ - * . │ │ │ │ │ - * │ │ │ │ │ - * 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]); │ │ │ │ │ - }, │ │ │ │ │ + allOverlays: false, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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 < len; ++i) { │ │ │ │ │ - try { │ │ │ │ │ - p = this.parseCoords["point"].apply(this, [array[i]]); │ │ │ │ │ - } catch (err) { │ │ │ │ │ - throw err; │ │ │ │ │ - } │ │ │ │ │ - points.push(p); │ │ │ │ │ - } │ │ │ │ │ - return new OpenLayers.Geometry.MultiPoint(points); │ │ │ │ │ - }, │ │ │ │ │ + /** │ │ │ │ │ + * 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, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: parseCoords.linestring │ │ │ │ │ - * Convert a coordinate array from GeoJSON into an │ │ │ │ │ - * . │ │ │ │ │ - * │ │ │ │ │ - * 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 < len; ++i) { │ │ │ │ │ - try { │ │ │ │ │ - p = this.parseCoords["point"].apply(this, [array[i]]); │ │ │ │ │ - } catch (err) { │ │ │ │ │ - throw err; │ │ │ │ │ - } │ │ │ │ │ - points.push(p); │ │ │ │ │ - } │ │ │ │ │ - return new OpenLayers.Geometry.LineString(points); │ │ │ │ │ - }, │ │ │ │ │ + /** │ │ │ │ │ + * Property: dragging │ │ │ │ │ + * {Boolean} The map is currently being dragged. │ │ │ │ │ + */ │ │ │ │ │ + dragging: false, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: parseCoords.multilinestring │ │ │ │ │ - * Convert a coordinate array from GeoJSON into an │ │ │ │ │ - * . │ │ │ │ │ - * │ │ │ │ │ - * 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 < len; ++i) { │ │ │ │ │ - try { │ │ │ │ │ - l = this.parseCoords["linestring"].apply(this, [array[i]]); │ │ │ │ │ - } catch (err) { │ │ │ │ │ - throw err; │ │ │ │ │ - } │ │ │ │ │ - lines.push(l); │ │ │ │ │ - } │ │ │ │ │ - return new OpenLayers.Geometry.MultiLineString(lines); │ │ │ │ │ - }, │ │ │ │ │ + /** │ │ │ │ │ + * Property: size │ │ │ │ │ + * {} Size of the main div (this.div) │ │ │ │ │ + */ │ │ │ │ │ + size: null, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: parseCoords.polygon │ │ │ │ │ - * Convert a coordinate array from GeoJSON into an │ │ │ │ │ - * . │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {} A geometry. │ │ │ │ │ - */ │ │ │ │ │ - "polygon": function(array) { │ │ │ │ │ - var rings = []; │ │ │ │ │ - var r, l; │ │ │ │ │ - for (var i = 0, len = array.length; i < len; ++i) { │ │ │ │ │ - try { │ │ │ │ │ - l = this.parseCoords["linestring"].apply(this, [array[i]]); │ │ │ │ │ - } catch (err) { │ │ │ │ │ - throw err; │ │ │ │ │ - } │ │ │ │ │ - r = new OpenLayers.Geometry.LinearRing(l.components); │ │ │ │ │ - rings.push(r); │ │ │ │ │ - } │ │ │ │ │ - return new OpenLayers.Geometry.Polygon(rings); │ │ │ │ │ - }, │ │ │ │ │ + /** │ │ │ │ │ + * Property: viewPortDiv │ │ │ │ │ + * {HTMLDivElement} The element that represents the map viewport │ │ │ │ │ + */ │ │ │ │ │ + viewPortDiv: null, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: parseCoords.multipolygon │ │ │ │ │ - * Convert a coordinate array from GeoJSON into an │ │ │ │ │ - * . │ │ │ │ │ - * │ │ │ │ │ - * 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 < len; ++i) { │ │ │ │ │ - try { │ │ │ │ │ - p = this.parseCoords["polygon"].apply(this, [array[i]]); │ │ │ │ │ - } catch (err) { │ │ │ │ │ - throw err; │ │ │ │ │ - } │ │ │ │ │ - polys.push(p); │ │ │ │ │ - } │ │ │ │ │ - return new OpenLayers.Geometry.MultiPolygon(polys); │ │ │ │ │ - }, │ │ │ │ │ + /** │ │ │ │ │ + * Property: layerContainerOrigin │ │ │ │ │ + * {} The lonlat at which the later container was │ │ │ │ │ + * re-initialized (on-zoom) │ │ │ │ │ + */ │ │ │ │ │ + layerContainerOrigin: null, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: parseCoords.box │ │ │ │ │ - * Convert a coordinate array from GeoJSON into an │ │ │ │ │ - * . │ │ │ │ │ - * │ │ │ │ │ - * 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]) │ │ │ │ │ - ]) │ │ │ │ │ - ]); │ │ │ │ │ - } │ │ │ │ │ + /** │ │ │ │ │ + * Property: layerContainerDiv │ │ │ │ │ + * {HTMLDivElement} The element that contains the layers. │ │ │ │ │ + */ │ │ │ │ │ + layerContainerDiv: null, │ │ │ │ │ │ │ │ │ │ - }, │ │ │ │ │ + /** │ │ │ │ │ + * APIProperty: layers │ │ │ │ │ + * {Array()} Ordered list of layers in the map │ │ │ │ │ + */ │ │ │ │ │ + layers: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * APIProperty: controls │ │ │ │ │ + * {Array()} List of controls associated with the map. │ │ │ │ │ * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {String} The GeoJSON string representation of the input geometry, │ │ │ │ │ - * features, or array of features. │ │ │ │ │ + * 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 │ │ │ │ │ + * - │ │ │ │ │ + * - │ │ │ │ │ */ │ │ │ │ │ - write: function(obj, pretty) { │ │ │ │ │ - var geojson = { │ │ │ │ │ - "type": null │ │ │ │ │ - }; │ │ │ │ │ - if (OpenLayers.Util.isArray(obj)) { │ │ │ │ │ - geojson.type = "FeatureCollection"; │ │ │ │ │ - var numFeatures = obj.length; │ │ │ │ │ - geojson.features = new Array(numFeatures); │ │ │ │ │ - for (var i = 0; i < numFeatures; ++i) { │ │ │ │ │ - var element = obj[i]; │ │ │ │ │ - if (!element instanceof OpenLayers.Feature.Vector) { │ │ │ │ │ - var msg = "FeatureCollection only supports collections " + │ │ │ │ │ - "of features: " + element; │ │ │ │ │ - throw msg; │ │ │ │ │ - } │ │ │ │ │ - geojson.features[i] = this.extract.feature.apply( │ │ │ │ │ - this, [element] │ │ │ │ │ - ); │ │ │ │ │ - } │ │ │ │ │ - } else if (obj.CLASS_NAME.indexOf("OpenLayers.Geometry") == 0) { │ │ │ │ │ - geojson = this.extract.geometry.apply(this, [obj]); │ │ │ │ │ - } else if (obj instanceof OpenLayers.Feature.Vector) { │ │ │ │ │ - geojson = this.extract.feature.apply(this, [obj]); │ │ │ │ │ - if (obj.layer && obj.layer.projection) { │ │ │ │ │ - geojson.crs = this.createCRSObject(obj); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - return OpenLayers.Format.JSON.prototype.write.apply(this, │ │ │ │ │ - [geojson, pretty]); │ │ │ │ │ - }, │ │ │ │ │ + controls: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: createCRSObject │ │ │ │ │ - * Create the CRS object for an object. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * object - {} │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Object} An object which can be assigned to the crs property │ │ │ │ │ - * of a GeoJSON object. │ │ │ │ │ + * Property: popups │ │ │ │ │ + * {Array()} List of popups associated with the map │ │ │ │ │ */ │ │ │ │ │ - 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; │ │ │ │ │ - }, │ │ │ │ │ + popups: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: extract │ │ │ │ │ - * Object with properties corresponding to the GeoJSON types. │ │ │ │ │ - * Property values are functions that do the actual value extraction. │ │ │ │ │ + * APIProperty: baseLayer │ │ │ │ │ + * {} The currently selected base layer. This determines │ │ │ │ │ + * min/max zoom level, projection, etc. │ │ │ │ │ */ │ │ │ │ │ - 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; │ │ │ │ │ - }, │ │ │ │ │ + baseLayer: null, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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 │ │ │ │ │ - }; │ │ │ │ │ - } │ │ │ │ │ + /** │ │ │ │ │ + * Property: center │ │ │ │ │ + * {} The current center of the map │ │ │ │ │ + */ │ │ │ │ │ + center: null, │ │ │ │ │ │ │ │ │ │ - return json; │ │ │ │ │ - }, │ │ │ │ │ + /** │ │ │ │ │ + * Property: resolution │ │ │ │ │ + * {Float} The resolution of the map. │ │ │ │ │ + */ │ │ │ │ │ + resolution: null, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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]; │ │ │ │ │ - }, │ │ │ │ │ + /** │ │ │ │ │ + * Property: zoom │ │ │ │ │ + * {Integer} The current zoom level of the map │ │ │ │ │ + */ │ │ │ │ │ + zoom: 0, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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 < len; ++i) { │ │ │ │ │ - array.push(this.extract.point.apply(this, [multipoint.components[i]])); │ │ │ │ │ - } │ │ │ │ │ - return array; │ │ │ │ │ - }, │ │ │ │ │ + /** │ │ │ │ │ + * Property: panRatio │ │ │ │ │ + * {Float} The ratio of the current extent within │ │ │ │ │ + * which panning will tween. │ │ │ │ │ + */ │ │ │ │ │ + panRatio: 1.5, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: extract.linestring │ │ │ │ │ - * Return an array of coordinate arrays from a linestring. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * linestring - {} │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Array} An array of coordinate arrays representing │ │ │ │ │ - * the linestring. │ │ │ │ │ - */ │ │ │ │ │ - 'linestring': function(linestring) { │ │ │ │ │ - var array = []; │ │ │ │ │ - for (var i = 0, len = linestring.components.length; i < len; ++i) { │ │ │ │ │ - array.push(this.extract.point.apply(this, [linestring.components[i]])); │ │ │ │ │ - } │ │ │ │ │ - return array; │ │ │ │ │ - }, │ │ │ │ │ + /** │ │ │ │ │ + * APIProperty: options │ │ │ │ │ + * {Object} The options object passed to the class constructor. Read-only. │ │ │ │ │ + */ │ │ │ │ │ + options: null, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: extract.multilinestring │ │ │ │ │ - * Return an array of linestring arrays from a linestring. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * multilinestring - {} │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Array} An array of linestring arrays representing │ │ │ │ │ - * the multilinestring. │ │ │ │ │ - */ │ │ │ │ │ - 'multilinestring': function(multilinestring) { │ │ │ │ │ - var array = []; │ │ │ │ │ - for (var i = 0, len = multilinestring.components.length; i < len; ++i) { │ │ │ │ │ - array.push(this.extract.linestring.apply(this, [multilinestring.components[i]])); │ │ │ │ │ - } │ │ │ │ │ - return array; │ │ │ │ │ - }, │ │ │ │ │ + // Options │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: extract.polygon │ │ │ │ │ - * Return an array of linear ring arrays from a polygon. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * polygon - {} │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Array} An array of linear ring arrays representing the polygon. │ │ │ │ │ - */ │ │ │ │ │ - 'polygon': function(polygon) { │ │ │ │ │ - var array = []; │ │ │ │ │ - for (var i = 0, len = polygon.components.length; i < len; ++i) { │ │ │ │ │ - array.push(this.extract.linestring.apply(this, [polygon.components[i]])); │ │ │ │ │ - } │ │ │ │ │ - return array; │ │ │ │ │ - }, │ │ │ │ │ + /** │ │ │ │ │ + * APIProperty: tileSize │ │ │ │ │ + * {} Set in the map options to override the default tile │ │ │ │ │ + * size for this map. │ │ │ │ │ + */ │ │ │ │ │ + tileSize: null, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: extract.multipolygon │ │ │ │ │ - * Return an array of polygon arrays from a multipolygon. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * multipolygon - {} │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Array} An array of polygon arrays representing │ │ │ │ │ - * the multipolygon │ │ │ │ │ - */ │ │ │ │ │ - 'multipolygon': function(multipolygon) { │ │ │ │ │ - var array = []; │ │ │ │ │ - for (var i = 0, len = multipolygon.components.length; i < len; ++i) { │ │ │ │ │ - array.push(this.extract.polygon.apply(this, [multipolygon.components[i]])); │ │ │ │ │ - } │ │ │ │ │ - return array; │ │ │ │ │ - }, │ │ │ │ │ + /** │ │ │ │ │ + * 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", │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: extract.collection │ │ │ │ │ - * Return an array of geometries from a geometry collection. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * collection - {} │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Array} An array of geometry objects representing the geometry │ │ │ │ │ - * collection. │ │ │ │ │ - */ │ │ │ │ │ - 'collection': function(collection) { │ │ │ │ │ - var len = collection.components.length; │ │ │ │ │ - var array = new Array(len); │ │ │ │ │ - for (var i = 0; i < len; ++i) { │ │ │ │ │ - array[i] = this.extract.geometry.apply( │ │ │ │ │ - this, [collection.components[i]] │ │ │ │ │ - ); │ │ │ │ │ - } │ │ │ │ │ - return array; │ │ │ │ │ - } │ │ │ │ │ + /** │ │ │ │ │ + * 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, │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.GeoJSON" │ │ │ │ │ + /** │ │ │ │ │ + * APIProperty: minResolution │ │ │ │ │ + * {Float} │ │ │ │ │ + */ │ │ │ │ │ + minResolution: null, │ │ │ │ │ │ │ │ │ │ -}); │ │ │ │ │ -/* ====================================================================== │ │ │ │ │ - OpenLayers/Format/XML.js │ │ │ │ │ - ====================================================================== */ │ │ │ │ │ + /** │ │ │ │ │ + * APIProperty: maxScale │ │ │ │ │ + * {Float} │ │ │ │ │ + */ │ │ │ │ │ + maxScale: null, │ │ │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ - * full text of the license. */ │ │ │ │ │ + /** │ │ │ │ │ + * APIProperty: minScale │ │ │ │ │ + * {Float} │ │ │ │ │ + */ │ │ │ │ │ + minScale: null, │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * @requires OpenLayers/Format.js │ │ │ │ │ - */ │ │ │ │ │ + /** │ │ │ │ │ + * 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, │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * 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, { │ │ │ │ │ + /** │ │ │ │ │ + * 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, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - namespaces: null, │ │ │ │ │ + restrictedExtent: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: namespaceAlias │ │ │ │ │ - * {Object} Mapping of namespace URI to namespace alias. This object │ │ │ │ │ - * is read-only. Use to add or set a namespace alias. │ │ │ │ │ + * APIProperty: numZoomLevels │ │ │ │ │ + * {Integer} Number of zoom levels for the map. Defaults to 16. Set a │ │ │ │ │ + * different value in the map options if needed. │ │ │ │ │ */ │ │ │ │ │ - namespaceAlias: null, │ │ │ │ │ + numZoomLevels: 16, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: defaultPrefix │ │ │ │ │ - * {String} The default namespace alias for creating element nodes. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - defaultPrefix: null, │ │ │ │ │ + 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, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * 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 {}. │ │ │ │ │ */ │ │ │ │ │ - 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. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - writers: {}, │ │ │ │ │ + fallThrough: false, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * APIProperty: autoUpdateSize │ │ │ │ │ + * {Boolean} Should OpenLayers automatically update the size of the map │ │ │ │ │ + * when the resize event is fired. Default is true. │ │ │ │ │ */ │ │ │ │ │ - xmldom: null, │ │ │ │ │ + autoUpdateSize: true, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - 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; │ │ │ │ │ - } │ │ │ │ │ - }, │ │ │ │ │ + eventListeners: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: destroy │ │ │ │ │ - * Clean up. │ │ │ │ │ + * Property: panTween │ │ │ │ │ + * {} Animated panning tween object, see panTo() │ │ │ │ │ */ │ │ │ │ │ - destroy: function() { │ │ │ │ │ - this.xmldom = null; │ │ │ │ │ - OpenLayers.Format.prototype.destroy.apply(this, arguments); │ │ │ │ │ - }, │ │ │ │ │ + panTween: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: setNamespace │ │ │ │ │ - * Set a namespace alias and URI for the format. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * alias - {String} The namespace alias (prefix). │ │ │ │ │ - * uri - {String} The namespace URI. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - setNamespace: function(alias, uri) { │ │ │ │ │ - this.namespaces[alias] = uri; │ │ │ │ │ - this.namespaceAlias[uri] = alias; │ │ │ │ │ - }, │ │ │ │ │ + panMethod: OpenLayers.Easing.Expo.easeOut, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: read │ │ │ │ │ - * Deserialize a XML string and return a DOM node. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * text - {String} A XML string │ │ │ │ │ - │ │ │ │ │ - * Returns: │ │ │ │ │ - * {DOMElement} A DOM node │ │ │ │ │ + * Property: panDuration │ │ │ │ │ + * {Integer} The number of steps to be passed to the │ │ │ │ │ + * OpenLayers.Tween.start() method when the map is │ │ │ │ │ + * panned. │ │ │ │ │ + * Default is 50. │ │ │ │ │ */ │ │ │ │ │ - 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; │ │ │ │ │ + panDuration: 50, │ │ │ │ │ │ │ │ │ │ - } │ │ │ │ │ - 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; │ │ │ │ │ - } │ │ │ │ │ - ); │ │ │ │ │ + /** │ │ │ │ │ + * Property: zoomTween │ │ │ │ │ + * {} Animated zooming tween object, see zoomTo() │ │ │ │ │ + */ │ │ │ │ │ + zoomTween: null, │ │ │ │ │ │ │ │ │ │ - if (this.keepData) { │ │ │ │ │ - this.data = node; │ │ │ │ │ - } │ │ │ │ │ + /** │ │ │ │ │ + * 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, │ │ │ │ │ │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ + /** │ │ │ │ │ + * 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, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * Property: paddingForPopups │ │ │ │ │ + * {} Outside margin of the popup. Used to prevent │ │ │ │ │ + * the popup from getting too close to the map border. │ │ │ │ │ */ │ │ │ │ │ - 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; │ │ │ │ │ - }, │ │ │ │ │ + paddingForPopups: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * Property: layerContainerOriginPx │ │ │ │ │ + * {Object} Cached object representing the layer container origin (in pixels). │ │ │ │ │ */ │ │ │ │ │ - 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; │ │ │ │ │ - }, │ │ │ │ │ + layerContainerOriginPx: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - createDocumentFragment: function() { │ │ │ │ │ - var element; │ │ │ │ │ - if (this.xmldom) { │ │ │ │ │ - element = this.xmldom.createDocumentFragment(); │ │ │ │ │ - } else { │ │ │ │ │ - element = document.createDocumentFragment(); │ │ │ │ │ - } │ │ │ │ │ - return element; │ │ │ │ │ - }, │ │ │ │ │ + minPx: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - 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; │ │ │ │ │ - }, │ │ │ │ │ + maxPx: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ - * │ │ │ │ │ + * 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: │ │ │ │ │ - * node - {Element} Node on which to search for other nodes. │ │ │ │ │ - * uri - {String} Namespace URI. │ │ │ │ │ - * name - {String} Local name of the tag (without the prefix). │ │ │ │ │ + * 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. │ │ │ │ │ * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {NodeList} A node list or array of elements. │ │ │ │ │ + * 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) │ │ │ │ │ */ │ │ │ │ │ - getElementsByTagNameNS: function(node, uri, name) { │ │ │ │ │ - var elements = []; │ │ │ │ │ - if (node.getElementsByTagNameNS) { │ │ │ │ │ - elements = node.getElementsByTagNameNS(uri, name); │ │ │ │ │ - } else { │ │ │ │ │ - // brute force method │ │ │ │ │ - var allNodes = node.getElementsByTagName("*"); │ │ │ │ │ - var potentialNode, fullName; │ │ │ │ │ - for (var i = 0, len = allNodes.length; i < len; ++i) { │ │ │ │ │ - potentialNode = allNodes[i]; │ │ │ │ │ - fullName = (potentialNode.prefix) ? │ │ │ │ │ - (potentialNode.prefix + ":" + name) : name; │ │ │ │ │ - if ((name == "*") || (fullName == potentialNode.nodeName)) { │ │ │ │ │ - if ((uri == "*") || (uri == potentialNode.namespaceURI)) { │ │ │ │ │ - elements.push(potentialNode); │ │ │ │ │ - } │ │ │ │ │ + initialize: function(div, options) { │ │ │ │ │ + │ │ │ │ │ + // If only one argument is provided, check if it is an object. │ │ │ │ │ + if (arguments.length === 1 && typeof div === "object") { │ │ │ │ │ + options = div; │ │ │ │ │ + div = options && options.div; │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + // Simple-type defaults are set in class definition. │ │ │ │ │ + // Now set complex-type defaults │ │ │ │ │ + this.tileSize = new OpenLayers.Size(OpenLayers.Map.TILE_WIDTH, │ │ │ │ │ + OpenLayers.Map.TILE_HEIGHT); │ │ │ │ │ + │ │ │ │ │ + this.paddingForPopups = new OpenLayers.Bounds(15, 15, 15, 15); │ │ │ │ │ + │ │ │ │ │ + this.theme = OpenLayers._getScriptLocation() + │ │ │ │ │ + 'theme/default/style.css'; │ │ │ │ │ + │ │ │ │ │ + // backup original options │ │ │ │ │ + this.options = OpenLayers.Util.extend({}, options); │ │ │ │ │ + │ │ │ │ │ + // now override default options │ │ │ │ │ + OpenLayers.Util.extend(this, options); │ │ │ │ │ + │ │ │ │ │ + var projCode = this.projection instanceof OpenLayers.Projection ? │ │ │ │ │ + this.projection.projCode : this.projection; │ │ │ │ │ + OpenLayers.Util.applyDefaults(this, OpenLayers.Projection.defaults[projCode]); │ │ │ │ │ + │ │ │ │ │ + // allow extents and center to be arrays │ │ │ │ │ + if (this.maxExtent && !(this.maxExtent instanceof OpenLayers.Bounds)) { │ │ │ │ │ + this.maxExtent = new OpenLayers.Bounds(this.maxExtent); │ │ │ │ │ + } │ │ │ │ │ + if (this.minExtent && !(this.minExtent instanceof OpenLayers.Bounds)) { │ │ │ │ │ + this.minExtent = new OpenLayers.Bounds(this.minExtent); │ │ │ │ │ + } │ │ │ │ │ + if (this.restrictedExtent && !(this.restrictedExtent instanceof OpenLayers.Bounds)) { │ │ │ │ │ + this.restrictedExtent = new OpenLayers.Bounds(this.restrictedExtent); │ │ │ │ │ + } │ │ │ │ │ + if (this.center && !(this.center instanceof OpenLayers.LonLat)) { │ │ │ │ │ + this.center = new OpenLayers.LonLat(this.center); │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + // initialize layers array │ │ │ │ │ + this.layers = []; │ │ │ │ │ + │ │ │ │ │ + this.id = OpenLayers.Util.createUniqueID("OpenLayers.Map_"); │ │ │ │ │ + │ │ │ │ │ + this.div = OpenLayers.Util.getElement(div); │ │ │ │ │ + if (!this.div) { │ │ │ │ │ + this.div = document.createElement("div"); │ │ │ │ │ + this.div.style.height = "1px"; │ │ │ │ │ + this.div.style.width = "1px"; │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + OpenLayers.Element.addClass(this.div, 'olMap'); │ │ │ │ │ + │ │ │ │ │ + // the viewPortDiv is the outermost div we modify │ │ │ │ │ + var id = this.id + "_OpenLayers_ViewPort"; │ │ │ │ │ + this.viewPortDiv = OpenLayers.Util.createDiv(id, null, null, null, │ │ │ │ │ + "relative", null, │ │ │ │ │ + "hidden"); │ │ │ │ │ + this.viewPortDiv.style.width = "100%"; │ │ │ │ │ + this.viewPortDiv.style.height = "100%"; │ │ │ │ │ + this.viewPortDiv.className = "olMapViewport"; │ │ │ │ │ + this.div.appendChild(this.viewPortDiv); │ │ │ │ │ + │ │ │ │ │ + this.events = new OpenLayers.Events( │ │ │ │ │ + this, this.viewPortDiv, null, this.fallThrough, { │ │ │ │ │ + includeXY: true │ │ │ │ │ + } │ │ │ │ │ + ); │ │ │ │ │ + │ │ │ │ │ + if (OpenLayers.TileManager && this.tileManager !== null) { │ │ │ │ │ + if (!(this.tileManager instanceof OpenLayers.TileManager)) { │ │ │ │ │ + this.tileManager = new OpenLayers.TileManager(this.tileManager); │ │ │ │ │ + } │ │ │ │ │ + this.tileManager.addMap(this); │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + // the layerContainerDiv is the one that holds all the layers │ │ │ │ │ + id = this.id + "_OpenLayers_Container"; │ │ │ │ │ + this.layerContainerDiv = OpenLayers.Util.createDiv(id); │ │ │ │ │ + this.layerContainerDiv.style.zIndex = this.Z_INDEX_BASE['Popup'] - 1; │ │ │ │ │ + this.layerContainerOriginPx = { │ │ │ │ │ + x: 0, │ │ │ │ │ + y: 0 │ │ │ │ │ + }; │ │ │ │ │ + this.applyTransform(); │ │ │ │ │ + │ │ │ │ │ + this.viewPortDiv.appendChild(this.layerContainerDiv); │ │ │ │ │ + │ │ │ │ │ + this.updateSize(); │ │ │ │ │ + if (this.eventListeners instanceof Object) { │ │ │ │ │ + this.events.on(this.eventListeners); │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + if (this.autoUpdateSize === true) { │ │ │ │ │ + // updateSize on catching the window's resize │ │ │ │ │ + // Note that this is ok, as updateSize() does nothing if the │ │ │ │ │ + // map's size has not actually changed. │ │ │ │ │ + this.updateSizeDestroy = OpenLayers.Function.bind(this.updateSize, │ │ │ │ │ + this); │ │ │ │ │ + OpenLayers.Event.observe(window, 'resize', │ │ │ │ │ + this.updateSizeDestroy); │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + // only append link stylesheet if the theme property is set │ │ │ │ │ + if (this.theme) { │ │ │ │ │ + // check existing links for equivalent url │ │ │ │ │ + var addNode = true; │ │ │ │ │ + var nodes = document.getElementsByTagName('link'); │ │ │ │ │ + for (var i = 0, len = nodes.length; i < len; ++i) { │ │ │ │ │ + if (OpenLayers.Util.isEquivalentUrl(nodes.item(i).href, │ │ │ │ │ + this.theme)) { │ │ │ │ │ + addNode = false; │ │ │ │ │ + break; │ │ │ │ │ } │ │ │ │ │ } │ │ │ │ │ + // only add a new node if one with an equivalent url hasn't already │ │ │ │ │ + // been added │ │ │ │ │ + if (addNode) { │ │ │ │ │ + var cssNode = document.createElement('link'); │ │ │ │ │ + cssNode.setAttribute('rel', 'stylesheet'); │ │ │ │ │ + cssNode.setAttribute('type', 'text/css'); │ │ │ │ │ + cssNode.setAttribute('href', this.theme); │ │ │ │ │ + document.getElementsByTagName('head')[0].appendChild(cssNode); │ │ │ │ │ + } │ │ │ │ │ } │ │ │ │ │ - return elements; │ │ │ │ │ - }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIMethod: getAttributeNodeNS │ │ │ │ │ - * Get an attribute node given the namespace URI and local name. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * node - {Element} Node on which to search for attribute nodes. │ │ │ │ │ - * uri - {String} Namespace URI. │ │ │ │ │ - * name - {String} Local name of the attribute (without the prefix). │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {DOMElement} An attribute node or null if none found. │ │ │ │ │ - */ │ │ │ │ │ - getAttributeNodeNS: function(node, uri, name) { │ │ │ │ │ - var attributeNode = null; │ │ │ │ │ - if (node.getAttributeNodeNS) { │ │ │ │ │ - attributeNode = node.getAttributeNodeNS(uri, name); │ │ │ │ │ - } else { │ │ │ │ │ - var attributes = node.attributes; │ │ │ │ │ - var potentialNode, fullName; │ │ │ │ │ - for (var i = 0, len = attributes.length; i < len; ++i) { │ │ │ │ │ - potentialNode = attributes[i]; │ │ │ │ │ - if (potentialNode.namespaceURI == uri) { │ │ │ │ │ - fullName = (potentialNode.prefix) ? │ │ │ │ │ - (potentialNode.prefix + ":" + name) : name; │ │ │ │ │ - if (fullName == potentialNode.nodeName) { │ │ │ │ │ - attributeNode = potentialNode; │ │ │ │ │ - break; │ │ │ │ │ - } │ │ │ │ │ + if (this.controls == null) { // default controls │ │ │ │ │ + this.controls = []; │ │ │ │ │ + if (OpenLayers.Control != null) { // running full or lite? │ │ │ │ │ + // Navigation or TouchNavigation depending on what is in build │ │ │ │ │ + if (OpenLayers.Control.Navigation) { │ │ │ │ │ + this.controls.push(new OpenLayers.Control.Navigation()); │ │ │ │ │ + } else if (OpenLayers.Control.TouchNavigation) { │ │ │ │ │ + this.controls.push(new OpenLayers.Control.TouchNavigation()); │ │ │ │ │ + } │ │ │ │ │ + if (OpenLayers.Control.Zoom) { │ │ │ │ │ + this.controls.push(new OpenLayers.Control.Zoom()); │ │ │ │ │ + } else if (OpenLayers.Control.PanZoom) { │ │ │ │ │ + this.controls.push(new OpenLayers.Control.PanZoom()); │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + if (OpenLayers.Control.ArgParser) { │ │ │ │ │ + this.controls.push(new OpenLayers.Control.ArgParser()); │ │ │ │ │ + } │ │ │ │ │ + if (OpenLayers.Control.Attribution) { │ │ │ │ │ + this.controls.push(new OpenLayers.Control.Attribution()); │ │ │ │ │ } │ │ │ │ │ } │ │ │ │ │ } │ │ │ │ │ - return attributeNode; │ │ │ │ │ - }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIMethod: getAttributeNS │ │ │ │ │ - * Get an attribute value given the namespace URI and local name. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * node - {Element} Node on which to search for an attribute. │ │ │ │ │ - * uri - {String} Namespace URI. │ │ │ │ │ - * name - {String} Local name of the attribute (without the prefix). │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {String} An attribute value or and empty string if none found. │ │ │ │ │ - */ │ │ │ │ │ - getAttributeNS: function(node, uri, name) { │ │ │ │ │ - var attributeValue = ""; │ │ │ │ │ - if (node.getAttributeNS) { │ │ │ │ │ - attributeValue = node.getAttributeNS(uri, name) || ""; │ │ │ │ │ - } else { │ │ │ │ │ - var attributeNode = this.getAttributeNodeNS(node, uri, name); │ │ │ │ │ - if (attributeNode) { │ │ │ │ │ - attributeValue = attributeNode.nodeValue; │ │ │ │ │ + for (var i = 0, len = this.controls.length; i < len; i++) { │ │ │ │ │ + this.addControlToMap(this.controls[i]); │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + this.popups = []; │ │ │ │ │ + │ │ │ │ │ + this.unloadDestroy = OpenLayers.Function.bind(this.destroy, this); │ │ │ │ │ + │ │ │ │ │ + │ │ │ │ │ + // always call map.destroy() │ │ │ │ │ + OpenLayers.Event.observe(window, 'unload', this.unloadDestroy); │ │ │ │ │ + │ │ │ │ │ + // add any initial layers │ │ │ │ │ + if (options && options.layers) { │ │ │ │ │ + /** │ │ │ │ │ + * If you have set options.center, the map center property will be │ │ │ │ │ + * set at this point. However, since setCenter has not been called, │ │ │ │ │ + * addLayers gets confused. So we delete the map center in this │ │ │ │ │ + * case. Because the check below uses options.center, it will │ │ │ │ │ + * be properly set below. │ │ │ │ │ + */ │ │ │ │ │ + delete this.center; │ │ │ │ │ + delete this.zoom; │ │ │ │ │ + this.addLayers(options.layers); │ │ │ │ │ + // set center (and optionally zoom) │ │ │ │ │ + if (options.center && !this.getCenter()) { │ │ │ │ │ + // zoom can be undefined here │ │ │ │ │ + this.setCenter(options.center, options.zoom); │ │ │ │ │ } │ │ │ │ │ } │ │ │ │ │ - return attributeValue; │ │ │ │ │ + │ │ │ │ │ + if (this.panMethod) { │ │ │ │ │ + this.panTween = new OpenLayers.Tween(this.panMethod); │ │ │ │ │ + } │ │ │ │ │ + if (this.zoomMethod && this.applyTransform.transform) { │ │ │ │ │ + this.zoomTween = new OpenLayers.Tween(this.zoomMethod); │ │ │ │ │ + } │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIMethod: getChildValue │ │ │ │ │ - * Get the textual value of the node if it exists, or return an │ │ │ │ │ - * optional default string. Returns an empty string if no first child │ │ │ │ │ - * exists and no default value is supplied. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * node - {DOMElement} The element used to look for a first child value. │ │ │ │ │ - * def - {String} Optional string to return in the event that no │ │ │ │ │ - * first child value exists. │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: getViewport │ │ │ │ │ + * Get the DOMElement representing the view port. │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {String} The value of the first child of the given node. │ │ │ │ │ + * {DOMElement} │ │ │ │ │ */ │ │ │ │ │ - getChildValue: function(node, def) { │ │ │ │ │ - var value = def || ""; │ │ │ │ │ - if (node) { │ │ │ │ │ - for (var child = node.firstChild; child; child = child.nextSibling) { │ │ │ │ │ - switch (child.nodeType) { │ │ │ │ │ - case 3: // text node │ │ │ │ │ - case 4: // cdata section │ │ │ │ │ - value += child.nodeValue; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - return value; │ │ │ │ │ + getViewport: function() { │ │ │ │ │ + return this.viewPortDiv; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: isSimpleContent │ │ │ │ │ - * Test if the given node has only simple content (i.e. no child element │ │ │ │ │ - * nodes). │ │ │ │ │ - * │ │ │ │ │ + * APIMethod: render │ │ │ │ │ + * Render the map to a specified container. │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * node - {DOMElement} An element node. │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Boolean} The node has no child element nodes (nodes of type 1). │ │ │ │ │ + * div - {String|DOMElement} The container that the map should be rendered │ │ │ │ │ + * to. If different than the current container, the map viewport │ │ │ │ │ + * will be moved from the current to the new container. │ │ │ │ │ */ │ │ │ │ │ - isSimpleContent: function(node) { │ │ │ │ │ - var simple = true; │ │ │ │ │ - for (var child = node.firstChild; child; child = child.nextSibling) { │ │ │ │ │ - if (child.nodeType === 1) { │ │ │ │ │ - simple = false; │ │ │ │ │ - break; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - return simple; │ │ │ │ │ + render: function(div) { │ │ │ │ │ + this.div = OpenLayers.Util.getElement(div); │ │ │ │ │ + OpenLayers.Element.addClass(this.div, 'olMap'); │ │ │ │ │ + this.viewPortDiv.parentNode.removeChild(this.viewPortDiv); │ │ │ │ │ + this.div.appendChild(this.viewPortDiv); │ │ │ │ │ + this.updateSize(); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: contentType │ │ │ │ │ - * Determine the content type for a given node. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * node - {DOMElement} │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Integer} One of OpenLayers.Format.XML.CONTENT_TYPE.{EMPTY,SIMPLE,COMPLEX,MIXED} │ │ │ │ │ - * if the node has no, simple, complex, or mixed content. │ │ │ │ │ + * Method: unloadDestroy │ │ │ │ │ + * Function that is called to destroy the map on page unload. stored here │ │ │ │ │ + * so that if map is manually destroyed, we can unregister this. │ │ │ │ │ */ │ │ │ │ │ - contentType: function(node) { │ │ │ │ │ - var simple = false, │ │ │ │ │ - complex = false; │ │ │ │ │ + unloadDestroy: null, │ │ │ │ │ │ │ │ │ │ - var type = OpenLayers.Format.XML.CONTENT_TYPE.EMPTY; │ │ │ │ │ + /** │ │ │ │ │ + * Method: updateSizeDestroy │ │ │ │ │ + * When the map is destroyed, we need to stop listening to updateSize │ │ │ │ │ + * events: this method stores the function we need to unregister in │ │ │ │ │ + * non-IE browsers. │ │ │ │ │ + */ │ │ │ │ │ + updateSizeDestroy: null, │ │ │ │ │ │ │ │ │ │ - for (var child = node.firstChild; child; child = child.nextSibling) { │ │ │ │ │ - switch (child.nodeType) { │ │ │ │ │ - case 1: // element │ │ │ │ │ - complex = true; │ │ │ │ │ - break; │ │ │ │ │ - case 8: // comment │ │ │ │ │ - break; │ │ │ │ │ - default: │ │ │ │ │ - simple = true; │ │ │ │ │ - } │ │ │ │ │ - if (complex && simple) { │ │ │ │ │ - break; │ │ │ │ │ - } │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: destroy │ │ │ │ │ + * Destroy this map. │ │ │ │ │ + * Note that if you are using an application which removes a container │ │ │ │ │ + * of the map from the DOM, you need to ensure that you destroy the │ │ │ │ │ + * map *before* this happens; otherwise, the page unload handler │ │ │ │ │ + * will fail because the DOM elements that map.destroy() wants │ │ │ │ │ + * to clean up will be gone. (See │ │ │ │ │ + * http://trac.osgeo.org/openlayers/ticket/2277 for more information). │ │ │ │ │ + * This will apply to GeoExt and also to other applications which │ │ │ │ │ + * modify the DOM of the container of the OpenLayers Map. │ │ │ │ │ + */ │ │ │ │ │ + destroy: function() { │ │ │ │ │ + // if unloadDestroy is null, we've already been destroyed │ │ │ │ │ + if (!this.unloadDestroy) { │ │ │ │ │ + return false; │ │ │ │ │ } │ │ │ │ │ │ │ │ │ │ - if (complex && simple) { │ │ │ │ │ - type = OpenLayers.Format.XML.CONTENT_TYPE.MIXED; │ │ │ │ │ - } else if (complex) { │ │ │ │ │ - return OpenLayers.Format.XML.CONTENT_TYPE.COMPLEX; │ │ │ │ │ - } else if (simple) { │ │ │ │ │ - return OpenLayers.Format.XML.CONTENT_TYPE.SIMPLE; │ │ │ │ │ + // make sure panning doesn't continue after destruction │ │ │ │ │ + if (this.panTween) { │ │ │ │ │ + this.panTween.stop(); │ │ │ │ │ + this.panTween = null; │ │ │ │ │ + } │ │ │ │ │ + // make sure zooming doesn't continue after destruction │ │ │ │ │ + if (this.zoomTween) { │ │ │ │ │ + this.zoomTween.stop(); │ │ │ │ │ + this.zoomTween = null; │ │ │ │ │ } │ │ │ │ │ - return type; │ │ │ │ │ - }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIMethod: hasAttributeNS │ │ │ │ │ - * Determine whether a node has a particular attribute matching the given │ │ │ │ │ - * name and namespace. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * node - {Element} Node on which to search for an attribute. │ │ │ │ │ - * uri - {String} Namespace URI. │ │ │ │ │ - * name - {String} Local name of the attribute (without the prefix). │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Boolean} The node has an attribute matching the name and namespace. │ │ │ │ │ - */ │ │ │ │ │ - hasAttributeNS: function(node, uri, name) { │ │ │ │ │ - var found = false; │ │ │ │ │ - if (node.hasAttributeNS) { │ │ │ │ │ - found = node.hasAttributeNS(uri, name); │ │ │ │ │ - } else { │ │ │ │ │ - found = !!this.getAttributeNodeNS(node, uri, name); │ │ │ │ │ + // map has been destroyed. dont do it again! │ │ │ │ │ + OpenLayers.Event.stopObserving(window, 'unload', this.unloadDestroy); │ │ │ │ │ + this.unloadDestroy = null; │ │ │ │ │ + │ │ │ │ │ + if (this.updateSizeDestroy) { │ │ │ │ │ + OpenLayers.Event.stopObserving(window, 'resize', │ │ │ │ │ + this.updateSizeDestroy); │ │ │ │ │ } │ │ │ │ │ - return found; │ │ │ │ │ - }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIMethod: setAttributeNS │ │ │ │ │ - * Adds a new attribute or changes the value of an attribute with the given │ │ │ │ │ - * namespace and name. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * node - {Element} Element node on which to set the attribute. │ │ │ │ │ - * uri - {String} Namespace URI for the attribute. │ │ │ │ │ - * name - {String} Qualified name (prefix:localname) for the attribute. │ │ │ │ │ - * value - {String} Attribute value. │ │ │ │ │ - */ │ │ │ │ │ - setAttributeNS: function(node, uri, name, value) { │ │ │ │ │ - if (node.setAttributeNS) { │ │ │ │ │ - node.setAttributeNS(uri, name, value); │ │ │ │ │ - } else { │ │ │ │ │ - if (this.xmldom) { │ │ │ │ │ - if (uri) { │ │ │ │ │ - var attribute = node.ownerDocument.createNode( │ │ │ │ │ - 2, name, uri │ │ │ │ │ - ); │ │ │ │ │ - attribute.nodeValue = value; │ │ │ │ │ - node.setAttributeNode(attribute); │ │ │ │ │ - } else { │ │ │ │ │ - node.setAttribute(name, value); │ │ │ │ │ - } │ │ │ │ │ - } else { │ │ │ │ │ - throw "setAttributeNS not implemented"; │ │ │ │ │ + this.paddingForPopups = null; │ │ │ │ │ + │ │ │ │ │ + if (this.controls != null) { │ │ │ │ │ + for (var i = this.controls.length - 1; i >= 0; --i) { │ │ │ │ │ + this.controls[i].destroy(); │ │ │ │ │ } │ │ │ │ │ + this.controls = null; │ │ │ │ │ } │ │ │ │ │ - }, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Method: createElementNSPlus │ │ │ │ │ - * Shorthand for creating namespaced elements with optional attributes and │ │ │ │ │ - * child text nodes. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * name - {String} The qualified node name. │ │ │ │ │ - * options - {Object} Optional object for node configuration. │ │ │ │ │ - * │ │ │ │ │ - * Valid options: │ │ │ │ │ - * uri - {String} Optional namespace uri for the element - supply a prefix │ │ │ │ │ - * instead if the namespace uri is a property of the format's namespace │ │ │ │ │ - * object. │ │ │ │ │ - * attributes - {Object} Optional attributes to be set using the │ │ │ │ │ - * 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 (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 (!uri) { │ │ │ │ │ - uri = this.namespaces[this.defaultPrefix]; │ │ │ │ │ + if (this.viewPortDiv && this.viewPortDiv.parentNode) { │ │ │ │ │ + this.viewPortDiv.parentNode.removeChild(this.viewPortDiv); │ │ │ │ │ } │ │ │ │ │ - var node = this.createElementNS(uri, name); │ │ │ │ │ - if (options.attributes) { │ │ │ │ │ - this.setAttributes(node, options.attributes); │ │ │ │ │ + this.viewPortDiv = null; │ │ │ │ │ + │ │ │ │ │ + if (this.tileManager) { │ │ │ │ │ + this.tileManager.removeMap(this); │ │ │ │ │ + this.tileManager = null; │ │ │ │ │ } │ │ │ │ │ - var value = options.value; │ │ │ │ │ - if (value != null) { │ │ │ │ │ - node.appendChild(this.createTextNode(value)); │ │ │ │ │ + │ │ │ │ │ + if (this.eventListeners) { │ │ │ │ │ + this.events.un(this.eventListeners); │ │ │ │ │ + this.eventListeners = null; │ │ │ │ │ } │ │ │ │ │ - return node; │ │ │ │ │ + this.events.destroy(); │ │ │ │ │ + this.events = null; │ │ │ │ │ + │ │ │ │ │ + this.options = null; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: setAttributes │ │ │ │ │ - * Set multiple attributes given key value pairs from an object. │ │ │ │ │ + * APIMethod: setOptions │ │ │ │ │ + * Change the map options │ │ │ │ │ * │ │ │ │ │ * 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. │ │ │ │ │ + * options - {Object} Hashtable of options to tag to the map │ │ │ │ │ */ │ │ │ │ │ - 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); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ + 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 │ │ │ │ │ + }); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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). │ │ │ │ │ + * APIMethod: getTileSize │ │ │ │ │ + * Get the tile size for the map │ │ │ │ │ * │ │ │ │ │ * 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; │ │ │ │ │ + getTileSize: function() { │ │ │ │ │ + return this.tileSize; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ + │ │ │ │ │ /** │ │ │ │ │ - * Method: readChildNodes │ │ │ │ │ - * Shorthand for applying the named readers to all children of a node. │ │ │ │ │ - * For each child of type 1 (element), is called. │ │ │ │ │ + * APIMethod: getBy │ │ │ │ │ + * Get a list of objects given a property and a match item. │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * node - {DOMElement} The node to be read (required). │ │ │ │ │ - * obj - {Object} The object to be modified (optional). │ │ │ │ │ + * 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: │ │ │ │ │ - * {Object} The input object, modified. │ │ │ │ │ + * {Array} An array of items where the given property matches the given │ │ │ │ │ + * criteria. │ │ │ │ │ */ │ │ │ │ │ - readChildNodes: function(node, obj) { │ │ │ │ │ - if (!obj) { │ │ │ │ │ - obj = {}; │ │ │ │ │ - } │ │ │ │ │ - var children = node.childNodes; │ │ │ │ │ - var child; │ │ │ │ │ - for (var i = 0, len = children.length; i < len; ++i) { │ │ │ │ │ - child = children[i]; │ │ │ │ │ - if (child.nodeType == 1) { │ │ │ │ │ - this.readNode(child, obj); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - return obj; │ │ │ │ │ + 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; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: writeNode │ │ │ │ │ - * Shorthand for applying one of the named writers and appending the │ │ │ │ │ - * results to a node. If a qualified name is not provided for the │ │ │ │ │ - * second argument (and a local name is used instead), the namespace │ │ │ │ │ - * of the parent node will be assumed. │ │ │ │ │ + * APIMethod: getLayersBy │ │ │ │ │ + * Get a list of layers with properties matching the given criteria. │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * name - {String} The name of a node to generate. If a qualified name │ │ │ │ │ - * (e.g. "pre:Name") is used, the namespace prefix is assumed to be │ │ │ │ │ - * in the 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. │ │ │ │ │ + * 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: │ │ │ │ │ - * {DOMElement} The child node. │ │ │ │ │ + * {Array()} A list of layers matching the given criteria. │ │ │ │ │ + * An empty array is returned if no matches are found. │ │ │ │ │ */ │ │ │ │ │ - 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; │ │ │ │ │ + getLayersBy: function(property, match) { │ │ │ │ │ + return this.getBy("layers", property, match); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: getChildEl │ │ │ │ │ - * Get the first child element. Optionally only return the first child │ │ │ │ │ - * if it matches the given name and namespace URI. │ │ │ │ │ + * APIMethod: getLayersByName │ │ │ │ │ + * Get a list of layers with names matching the given name. │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * node - {DOMElement} The parent node. │ │ │ │ │ - * name - {String} Optional node name (local) to search for. │ │ │ │ │ - * uri - {String} Optional namespace URI to search for. │ │ │ │ │ + * 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: │ │ │ │ │ - * {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. │ │ │ │ │ + * {Array()} A list of layers matching the given name. │ │ │ │ │ + * An empty array is returned if no matches are found. │ │ │ │ │ */ │ │ │ │ │ - getChildEl: function(node, name, uri) { │ │ │ │ │ - return node && this.getThisOrNextEl(node.firstChild, name, uri); │ │ │ │ │ + getLayersByName: function(match) { │ │ │ │ │ + return this.getLayersBy("name", match); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: getNextEl │ │ │ │ │ - * Get the next sibling element. Optionally get the first sibling only │ │ │ │ │ - * if it matches the given local name and namespace URI. │ │ │ │ │ + * APIMethod: getLayersByClass │ │ │ │ │ + * Get a list of layers of a given class (CLASS_NAME). │ │ │ │ │ * │ │ │ │ │ * 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. │ │ │ │ │ + * 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: │ │ │ │ │ - * {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. │ │ │ │ │ + * {Array()} A list of layers matching the given class. │ │ │ │ │ + * An empty array is returned if no matches are found. │ │ │ │ │ */ │ │ │ │ │ - getNextEl: function(node, name, uri) { │ │ │ │ │ - return node && this.getThisOrNextEl(node.nextSibling, name, uri); │ │ │ │ │ + getLayersByClass: function(match) { │ │ │ │ │ + return this.getLayersBy("CLASS_NAME", match); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: getThisOrNextEl │ │ │ │ │ - * Return this node or the next element node. Optionally get the first │ │ │ │ │ - * sibling with the given local name or namespace URI. │ │ │ │ │ + * APIMethod: getControlsBy │ │ │ │ │ + * Get a list of controls with properties matching the given criteria. │ │ │ │ │ * │ │ │ │ │ * 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. │ │ │ │ │ + * 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: │ │ │ │ │ - * {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. │ │ │ │ │ + * {Array()} A list of controls matching the given │ │ │ │ │ + * criteria. An empty array is returned if no matches are found. │ │ │ │ │ */ │ │ │ │ │ - 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; │ │ │ │ │ + getControlsBy: function(property, match) { │ │ │ │ │ + return this.getBy("controls", property, match); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * APIMethod: getControlsByClass │ │ │ │ │ + * Get a list of controls of a given class (CLASS_NAME). │ │ │ │ │ * │ │ │ │ │ - * 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. │ │ │ │ │ - * │ │ │ │ │ + * 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: │ │ │ │ │ - * {String} The namespace URI for the given prefix. Returns null if the prefix │ │ │ │ │ - * cannot be found or the node is the wrong type. │ │ │ │ │ + * {Array()} A list of controls matching the given class. │ │ │ │ │ + * An empty array is returned if no matches are found. │ │ │ │ │ */ │ │ │ │ │ - lookupNamespaceURI: function(node, prefix) { │ │ │ │ │ - var uri = null; │ │ │ │ │ - if (node) { │ │ │ │ │ - if (node.lookupNamespaceURI) { │ │ │ │ │ - uri = node.lookupNamespaceURI(prefix); │ │ │ │ │ - } else { │ │ │ │ │ - outer: switch (node.nodeType) { │ │ │ │ │ - case 1: // ELEMENT_NODE │ │ │ │ │ - if (node.namespaceURI !== null && node.prefix === prefix) { │ │ │ │ │ - uri = node.namespaceURI; │ │ │ │ │ - break outer; │ │ │ │ │ - } │ │ │ │ │ - var len = node.attributes.length; │ │ │ │ │ - if (len) { │ │ │ │ │ - var attr; │ │ │ │ │ - for (var i = 0; i < len; ++i) { │ │ │ │ │ - attr = node.attributes[i]; │ │ │ │ │ - if (attr.prefix === "xmlns" && attr.name === "xmlns:" + prefix) { │ │ │ │ │ - uri = attr.value || null; │ │ │ │ │ - break outer; │ │ │ │ │ - } else if (attr.name === "xmlns" && prefix === null) { │ │ │ │ │ - uri = attr.value || null; │ │ │ │ │ - break outer; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - uri = this.lookupNamespaceURI(node.parentNode, prefix); │ │ │ │ │ - break outer; │ │ │ │ │ - case 2: // ATTRIBUTE_NODE │ │ │ │ │ - uri = this.lookupNamespaceURI(node.ownerElement, prefix); │ │ │ │ │ - break outer; │ │ │ │ │ - case 9: // DOCUMENT_NODE │ │ │ │ │ - uri = this.lookupNamespaceURI(node.documentElement, prefix); │ │ │ │ │ - break outer; │ │ │ │ │ - case 6: // ENTITY_NODE │ │ │ │ │ - case 12: // NOTATION_NODE │ │ │ │ │ - case 10: // DOCUMENT_TYPE_NODE │ │ │ │ │ - case 11: // DOCUMENT_FRAGMENT_NODE │ │ │ │ │ - break outer; │ │ │ │ │ - default: │ │ │ │ │ - // TEXT_NODE (3), CDATA_SECTION_NODE (4), ENTITY_REFERENCE_NODE (5), │ │ │ │ │ - // PROCESSING_INSTRUCTION_NODE (7), COMMENT_NODE (8) │ │ │ │ │ - uri = this.lookupNamespaceURI(node.parentNode, prefix); │ │ │ │ │ - break outer; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - return uri; │ │ │ │ │ + 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 */ │ │ │ │ │ + /* */ │ │ │ │ │ + /********************************************************/ │ │ │ │ │ + │ │ │ │ │ /** │ │ │ │ │ - * Method: getXMLDoc │ │ │ │ │ - * Get an XML document for nodes that are not supported in HTML (e.g. │ │ │ │ │ - * createCDATASection). On IE, this will either return an existing or │ │ │ │ │ - * create a new on the instance. On other browsers, this will │ │ │ │ │ - * either return an existing or create a new shared document (see │ │ │ │ │ - * ). │ │ │ │ │ + * APIMethod: getLayer │ │ │ │ │ + * Get a layer based on its id │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * id - {String} A layer id │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {XMLDocument} │ │ │ │ │ + * {} The Layer with the corresponding id from the map's │ │ │ │ │ + * layer collection, or null if not found. │ │ │ │ │ */ │ │ │ │ │ - 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"); │ │ │ │ │ + getLayer: function(id) { │ │ │ │ │ + var foundLayer = null; │ │ │ │ │ + for (var i = 0, len = this.layers.length; i < len; i++) { │ │ │ │ │ + var layer = this.layers[i]; │ │ │ │ │ + if (layer.id == id) { │ │ │ │ │ + foundLayer = layer; │ │ │ │ │ + break; │ │ │ │ │ } │ │ │ │ │ } │ │ │ │ │ - return OpenLayers.Format.XML.document || this.xmldom; │ │ │ │ │ + return foundLayer; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.XML" │ │ │ │ │ - │ │ │ │ │ -}); │ │ │ │ │ - │ │ │ │ │ -OpenLayers.Format.XML.CONTENT_TYPE = { │ │ │ │ │ - EMPTY: 0, │ │ │ │ │ - SIMPLE: 1, │ │ │ │ │ - COMPLEX: 2, │ │ │ │ │ - MIXED: 3 │ │ │ │ │ -}; │ │ │ │ │ - │ │ │ │ │ -/** │ │ │ │ │ - * APIFunction: OpenLayers.Format.XML.lookupNamespaceURI │ │ │ │ │ - * Takes a prefix and returns the namespace URI associated with it on the given │ │ │ │ │ - * node if found (and null if not). Supplying null for the prefix will │ │ │ │ │ - * return the default namespace. │ │ │ │ │ - * │ │ │ │ │ - * For browsers that support it, this calls the native lookupNamesapceURI │ │ │ │ │ - * function. In other browsers, this is an implementation of │ │ │ │ │ - * http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-lookupNamespaceURI. │ │ │ │ │ - * │ │ │ │ │ - * For browsers that don't support the attribute.ownerElement property, this │ │ │ │ │ - * method cannot be called on attribute nodes. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * node - {DOMElement} The node from which to start looking. │ │ │ │ │ - * prefix - {String} The prefix to lookup or null to lookup the default namespace. │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {String} The namespace URI for the given prefix. Returns null if the prefix │ │ │ │ │ - * cannot be found or the node is the wrong type. │ │ │ │ │ - */ │ │ │ │ │ -OpenLayers.Format.XML.lookupNamespaceURI = OpenLayers.Function.bind( │ │ │ │ │ - OpenLayers.Format.XML.prototype.lookupNamespaceURI, │ │ │ │ │ - OpenLayers.Format.XML.prototype │ │ │ │ │ -); │ │ │ │ │ - │ │ │ │ │ -/** │ │ │ │ │ - * Property: OpenLayers.Format.XML.document │ │ │ │ │ - * {XMLDocument} XML document to reuse for creating non-HTML compliant nodes, │ │ │ │ │ - * like document.createCDATASection. │ │ │ │ │ - */ │ │ │ │ │ -OpenLayers.Format.XML.document = null; │ │ │ │ │ -/* ====================================================================== │ │ │ │ │ - OpenLayers/Format/OGCExceptionReport.js │ │ │ │ │ - ====================================================================== */ │ │ │ │ │ - │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ - * full text of the license. */ │ │ │ │ │ - │ │ │ │ │ -/** │ │ │ │ │ - * @requires OpenLayers/Format/XML.js │ │ │ │ │ - */ │ │ │ │ │ - │ │ │ │ │ -/** │ │ │ │ │ - * Class: OpenLayers.Format.OGCExceptionReport │ │ │ │ │ - * Class to read exception reports for various OGC services and versions. │ │ │ │ │ - * │ │ │ │ │ - * Inherits from: │ │ │ │ │ - * - │ │ │ │ │ - */ │ │ │ │ │ -OpenLayers.Format.OGCExceptionReport = OpenLayers.Class(OpenLayers.Format.XML, { │ │ │ │ │ - │ │ │ │ │ /** │ │ │ │ │ - * Property: namespaces │ │ │ │ │ - * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ │ + * Method: setLayerZIndex │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * layer - {} │ │ │ │ │ + * zIdx - {int} │ │ │ │ │ */ │ │ │ │ │ - namespaces: { │ │ │ │ │ - ogc: "http://www.opengis.net/ogc" │ │ │ │ │ + setLayerZIndex: function(layer, zIdx) { │ │ │ │ │ + layer.setZIndex( │ │ │ │ │ + this.Z_INDEX_BASE[layer.isBaseLayer ? 'BaseLayer' : 'Overlay'] + │ │ │ │ │ + zIdx * 5); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: regExes │ │ │ │ │ - * Compiled regular expressions for manipulating strings. │ │ │ │ │ + * Method: resetLayersZIndex │ │ │ │ │ + * Reset each layer's z-index based on layer's array index │ │ │ │ │ */ │ │ │ │ │ - regExes: { │ │ │ │ │ - trimSpace: (/^\s*|\s*$/g), │ │ │ │ │ - removeSpace: (/\s*/g), │ │ │ │ │ - splitSpace: (/\s+/), │ │ │ │ │ - trimComma: (/\s*,\s*/g) │ │ │ │ │ + resetLayersZIndex: function() { │ │ │ │ │ + for (var i = 0, len = this.layers.length; i < len; i++) { │ │ │ │ │ + var layer = this.layers[i]; │ │ │ │ │ + this.setLayerZIndex(layer, i); │ │ │ │ │ + } │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * APIMethod: addLayer │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * data - {String} or {DOMElement} data to read/parse. │ │ │ │ │ + * layer - {} │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {Object} Information about the exceptions that occurred. │ │ │ │ │ + * {Boolean} True if the layer has been added to the map. │ │ │ │ │ */ │ │ │ │ │ - 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); │ │ │ │ │ + addLayer: function(layer) { │ │ │ │ │ + for (var i = 0, len = this.layers.length; i < len; i++) { │ │ │ │ │ + if (this.layers[i] == layer) { │ │ │ │ │ + return false; │ │ │ │ │ } │ │ │ │ │ } │ │ │ │ │ - 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); │ │ │ │ │ - } │ │ │ │ │ + if (this.events.triggerEvent("preaddlayer", { │ │ │ │ │ + layer: layer │ │ │ │ │ + }) === false) { │ │ │ │ │ + return false; │ │ │ │ │ + } │ │ │ │ │ + if (this.allOverlays) { │ │ │ │ │ + layer.isBaseLayer = false; │ │ │ │ │ } │ │ │ │ │ - }, │ │ │ │ │ - │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.OGCExceptionReport" │ │ │ │ │ │ │ │ │ │ -}); │ │ │ │ │ -/* ====================================================================== │ │ │ │ │ - OpenLayers/Format/XML/VersionedOGC.js │ │ │ │ │ - ====================================================================== */ │ │ │ │ │ + layer.div.className = "olLayerDiv"; │ │ │ │ │ + layer.div.style.overflow = ""; │ │ │ │ │ + this.setLayerZIndex(layer, this.layers.length); │ │ │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ - * full text of the license. */ │ │ │ │ │ + if (layer.isFixed) { │ │ │ │ │ + this.viewPortDiv.appendChild(layer.div); │ │ │ │ │ + } else { │ │ │ │ │ + this.layerContainerDiv.appendChild(layer.div); │ │ │ │ │ + } │ │ │ │ │ + this.layers.push(layer); │ │ │ │ │ + layer.setMap(this); │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * @requires OpenLayers/Format/XML.js │ │ │ │ │ - * @requires OpenLayers/Format/OGCExceptionReport.js │ │ │ │ │ - */ │ │ │ │ │ + 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(); │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * 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, { │ │ │ │ │ + this.events.triggerEvent("addlayer", { │ │ │ │ │ + layer: layer │ │ │ │ │ + }); │ │ │ │ │ + layer.events.triggerEvent("added", { │ │ │ │ │ + map: this, │ │ │ │ │ + layer: layer │ │ │ │ │ + }); │ │ │ │ │ + layer.afterAdd(); │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIProperty: defaultVersion │ │ │ │ │ - * {String} Version number to assume if none found. │ │ │ │ │ - */ │ │ │ │ │ - defaultVersion: null, │ │ │ │ │ + return true; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: version │ │ │ │ │ - * {String} Specify a version string if one is known. │ │ │ │ │ + * APIMethod: addLayers │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * layers - {Array()} │ │ │ │ │ */ │ │ │ │ │ - version: null, │ │ │ │ │ + addLayers: function(layers) { │ │ │ │ │ + for (var i = 0, len = layers.length; i < len; i++) { │ │ │ │ │ + this.addLayer(layers[i]); │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIProperty: profile │ │ │ │ │ - * {String} If provided, use a custom profile. │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: removeLayer │ │ │ │ │ + * Removes a layer from the map by removing its visual element (the │ │ │ │ │ + * layer.div property), then removing it from the map's internal list │ │ │ │ │ + * of layers, setting the layer's map property to null. │ │ │ │ │ + * │ │ │ │ │ + * a "removelayer" event is triggered. │ │ │ │ │ + * │ │ │ │ │ + * very worthy of mention is that simply removing a layer from a map │ │ │ │ │ + * will not cause the removal of any popups which may have been created │ │ │ │ │ + * by the layer. this is due to the fact that it was decided at some │ │ │ │ │ + * point that popups would not belong to layers. thus there is no way │ │ │ │ │ + * for us to know here to which layer the popup belongs. │ │ │ │ │ + * │ │ │ │ │ + * A simple solution to this is simply to call destroy() on the layer. │ │ │ │ │ + * the default OpenLayers.Layer class's destroy() function │ │ │ │ │ + * automatically takes care to remove itself from whatever map it has │ │ │ │ │ + * been attached to. │ │ │ │ │ + * │ │ │ │ │ + * The correct solution is for the layer itself to register an │ │ │ │ │ + * event-handler on "removelayer" and when it is called, if it │ │ │ │ │ + * recognizes itself as the layer being removed, then it cycles through │ │ │ │ │ + * its own personal list of popups, removing them from the map. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * layer - {} │ │ │ │ │ + * setNewBaseLayer - {Boolean} Default is true │ │ │ │ │ */ │ │ │ │ │ - profile: null, │ │ │ │ │ + removeLayer: function(layer, setNewBaseLayer) { │ │ │ │ │ + if (this.events.triggerEvent("preremovelayer", { │ │ │ │ │ + layer: layer │ │ │ │ │ + }) === false) { │ │ │ │ │ + return; │ │ │ │ │ + } │ │ │ │ │ + if (setNewBaseLayer == null) { │ │ │ │ │ + setNewBaseLayer = true; │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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, │ │ │ │ │ + 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; │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: name │ │ │ │ │ - * {String} The name of this parser, this is the part of the CLASS_NAME │ │ │ │ │ - * except for "OpenLayers.Format." │ │ │ │ │ - */ │ │ │ │ │ - name: null, │ │ │ │ │ + // if we removed the base layer, need to set a new one │ │ │ │ │ + if (this.baseLayer == layer) { │ │ │ │ │ + this.baseLayer = null; │ │ │ │ │ + if (setNewBaseLayer) { │ │ │ │ │ + for (var i = 0, len = this.layers.length; i < len; i++) { │ │ │ │ │ + var iLayer = this.layers[i]; │ │ │ │ │ + if (iLayer.isBaseLayer || this.allOverlays) { │ │ │ │ │ + this.setBaseLayer(iLayer); │ │ │ │ │ + break; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIProperty: stringifyOutput │ │ │ │ │ - * {Boolean} If true, write will return a string otherwise a DOMElement. │ │ │ │ │ - * Default is false. │ │ │ │ │ - */ │ │ │ │ │ - stringifyOutput: false, │ │ │ │ │ + this.resetLayersZIndex(); │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: parser │ │ │ │ │ - * {Object} Instance of the versioned parser. Cached for multiple read and │ │ │ │ │ - * write calls of the same version. │ │ │ │ │ - */ │ │ │ │ │ - parser: null, │ │ │ │ │ + this.events.triggerEvent("removelayer", { │ │ │ │ │ + layer: layer │ │ │ │ │ + }); │ │ │ │ │ + layer.events.triggerEvent("removed", { │ │ │ │ │ + map: this, │ │ │ │ │ + layer: layer │ │ │ │ │ + }); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Constructor: OpenLayers.Format.XML.VersionedOGC. │ │ │ │ │ - * Constructor. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * options - {Object} Optional object whose properties will be set on │ │ │ │ │ - * the object. │ │ │ │ │ + * APIMethod: getNumLayers │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Int} The number of layers attached to the map. │ │ │ │ │ */ │ │ │ │ │ - initialize: function(options) { │ │ │ │ │ - OpenLayers.Format.XML.prototype.initialize.apply(this, [options]); │ │ │ │ │ - var className = this.CLASS_NAME; │ │ │ │ │ - this.name = className.substring(className.lastIndexOf(".") + 1); │ │ │ │ │ + getNumLayers: function() { │ │ │ │ │ + return this.layers.length; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: getVersion │ │ │ │ │ - * Returns the version to use. Subclasses can override this function │ │ │ │ │ - * if a different version detection is needed. │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: getLayerIndex │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * root - {DOMElement} │ │ │ │ │ - * options - {Object} Optional configuration object. │ │ │ │ │ + * layer - {} │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {String} The version to use. │ │ │ │ │ + * {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. │ │ │ │ │ */ │ │ │ │ │ - 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; │ │ │ │ │ + getLayerIndex: function(layer) { │ │ │ │ │ + return OpenLayers.Util.indexOf(this.layers, layer); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: getParser │ │ │ │ │ - * Get an instance of the cached parser if available, otherwise create one. │ │ │ │ │ + /** │ │ │ │ │ + * 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: │ │ │ │ │ - * version - {String} │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {} │ │ │ │ │ + * layer - {} │ │ │ │ │ + * idx - {int} │ │ │ │ │ */ │ │ │ │ │ - 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; │ │ │ │ │ + setLayerIndex: function(layer, idx) { │ │ │ │ │ + var base = this.getLayerIndex(layer); │ │ │ │ │ + if (idx < 0) { │ │ │ │ │ + idx = 0; │ │ │ │ │ + } else if (idx > this.layers.length) { │ │ │ │ │ + idx = this.layers.length; │ │ │ │ │ + } │ │ │ │ │ + if (base != idx) { │ │ │ │ │ + this.layers.splice(base, 1); │ │ │ │ │ + this.layers.splice(idx, 0, layer); │ │ │ │ │ + for (var i = 0, len = this.layers.length; i < len; i++) { │ │ │ │ │ + this.setLayerZIndex(this.layers[i], i); │ │ │ │ │ + } │ │ │ │ │ + this.events.triggerEvent("changelayer", { │ │ │ │ │ + layer: layer, │ │ │ │ │ + property: "order" │ │ │ │ │ + }); │ │ │ │ │ + if (this.allOverlays) { │ │ │ │ │ + if (idx === 0) { │ │ │ │ │ + this.setBaseLayer(layer); │ │ │ │ │ + } else if (this.baseLayer !== this.layers[0]) { │ │ │ │ │ + this.setBaseLayer(this.layers[0]); │ │ │ │ │ } │ │ │ │ │ } │ │ │ │ │ - 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. │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: raiseLayer │ │ │ │ │ + * Change the index of the given layer by delta. If delta is positive, │ │ │ │ │ + * the layer is moved up the map's layer stack; if delta is negative, │ │ │ │ │ + * the layer is moved down. Again, note that this cannot (or at least │ │ │ │ │ + * should not) be effectively used to raise base layers above overlays. │ │ │ │ │ * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {String} The document as a string │ │ │ │ │ + * Paremeters: │ │ │ │ │ + * layer - {} │ │ │ │ │ + * delta - {int} │ │ │ │ │ */ │ │ │ │ │ - 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]); │ │ │ │ │ - } │ │ │ │ │ + raiseLayer: function(layer, delta) { │ │ │ │ │ + var idx = this.getLayerIndex(layer) + delta; │ │ │ │ │ + this.setLayerIndex(layer, idx); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIMethod: read │ │ │ │ │ - * Read a doc and return an object representing the document. │ │ │ │ │ - * │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: setBaseLayer │ │ │ │ │ + * Allows user to specify one of the currently-loaded layers as the Map's │ │ │ │ │ + * new base layer. │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * data - {String | DOMElement} Data to read. │ │ │ │ │ - * options - {Object} Options for the reader. │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Object} An object representing the document. │ │ │ │ │ + * newBaseLayer - {} │ │ │ │ │ */ │ │ │ │ │ - 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 │ │ │ │ │ + setBaseLayer: function(newBaseLayer) { │ │ │ │ │ │ │ │ │ │ - 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; │ │ │ │ │ - }, │ │ │ │ │ + if (newBaseLayer != this.baseLayer) { │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.XML.VersionedOGC" │ │ │ │ │ -}); │ │ │ │ │ -/* ====================================================================== │ │ │ │ │ - OpenLayers/Format/OWSCommon.js │ │ │ │ │ - ====================================================================== */ │ │ │ │ │ + // ensure newBaseLayer is already loaded │ │ │ │ │ + if (OpenLayers.Util.indexOf(this.layers, newBaseLayer) != -1) { │ │ │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ - * full text of the license. */ │ │ │ │ │ + // preserve center and scale when changing base layers │ │ │ │ │ + var center = this.getCachedCenter(); │ │ │ │ │ + var newResolution = OpenLayers.Util.getResolutionFromScale( │ │ │ │ │ + this.getScale(), newBaseLayer.units │ │ │ │ │ + ); │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * @requires OpenLayers/Format/XML/VersionedOGC.js │ │ │ │ │ - */ │ │ │ │ │ + // make the old base layer invisible │ │ │ │ │ + if (this.baseLayer != null && !this.allOverlays) { │ │ │ │ │ + this.baseLayer.setVisibility(false); │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * Class: OpenLayers.Format.OWSCommon │ │ │ │ │ - * Read OWSCommon. Create a new instance with the │ │ │ │ │ - * constructor. │ │ │ │ │ - * │ │ │ │ │ - * Inherits from: │ │ │ │ │ - * - │ │ │ │ │ - */ │ │ │ │ │ -OpenLayers.Format.OWSCommon = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, { │ │ │ │ │ + // set new baselayer │ │ │ │ │ + this.baseLayer = newBaseLayer; │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIProperty: defaultVersion │ │ │ │ │ - * {String} Version number to assume if none found. Default is "1.0.0". │ │ │ │ │ - */ │ │ │ │ │ - defaultVersion: "1.0.0", │ │ │ │ │ + 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(); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Constructor: OpenLayers.Format.OWSCommon │ │ │ │ │ - * Create a new parser for OWSCommon. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ │ - * this instance. │ │ │ │ │ - */ │ │ │ │ │ + // 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); │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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; │ │ │ │ │ + this.events.triggerEvent("changebaselayer", { │ │ │ │ │ + layer: this.baseLayer │ │ │ │ │ + }); │ │ │ │ │ } │ │ │ │ │ } │ │ │ │ │ - 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, { │ │ │ │ │ + /********************************************************/ │ │ │ │ │ + /* */ │ │ │ │ │ + /* Control Functions */ │ │ │ │ │ + /* */ │ │ │ │ │ + /* The following functions deal with adding and */ │ │ │ │ │ + /* removing Controls to and from the Map */ │ │ │ │ │ + /* */ │ │ │ │ │ + /********************************************************/ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: regExes │ │ │ │ │ - * Compiled regular expressions for manipulating strings. │ │ │ │ │ + * APIMethod: addControl │ │ │ │ │ + * Add the passed over control to the map. Optionally │ │ │ │ │ + * position the control at the given pixel. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * control - {} │ │ │ │ │ + * px - {} │ │ │ │ │ */ │ │ │ │ │ - regExes: { │ │ │ │ │ - trimSpace: (/^\s*|\s*$/g), │ │ │ │ │ - removeSpace: (/\s*/g), │ │ │ │ │ - splitSpace: (/\s+/), │ │ │ │ │ - trimComma: (/\s*,\s*/g) │ │ │ │ │ + addControl: function(control, px) { │ │ │ │ │ + this.controls.push(control); │ │ │ │ │ + this.addControlToMap(control, px); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: read │ │ │ │ │ - * │ │ │ │ │ + * 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: │ │ │ │ │ - * data - {DOMElement} An OWSCommon document element. │ │ │ │ │ - * options - {Object} Options for the reader. │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Object} An object representing the OWSCommon document. │ │ │ │ │ + * controls - {Array()} │ │ │ │ │ + * pixels - {Array()} │ │ │ │ │ */ │ │ │ │ │ - read: function(data, options) { │ │ │ │ │ - options = OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ │ - var ows = {}; │ │ │ │ │ - this.readChildNodes(data, ows); │ │ │ │ │ - return ows; │ │ │ │ │ + addControls: function(controls, pixels) { │ │ │ │ │ + var pxs = (arguments.length === 1) ? [] : pixels; │ │ │ │ │ + for (var i = 0, len = controls.length; i < len; i++) { │ │ │ │ │ + var ctrl = controls[i]; │ │ │ │ │ + var px = (pxs[i]) ? pxs[i] : null; │ │ │ │ │ + this.addControl(ctrl, px); │ │ │ │ │ + } │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * Method: addControlToMap │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * │ │ │ │ │ + * control - {} │ │ │ │ │ + * px - {} │ │ │ │ │ */ │ │ │ │ │ - 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); │ │ │ │ │ - } │ │ │ │ │ + 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; │ │ │ │ │ } │ │ │ │ │ - }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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; │ │ │ │ │ + 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 < len; i++) { │ │ │ │ │ + var control = this.controls[i]; │ │ │ │ │ + if (control.id == id) { │ │ │ │ │ + returnControl = control; │ │ │ │ │ + break; │ │ │ │ │ } │ │ │ │ │ } │ │ │ │ │ + return returnControl; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.OWSCommon.v1" │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: removeControl │ │ │ │ │ + * Remove a control from the map. Removes the control both from the map │ │ │ │ │ + * object's internal array of controls, as well as from the map's │ │ │ │ │ + * viewPort (assuming the control was not added outsideViewport) │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * control - {} 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); │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ -}); │ │ │ │ │ -/* ====================================================================== │ │ │ │ │ - OpenLayers/Format/OWSCommon/v1_1_0.js │ │ │ │ │ - ====================================================================== */ │ │ │ │ │ + /********************************************************/ │ │ │ │ │ + /* */ │ │ │ │ │ + /* Popup Functions */ │ │ │ │ │ + /* */ │ │ │ │ │ + /* The following functions deal with adding and */ │ │ │ │ │ + /* removing Popups to and from the Map */ │ │ │ │ │ + /* */ │ │ │ │ │ + /********************************************************/ │ │ │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ - * full text of the license. */ │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: addPopup │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * popup - {} │ │ │ │ │ + * exclusive - {Boolean} If true, closes all other popups first │ │ │ │ │ + */ │ │ │ │ │ + addPopup: function(popup, exclusive) { │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * @requires OpenLayers/Format/OWSCommon/v1.js │ │ │ │ │ - */ │ │ │ │ │ + if (exclusive) { │ │ │ │ │ + //remove all other popups from screen │ │ │ │ │ + for (var i = this.popups.length - 1; i >= 0; --i) { │ │ │ │ │ + this.removePopup(this.popups[i]); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * 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, { │ │ │ │ │ + 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 */ │ │ │ │ │ + /* */ │ │ │ │ │ + /********************************************************/ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: namespaces │ │ │ │ │ - * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - namespaces: { │ │ │ │ │ - ows: "http://www.opengis.net/ows/1.1", │ │ │ │ │ - xlink: "http://www.w3.org/1999/xlink" │ │ │ │ │ + getSize: function() { │ │ │ │ │ + var size = null; │ │ │ │ │ + if (this.size != null) { │ │ │ │ │ + size = this.size.clone(); │ │ │ │ │ + } │ │ │ │ │ + return size; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * 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) │ │ │ │ │ */ │ │ │ │ │ - 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); │ │ │ │ │ + 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; │ │ │ │ │ } │ │ │ │ │ - }, OpenLayers.Format.OWSCommon.v1.prototype.readers["ows"]) │ │ │ │ │ + if (!newSize.equals(oldSize)) { │ │ │ │ │ + │ │ │ │ │ + // store the new size │ │ │ │ │ + this.size = newSize; │ │ │ │ │ + │ │ │ │ │ + //notify layers of mapresize │ │ │ │ │ + for (var i = 0, len = this.layers.length; i < len; i++) { │ │ │ │ │ + this.layers[i].onMapResize(); │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + var center = this.getCachedCenter(); │ │ │ │ │ + │ │ │ │ │ + if (this.baseLayer != null && center != null) { │ │ │ │ │ + var zoom = this.getZoom(); │ │ │ │ │ + this.zoom = null; │ │ │ │ │ + this.setCenter(center, zoom); │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + this.events.triggerEvent("updatesize"); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * Method: getCurrentSize │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {} A new object with the dimensions │ │ │ │ │ + * of the map div │ │ │ │ │ */ │ │ │ │ │ - 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"]) │ │ │ │ │ + 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; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.OWSCommon.v1_1_0" │ │ │ │ │ + /** │ │ │ │ │ + * 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) { │ │ │ │ │ │ │ │ │ │ -}); │ │ │ │ │ -/* ====================================================================== │ │ │ │ │ - OpenLayers/Format/WCSGetCoverage.js │ │ │ │ │ - ====================================================================== */ │ │ │ │ │ + var extent = null; │ │ │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ - * full text of the license. */ │ │ │ │ │ + if (center == null) { │ │ │ │ │ + center = this.getCachedCenter(); │ │ │ │ │ + } │ │ │ │ │ + if (resolution == null) { │ │ │ │ │ + resolution = this.getResolution(); │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * @requires OpenLayers/Format/XML.js │ │ │ │ │ - * @requires OpenLayers/Format/OWSCommon/v1_1_0.js │ │ │ │ │ - */ │ │ │ │ │ + if ((center != null) && (resolution != null)) { │ │ │ │ │ + var halfWDeg = (this.size.w * resolution) / 2; │ │ │ │ │ + var halfHDeg = (this.size.h * resolution) / 2; │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * Class: OpenLayers.Format.WCSGetCoverage version 1.1.0 │ │ │ │ │ - * │ │ │ │ │ - * Inherits from: │ │ │ │ │ - * - │ │ │ │ │ - */ │ │ │ │ │ -OpenLayers.Format.WCSGetCoverage = OpenLayers.Class(OpenLayers.Format.XML, { │ │ │ │ │ + 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 */ │ │ │ │ │ + /* */ │ │ │ │ │ + /********************************************************/ │ │ │ │ │ /** │ │ │ │ │ - * Property: namespaces │ │ │ │ │ - * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ │ + * APIMethod: getCenter │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {} │ │ │ │ │ */ │ │ │ │ │ - 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" │ │ │ │ │ + getCenter: function() { │ │ │ │ │ + var center = null; │ │ │ │ │ + var cachedCenter = this.getCachedCenter(); │ │ │ │ │ + if (cachedCenter) { │ │ │ │ │ + center = cachedCenter.clone(); │ │ │ │ │ + } │ │ │ │ │ + return center; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: regExes │ │ │ │ │ - * Compiled regular expressions for manipulating strings. │ │ │ │ │ + * Method: getCachedCenter │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {} │ │ │ │ │ */ │ │ │ │ │ - regExes: { │ │ │ │ │ - trimSpace: (/^\s*|\s*$/g), │ │ │ │ │ - removeSpace: (/\s*/g), │ │ │ │ │ - splitSpace: (/\s+/), │ │ │ │ │ - trimComma: (/\s*,\s*/g) │ │ │ │ │ + getCachedCenter: function() { │ │ │ │ │ + if (!this.center && this.size) { │ │ │ │ │ + this.center = this.getLonLatFromViewPortPx({ │ │ │ │ │ + x: this.size.w / 2, │ │ │ │ │ + y: this.size.h / 2 │ │ │ │ │ + }); │ │ │ │ │ + } │ │ │ │ │ + return this.center; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Constant: VERSION │ │ │ │ │ - * {String} 1.1.2 │ │ │ │ │ + * APIMethod: getZoom │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Integer} │ │ │ │ │ */ │ │ │ │ │ - VERSION: "1.1.2", │ │ │ │ │ + getZoom: function() { │ │ │ │ │ + return this.zoom; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: schemaLocation │ │ │ │ │ - * {String} Schema location │ │ │ │ │ + /** │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - schemaLocation: "http://www.opengis.net/wcs/1.1 http://schemas.opengis.net/wcs/1.1/wcsGetCoverage.xsd", │ │ │ │ │ + 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); │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Constructor: OpenLayers.Format.WCSGetCoverage │ │ │ │ │ + * 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: │ │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ │ - * this instance. │ │ │ │ │ + * dx - {Number} │ │ │ │ │ + * dy - {Number} │ │ │ │ │ */ │ │ │ │ │ + moveByPx: function(dx, dy) { │ │ │ │ │ + var hw = this.size.w / 2; │ │ │ │ │ + var hh = this.size.h / 2; │ │ │ │ │ + var x = hw + dx; │ │ │ │ │ + var y = hh + dy; │ │ │ │ │ + var wrapDateLine = this.baseLayer.wrapDateLine; │ │ │ │ │ + var xRestriction = 0; │ │ │ │ │ + var yRestriction = 0; │ │ │ │ │ + if (this.restrictedExtent) { │ │ │ │ │ + xRestriction = hw; │ │ │ │ │ + yRestriction = hh; │ │ │ │ │ + // wrapping the date line makes no sense for restricted extents │ │ │ │ │ + wrapDateLine = false; │ │ │ │ │ + } │ │ │ │ │ + dx = wrapDateLine || │ │ │ │ │ + x <= this.maxPx.x - xRestriction && │ │ │ │ │ + x >= this.minPx.x + xRestriction ? Math.round(dx) : 0; │ │ │ │ │ + dy = y <= this.maxPx.y - yRestriction && │ │ │ │ │ + y >= this.minPx.y + yRestriction ? Math.round(dy) : 0; │ │ │ │ │ + if (dx || dy) { │ │ │ │ │ + if (!this.dragging) { │ │ │ │ │ + this.dragging = true; │ │ │ │ │ + this.events.triggerEvent("movestart"); │ │ │ │ │ + } │ │ │ │ │ + this.center = null; │ │ │ │ │ + if (dx) { │ │ │ │ │ + this.layerContainerOriginPx.x -= dx; │ │ │ │ │ + this.minPx.x -= dx; │ │ │ │ │ + this.maxPx.x -= dx; │ │ │ │ │ + } │ │ │ │ │ + if (dy) { │ │ │ │ │ + this.layerContainerOriginPx.y -= dy; │ │ │ │ │ + this.minPx.y -= dy; │ │ │ │ │ + this.maxPx.y -= dy; │ │ │ │ │ + } │ │ │ │ │ + this.applyTransform(); │ │ │ │ │ + var layer, i, len; │ │ │ │ │ + for (i = 0, len = this.layers.length; i < len; ++i) { │ │ │ │ │ + layer = this.layers[i]; │ │ │ │ │ + if (layer.visibility && │ │ │ │ │ + (layer === this.baseLayer || layer.inRange)) { │ │ │ │ │ + layer.moveByPx(dx, dy); │ │ │ │ │ + layer.events.triggerEvent("move"); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + this.events.triggerEvent("move"); │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: write │ │ │ │ │ + * Method: adjustZoom │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * options - {Object} Optional object. │ │ │ │ │ + * zoom - {Number} The zoom level to adjust │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {String} A WCS GetCoverage request XML string. │ │ │ │ │ + * {Integer} Adjusted zoom level that shows a map not wider than its │ │ │ │ │ + * 's maxExtent. │ │ │ │ │ */ │ │ │ │ │ - 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]); │ │ │ │ │ + adjustZoom: function(zoom) { │ │ │ │ │ + if (this.baseLayer && this.baseLayer.wrapDateLine) { │ │ │ │ │ + var resolution, resolutions = this.baseLayer.resolutions, │ │ │ │ │ + maxResolution = this.getMaxExtent().getWidth() / this.size.w; │ │ │ │ │ + if (this.getResolutionForZoom(zoom) > maxResolution) { │ │ │ │ │ + if (this.fractionalZoom) { │ │ │ │ │ + zoom = this.getZoomForResolution(maxResolution); │ │ │ │ │ + } else { │ │ │ │ │ + for (var i = zoom | 0, ii = resolutions.length; i < ii; ++i) { │ │ │ │ │ + if (resolutions[i] <= maxResolution) { │ │ │ │ │ + zoom = i; │ │ │ │ │ + break; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + return zoom; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * APIMethod: getMinZoom │ │ │ │ │ + * Returns the minimum zoom level for the current map view. If the base │ │ │ │ │ + * layer is configured with 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. │ │ │ │ │ */ │ │ │ │ │ - writers: { │ │ │ │ │ - "wcs": { │ │ │ │ │ - "GetCoverage": function(options) { │ │ │ │ │ - var node = this.createElementNSPlus("wcs:GetCoverage", { │ │ │ │ │ - attributes: { │ │ │ │ │ - version: options.version || this.VERSION, │ │ │ │ │ - service: 'WCS' │ │ │ │ │ - } │ │ │ │ │ - }); │ │ │ │ │ - this.writeNode("ows:Identifier", options.identifier, node); │ │ │ │ │ - this.writeNode("wcs:DomainSubset", options.domainSubset, node); │ │ │ │ │ - this.writeNode("wcs:Output", options.output, node); │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "DomainSubset": function(domainSubset) { │ │ │ │ │ - var node = this.createElementNSPlus("wcs:DomainSubset", {}); │ │ │ │ │ - this.writeNode("ows:BoundingBox", domainSubset.boundingBox, node); │ │ │ │ │ - if (domainSubset.temporalSubset) { │ │ │ │ │ - this.writeNode("wcs:TemporalSubset", domainSubset.temporalSubset, node); │ │ │ │ │ - } │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "TemporalSubset": function(temporalSubset) { │ │ │ │ │ - var node = this.createElementNSPlus("wcs:TemporalSubset", {}); │ │ │ │ │ - for (var i = 0, len = temporalSubset.timePeriods.length; i < len; ++i) { │ │ │ │ │ - this.writeNode("wcs:TimePeriod", temporalSubset.timePeriods[i], node); │ │ │ │ │ - } │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "TimePeriod": function(timePeriod) { │ │ │ │ │ - var node = this.createElementNSPlus("wcs:TimePeriod", {}); │ │ │ │ │ - this.writeNode("wcs:BeginPosition", timePeriod.begin, node); │ │ │ │ │ - this.writeNode("wcs:EndPosition", timePeriod.end, node); │ │ │ │ │ - if (timePeriod.resolution) { │ │ │ │ │ - this.writeNode("wcs:TimeResolution", timePeriod.resolution, node); │ │ │ │ │ - } │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "BeginPosition": function(begin) { │ │ │ │ │ - var node = this.createElementNSPlus("wcs:BeginPosition", { │ │ │ │ │ - value: begin │ │ │ │ │ - }); │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "EndPosition": function(end) { │ │ │ │ │ - var node = this.createElementNSPlus("wcs:EndPosition", { │ │ │ │ │ - value: end │ │ │ │ │ - }); │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "TimeResolution": function(resolution) { │ │ │ │ │ - var node = this.createElementNSPlus("wcs:TimeResolution", { │ │ │ │ │ - value: resolution │ │ │ │ │ - }); │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "Output": function(output) { │ │ │ │ │ - var node = this.createElementNSPlus("wcs:Output", { │ │ │ │ │ - attributes: { │ │ │ │ │ - format: output.format, │ │ │ │ │ - store: output.store │ │ │ │ │ - } │ │ │ │ │ - }); │ │ │ │ │ - if (output.gridCRS) { │ │ │ │ │ - this.writeNode("wcs:GridCRS", output.gridCRS, node); │ │ │ │ │ - } │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "GridCRS": function(gridCRS) { │ │ │ │ │ - var node = this.createElementNSPlus("wcs:GridCRS", {}); │ │ │ │ │ - this.writeNode("wcs:GridBaseCRS", gridCRS.baseCRS, node); │ │ │ │ │ - if (gridCRS.type) { │ │ │ │ │ - this.writeNode("wcs:GridType", gridCRS.type, node); │ │ │ │ │ + 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 (gridCRS.origin) { │ │ │ │ │ - this.writeNode("wcs:GridOrigin", gridCRS.origin, node); │ │ │ │ │ + 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); │ │ │ │ │ } │ │ │ │ │ - this.writeNode("wcs:GridOffsets", gridCRS.offsets, node); │ │ │ │ │ - if (gridCRS.CS) { │ │ │ │ │ - this.writeNode("wcs:GridCS", gridCRS.CS, node); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + 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); │ │ │ │ │ } │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "GridBaseCRS": function(baseCRS) { │ │ │ │ │ - return this.createElementNSPlus("wcs:GridBaseCRS", { │ │ │ │ │ - value: baseCRS │ │ │ │ │ - }); │ │ │ │ │ - }, │ │ │ │ │ - "GridOrigin": function(origin) { │ │ │ │ │ - return this.createElementNSPlus("wcs:GridOrigin", { │ │ │ │ │ - value: origin │ │ │ │ │ - }); │ │ │ │ │ - }, │ │ │ │ │ - "GridType": function(type) { │ │ │ │ │ - return this.createElementNSPlus("wcs:GridType", { │ │ │ │ │ - value: type │ │ │ │ │ - }); │ │ │ │ │ - }, │ │ │ │ │ - "GridOffsets": function(offsets) { │ │ │ │ │ - return this.createElementNSPlus("wcs:GridOffsets", { │ │ │ │ │ - value: offsets │ │ │ │ │ - }); │ │ │ │ │ - }, │ │ │ │ │ - "GridCS": function(CS) { │ │ │ │ │ - return this.createElementNSPlus("wcs:GridCS", { │ │ │ │ │ - value: CS │ │ │ │ │ + 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) │ │ │ │ │ + }; │ │ │ │ │ } │ │ │ │ │ - }, │ │ │ │ │ - "ows": OpenLayers.Format.OWSCommon.v1_1_0.prototype.writers.ows │ │ │ │ │ - }, │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.WCSGetCoverage" │ │ │ │ │ + if (zoomChanged) { │ │ │ │ │ + this.zoom = zoom; │ │ │ │ │ + this.resolution = res; │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ -}); │ │ │ │ │ -/* ====================================================================== │ │ │ │ │ - OpenLayers/Format/WFST.js │ │ │ │ │ - ====================================================================== */ │ │ │ │ │ + var bounds = this.getExtent(); │ │ │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ - * full text of the license. */ │ │ │ │ │ + //send the move call to the baselayer and all the overlays │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * @requires OpenLayers/Format.js │ │ │ │ │ - */ │ │ │ │ │ + if (this.baseLayer.visibility) { │ │ │ │ │ + this.baseLayer.moveTo(bounds, zoomChanged, options.dragging); │ │ │ │ │ + options.dragging || this.baseLayer.events.triggerEvent( │ │ │ │ │ + "moveend", { │ │ │ │ │ + zoomChanged: zoomChanged │ │ │ │ │ + } │ │ │ │ │ + ); │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * 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); │ │ │ │ │ -}; │ │ │ │ │ + bounds = this.baseLayer.getExtent(); │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * Constant: OpenLayers.Format.WFST.DEFAULTS │ │ │ │ │ - * {Object} Default properties for the WFST format. │ │ │ │ │ - */ │ │ │ │ │ -OpenLayers.Format.WFST.DEFAULTS = { │ │ │ │ │ - "version": "1.0.0" │ │ │ │ │ -}; │ │ │ │ │ -/* ====================================================================== │ │ │ │ │ - OpenLayers/Style.js │ │ │ │ │ - ====================================================================== */ │ │ │ │ │ + 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 │ │ │ │ │ + } │ │ │ │ │ + ); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ - * full text of the license. */ │ │ │ │ │ + this.events.triggerEvent("move"); │ │ │ │ │ + dragging || this.events.triggerEvent("moveend"); │ │ │ │ │ │ │ │ │ │ + if (zoomChanged) { │ │ │ │ │ + //redraw popups │ │ │ │ │ + for (var i = 0, len = this.popups.length; i < len; i++) { │ │ │ │ │ + this.popups[i].updatePosition(); │ │ │ │ │ + } │ │ │ │ │ + this.events.triggerEvent("zoomend"); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ │ - * @requires OpenLayers/Util.js │ │ │ │ │ - * @requires OpenLayers/Feature/Vector.js │ │ │ │ │ - */ │ │ │ │ │ + /** │ │ │ │ │ + * Method: centerLayerContainer │ │ │ │ │ + * This function takes care to recenter the layerContainerDiv. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * lonlat - {} │ │ │ │ │ + */ │ │ │ │ │ + centerLayerContainer: function(lonlat) { │ │ │ │ │ + var originPx = this.getViewPortPxFromLonLat(this.layerContainerOrigin); │ │ │ │ │ + var newPx = this.getViewPortPxFromLonLat(lonlat); │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * Class: OpenLayers.Style │ │ │ │ │ - * This class represents a UserStyle obtained │ │ │ │ │ - * from a SLD, containing styling rules. │ │ │ │ │ - */ │ │ │ │ │ -OpenLayers.Style = OpenLayers.Class({ │ │ │ │ │ + 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; │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: id │ │ │ │ │ - * {String} A unique id for this session. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - id: null, │ │ │ │ │ + isValidZoomLevel: function(zoomLevel) { │ │ │ │ │ + return ((zoomLevel != null) && │ │ │ │ │ + (zoomLevel >= 0) && │ │ │ │ │ + (zoomLevel < this.getNumZoomLevels())); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: name │ │ │ │ │ - * {String} │ │ │ │ │ + * Method: isValidLonLat │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * lonlat - {} │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Boolean} Whether or not the lonlat passed in is non-null and within │ │ │ │ │ + * the maxExtent bounds │ │ │ │ │ */ │ │ │ │ │ - name: null, │ │ │ │ │ + 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 */ │ │ │ │ │ + /* */ │ │ │ │ │ + /********************************************************/ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: title │ │ │ │ │ - * {String} Title of this style (set if included in SLD) │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - title: null, │ │ │ │ │ + getProjection: function() { │ │ │ │ │ + var projection = this.getProjectionObject(); │ │ │ │ │ + return projection ? projection.getCode() : null; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: description │ │ │ │ │ - * {String} Description of this style (set if abstract is included in SLD) │ │ │ │ │ + * APIMethod: getProjectionObject │ │ │ │ │ + * Returns the projection obect from the baselayer. │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {} The Projection of the base layer. │ │ │ │ │ */ │ │ │ │ │ - description: null, │ │ │ │ │ + getProjectionObject: function() { │ │ │ │ │ + var projection = null; │ │ │ │ │ + if (this.baseLayer != null) { │ │ │ │ │ + projection = this.baseLayer.projection; │ │ │ │ │ + } │ │ │ │ │ + return projection; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: layerName │ │ │ │ │ - * {} name of the layer that this style belongs to, usually │ │ │ │ │ - * according to the NamedLayer attribute of an SLD document. │ │ │ │ │ + * APIMethod: getMaxResolution │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {String} The Map's Maximum Resolution │ │ │ │ │ */ │ │ │ │ │ - layerName: null, │ │ │ │ │ + getMaxResolution: function() { │ │ │ │ │ + var maxResolution = null; │ │ │ │ │ + if (this.baseLayer != null) { │ │ │ │ │ + maxResolution = this.baseLayer.maxResolution; │ │ │ │ │ + } │ │ │ │ │ + return maxResolution; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: isDefault │ │ │ │ │ - * {Boolean} │ │ │ │ │ + * 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). │ │ │ │ │ */ │ │ │ │ │ - isDefault: false, │ │ │ │ │ + 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; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: rules │ │ │ │ │ - * {Array()} │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: getNumZoomLevels │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Integer} The total number of zoom levels that can be displayed by the │ │ │ │ │ + * current baseLayer. │ │ │ │ │ */ │ │ │ │ │ - rules: null, │ │ │ │ │ + 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 */ │ │ │ │ │ + /* */ │ │ │ │ │ + /********************************************************/ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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 │ │ │ │ │ + * APIMethod: getExtent │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {} A Bounds object which represents the lon/lat │ │ │ │ │ + * bounds of the current viewPort. │ │ │ │ │ + * If no baselayer is set, returns null. │ │ │ │ │ */ │ │ │ │ │ - context: null, │ │ │ │ │ + getExtent: function() { │ │ │ │ │ + var extent = null; │ │ │ │ │ + if (this.baseLayer != null) { │ │ │ │ │ + extent = this.baseLayer.getExtent(); │ │ │ │ │ + } │ │ │ │ │ + return extent; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * APIMethod: getResolution │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Float} The current resolution of the map. │ │ │ │ │ + * If no baselayer is set, returns null. │ │ │ │ │ */ │ │ │ │ │ - defaultStyle: 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; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * APIMethod: getUnits │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Float} The current units of the map. │ │ │ │ │ + * If no baselayer is set, returns null. │ │ │ │ │ */ │ │ │ │ │ - defaultsPerSymbolizer: false, │ │ │ │ │ + getUnits: function() { │ │ │ │ │ + var units = null; │ │ │ │ │ + if (this.baseLayer != null) { │ │ │ │ │ + units = this.baseLayer.units; │ │ │ │ │ + } │ │ │ │ │ + return units; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * APIMethod: getScale │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Float} The current scale denominator of the map. │ │ │ │ │ + * If no baselayer is set, returns null. │ │ │ │ │ */ │ │ │ │ │ - propertyStyles: 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; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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. │ │ │ │ │ + /** │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - initialize: function(style, options) { │ │ │ │ │ - │ │ │ │ │ - OpenLayers.Util.extend(this, options); │ │ │ │ │ - this.rules = []; │ │ │ │ │ - if (options && options.rules) { │ │ │ │ │ - this.addRules(options.rules); │ │ │ │ │ + getZoomForExtent: function(bounds, closest) { │ │ │ │ │ + var zoom = null; │ │ │ │ │ + if (this.baseLayer != null) { │ │ │ │ │ + zoom = this.baseLayer.getZoomForExtent(bounds, closest); │ │ │ │ │ } │ │ │ │ │ - │ │ │ │ │ - // 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 + "_"); │ │ │ │ │ + return zoom; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIMethod: destroy │ │ │ │ │ - * nullify references to prevent circular references and memory leaks │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: getResolutionForZoom │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * zoom - {Float} │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Float} A suitable resolution for the specified zoom. If no baselayer │ │ │ │ │ + * is set, returns null. │ │ │ │ │ */ │ │ │ │ │ - destroy: function() { │ │ │ │ │ - for (var i = 0, len = this.rules.length; i < len; i++) { │ │ │ │ │ - this.rules[i].destroy(); │ │ │ │ │ - this.rules[i] = null; │ │ │ │ │ + getResolutionForZoom: function(zoom) { │ │ │ │ │ + var resolution = null; │ │ │ │ │ + if (this.baseLayer) { │ │ │ │ │ + resolution = this.baseLayer.getResolutionForZoom(zoom); │ │ │ │ │ } │ │ │ │ │ - this.rules = null; │ │ │ │ │ - this.defaultStyle = null; │ │ │ │ │ + return resolution; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: createSymbolizer │ │ │ │ │ - * creates a style by applying all feature-dependent rules to the base │ │ │ │ │ - * style. │ │ │ │ │ + * APIMethod: getZoomForResolution │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * feature - {} feature to evaluate rules for │ │ │ │ │ + * 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: │ │ │ │ │ - * {Object} symbolizer hash │ │ │ │ │ + * {Integer} A suitable zoom level for the specified resolution. │ │ │ │ │ + * If no baselayer is set, returns null. │ │ │ │ │ */ │ │ │ │ │ - createSymbolizer: function(feature) { │ │ │ │ │ - var style = this.defaultsPerSymbolizer ? {} : this.createLiterals( │ │ │ │ │ - OpenLayers.Util.extend({}, this.defaultStyle), feature); │ │ │ │ │ + getZoomForResolution: function(resolution, closest) { │ │ │ │ │ + var zoom = null; │ │ │ │ │ + if (this.baseLayer != null) { │ │ │ │ │ + zoom = this.baseLayer.getZoomForResolution(resolution, closest); │ │ │ │ │ + } │ │ │ │ │ + return zoom; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - var rules = this.rules; │ │ │ │ │ + /********************************************************/ │ │ │ │ │ + /* */ │ │ │ │ │ + /* Zooming Functions */ │ │ │ │ │ + /* */ │ │ │ │ │ + /* The following functions, all publicly exposed */ │ │ │ │ │ + /* in the API, are all merely wrappers to the */ │ │ │ │ │ + /* the setCenter() function */ │ │ │ │ │ + /* */ │ │ │ │ │ + /********************************************************/ │ │ │ │ │ │ │ │ │ │ - var rule, context; │ │ │ │ │ - var elseRules = []; │ │ │ │ │ - var appliedRules = false; │ │ │ │ │ - for (var i = 0, len = rules.length; i < len; i++) { │ │ │ │ │ - rule = rules[i]; │ │ │ │ │ - // does the rule apply? │ │ │ │ │ - var applies = rule.evaluate(feature); │ │ │ │ │ + /** │ │ │ │ │ + * 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 │ │ │ │ │ │ │ │ │ │ - if (applies) { │ │ │ │ │ - if (rule instanceof OpenLayers.Rule && rule.elseFilter) { │ │ │ │ │ - elseRules.push(rule); │ │ │ │ │ + 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 { │ │ │ │ │ - appliedRules = true; │ │ │ │ │ - this.applySymbolizer(rule, style, feature); │ │ │ │ │ + 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); │ │ │ │ │ } │ │ │ │ │ } │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - // if no other rules apply, apply the rules with else filters │ │ │ │ │ - if (appliedRules == false && elseRules.length > 0) { │ │ │ │ │ - appliedRules = true; │ │ │ │ │ - for (var i = 0, len = elseRules.length; i < len; i++) { │ │ │ │ │ - this.applySymbolizer(elseRules[i], style, feature); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: zoomIn │ │ │ │ │ + * │ │ │ │ │ + */ │ │ │ │ │ + zoomIn: function() { │ │ │ │ │ + this.zoomTo(this.getZoom() + 1); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - // don't display if there were rules but none applied │ │ │ │ │ - if (rules.length > 0 && appliedRules == false) { │ │ │ │ │ - style.display = "none"; │ │ │ │ │ - } │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: zoomOut │ │ │ │ │ + * │ │ │ │ │ + */ │ │ │ │ │ + zoomOut: function() { │ │ │ │ │ + this.zoomTo(this.getZoom() - 1); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - if (style.label != null && typeof style.label !== "string") { │ │ │ │ │ - style.label = String(style.label); │ │ │ │ │ + /** │ │ │ │ │ + * 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(); │ │ │ │ │ │ │ │ │ │ - return style; │ │ │ │ │ + //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)); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: applySymbolizer │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: zoomToMaxExtent │ │ │ │ │ + * Zoom to the full extent and recenter. │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * rule - {} │ │ │ │ │ - * style - {Object} │ │ │ │ │ - * feature - {} │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Object} A style with new symbolizer applied. │ │ │ │ │ + * options - {Object} │ │ │ │ │ + * │ │ │ │ │ + * Allowed Options: │ │ │ │ │ + * restricted - {Boolean} True to zoom to restricted extent if it is │ │ │ │ │ + * set. Defaults to true. │ │ │ │ │ */ │ │ │ │ │ - applySymbolizer: function(rule, style, feature) { │ │ │ │ │ - var symbolizerPrefix = feature.geometry ? │ │ │ │ │ - this.getSymbolizerPrefix(feature.geometry) : │ │ │ │ │ - OpenLayers.Style.SYMBOLIZER_PREFIXES[0]; │ │ │ │ │ + zoomToMaxExtent: function(options) { │ │ │ │ │ + //restricted is true by default │ │ │ │ │ + var restricted = (options) ? options.restricted : true; │ │ │ │ │ │ │ │ │ │ - var symbolizer = rule.symbolizer[symbolizerPrefix] || rule.symbolizer; │ │ │ │ │ + var maxExtent = this.getMaxExtent({ │ │ │ │ │ + 'restricted': restricted │ │ │ │ │ + }); │ │ │ │ │ + this.zoomToExtent(maxExtent); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - 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 │ │ │ │ │ - }); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ + /** │ │ │ │ │ + * 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); │ │ │ │ │ │ │ │ │ │ - // merge the style with the current style │ │ │ │ │ - return this.createLiterals( │ │ │ │ │ - OpenLayers.Util.extend(style, symbolizer), feature); │ │ │ │ │ + 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: createLiterals │ │ │ │ │ - * creates literals for all style properties that have an entry in │ │ │ │ │ - * . │ │ │ │ │ + * Method: getLonLatFromViewPortPx │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * style - {Object} style to create literals for. Will be modified │ │ │ │ │ - * inline. │ │ │ │ │ - * feature - {Object} │ │ │ │ │ + * viewPortPx - {|Object} An OpenLayers.Pixel or │ │ │ │ │ + * an object with a 'x' │ │ │ │ │ + * and 'y' properties. │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {Object} the modified style │ │ │ │ │ + * {} An OpenLayers.LonLat which is the passed-in view │ │ │ │ │ + * port , translated into lon/lat │ │ │ │ │ + * by the current base layer. │ │ │ │ │ */ │ │ │ │ │ - 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); │ │ │ │ │ + getLonLatFromViewPortPx: function(viewPortPx) { │ │ │ │ │ + var lonlat = null; │ │ │ │ │ + if (this.baseLayer != null) { │ │ │ │ │ + lonlat = this.baseLayer.getLonLatFromViewPortPx(viewPortPx); │ │ │ │ │ } │ │ │ │ │ - return style; │ │ │ │ │ + return lonlat; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * APIMethod: getViewPortPxFromLonLat │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * lonlat - {} │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {Object} hash of property names that need createLiteral parsing. The │ │ │ │ │ - * name of the property is the key, and the value is true; │ │ │ │ │ + * {} An OpenLayers.Pixel which is the passed-in │ │ │ │ │ + * , translated into view port │ │ │ │ │ + * pixels by the current base layer. │ │ │ │ │ */ │ │ │ │ │ - findPropertyStyles: function() { │ │ │ │ │ - var propertyStyles = {}; │ │ │ │ │ - │ │ │ │ │ - // check the default style │ │ │ │ │ - var style = this.defaultStyle; │ │ │ │ │ - this.addPropertyStyles(propertyStyles, style); │ │ │ │ │ - │ │ │ │ │ - // walk through all rules to check for properties in their symbolizer │ │ │ │ │ - var rules = this.rules; │ │ │ │ │ - var symbolizer, value; │ │ │ │ │ - for (var i = 0, len = rules.length; i < len; i++) { │ │ │ │ │ - symbolizer = rules[i].symbolizer; │ │ │ │ │ - for (var key in symbolizer) { │ │ │ │ │ - value = symbolizer[key]; │ │ │ │ │ - if (typeof value == "object") { │ │ │ │ │ - // symbolizer key is "Point", "Line" or "Polygon" │ │ │ │ │ - this.addPropertyStyles(propertyStyles, value); │ │ │ │ │ - } else { │ │ │ │ │ - // symbolizer is a hash of style properties │ │ │ │ │ - this.addPropertyStyles(propertyStyles, symbolizer); │ │ │ │ │ - break; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ + getViewPortPxFromLonLat: function(lonlat) { │ │ │ │ │ + var px = null; │ │ │ │ │ + if (this.baseLayer != null) { │ │ │ │ │ + px = this.baseLayer.getViewPortPxFromLonLat(lonlat); │ │ │ │ │ } │ │ │ │ │ - return propertyStyles; │ │ │ │ │ + return px; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: addPropertyStyles │ │ │ │ │ - * │ │ │ │ │ + * Method: getZoomTargetCenter │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * propertyStyles - {Object} hash to add new property styles to. Will be │ │ │ │ │ - * modified inline │ │ │ │ │ - * symbolizer - {Object} search this symbolizer for property styles │ │ │ │ │ - * │ │ │ │ │ + * xy - {} The zoom origin pixel location on the screen │ │ │ │ │ + * resolution - {Float} The resolution we want to get the center for │ │ │ │ │ + * │ │ │ │ │ * Returns: │ │ │ │ │ - * {Object} propertyStyles hash │ │ │ │ │ + * {} The location of the map center after the │ │ │ │ │ + * transformation described by the origin xy and the target resolution. │ │ │ │ │ */ │ │ │ │ │ - addPropertyStyles: function(propertyStyles, symbolizer) { │ │ │ │ │ - var property; │ │ │ │ │ - for (var key in symbolizer) { │ │ │ │ │ - property = symbolizer[key]; │ │ │ │ │ - if (typeof property == "string" && │ │ │ │ │ - property.match(/\$\{\w+\}/)) { │ │ │ │ │ - propertyStyles[key] = true; │ │ │ │ │ - } │ │ │ │ │ + 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 propertyStyles; │ │ │ │ │ + return lonlat; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ + // │ │ │ │ │ + // CONVENIENCE TRANSLATION FUNCTIONS FOR API │ │ │ │ │ + // │ │ │ │ │ + │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: addRules │ │ │ │ │ - * Adds rules to this style. │ │ │ │ │ + * APIMethod: getLonLatFromPixel │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * rules - {Array()} │ │ │ │ │ + * 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 │ │ │ │ │ */ │ │ │ │ │ - addRules: function(rules) { │ │ │ │ │ - Array.prototype.push.apply(this.rules, rules); │ │ │ │ │ - this.propertyStyles = this.findPropertyStyles(); │ │ │ │ │ + getLonLatFromPixel: function(px) { │ │ │ │ │ + return this.getLonLatFromViewPortPx(px); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: setDefaultStyle │ │ │ │ │ - * Sets the default style for this style object. │ │ │ │ │ + * 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: │ │ │ │ │ - * style - {Object} Hash of style properties │ │ │ │ │ + * lonlat - {} A map location. │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {} An OpenLayers.Pixel corresponding to the │ │ │ │ │ + * translated into view port pixels by the current │ │ │ │ │ + * base layer. │ │ │ │ │ */ │ │ │ │ │ - setDefaultStyle: function(style) { │ │ │ │ │ - this.defaultStyle = style; │ │ │ │ │ - this.propertyStyles = this.findPropertyStyles(); │ │ │ │ │ + getPixelFromLonLat: function(lonlat) { │ │ │ │ │ + var px = this.getViewPortPxFromLonLat(lonlat); │ │ │ │ │ + px.x = Math.round(px.x); │ │ │ │ │ + px.y = Math.round(px.y); │ │ │ │ │ + return px; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: getSymbolizerPrefix │ │ │ │ │ - * Returns the correct symbolizer prefix according to the │ │ │ │ │ - * geometry type of the passed geometry │ │ │ │ │ + * Method: getGeodesicPixelSize │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * geometry - {} │ │ │ │ │ + * px - {} The pixel to get the geodesic length for. If │ │ │ │ │ + * not provided, the center pixel of the map viewport will be used. │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {String} key of the according symbolizer │ │ │ │ │ + * {} The geodesic size of the pixel in kilometers. │ │ │ │ │ */ │ │ │ │ │ - getSymbolizerPrefix: function(geometry) { │ │ │ │ │ - var prefixes = OpenLayers.Style.SYMBOLIZER_PREFIXES; │ │ │ │ │ - for (var i = 0, len = prefixes.length; i < len; i++) { │ │ │ │ │ - if (geometry.CLASS_NAME.indexOf(prefixes[i]) != -1) { │ │ │ │ │ - return prefixes[i]; │ │ │ │ │ - } │ │ │ │ │ + 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: clone │ │ │ │ │ - * Clones this style. │ │ │ │ │ + * APIMethod: getViewPortPxFromLayerPx │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * layerPx - {} │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {} Clone of this style. │ │ │ │ │ + * {} Layer Pixel translated into ViewPort Pixel │ │ │ │ │ + * coordinates │ │ │ │ │ */ │ │ │ │ │ - clone: function() { │ │ │ │ │ - var options = OpenLayers.Util.extend({}, this); │ │ │ │ │ - // clone rules │ │ │ │ │ - if (this.rules) { │ │ │ │ │ - options.rules = []; │ │ │ │ │ - for (var i = 0, len = this.rules.length; i < len; ++i) { │ │ │ │ │ - options.rules.push(this.rules[i].clone()); │ │ │ │ │ - } │ │ │ │ │ + getViewPortPxFromLayerPx: function(layerPx) { │ │ │ │ │ + var viewPortPx = null; │ │ │ │ │ + if (layerPx != null) { │ │ │ │ │ + var dX = this.layerContainerOriginPx.x; │ │ │ │ │ + var dY = this.layerContainerOriginPx.y; │ │ │ │ │ + viewPortPx = layerPx.add(dX, dY); │ │ │ │ │ } │ │ │ │ │ - // clone context │ │ │ │ │ - options.context = this.context && OpenLayers.Util.extend({}, this.context); │ │ │ │ │ - //clone default style │ │ │ │ │ - var defaultStyle = OpenLayers.Util.extend({}, this.defaultStyle); │ │ │ │ │ - return new OpenLayers.Style(defaultStyle, options); │ │ │ │ │ + return viewPortPx; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Style" │ │ │ │ │ -}); │ │ │ │ │ - │ │ │ │ │ - │ │ │ │ │ -/** │ │ │ │ │ - * Function: createLiteral │ │ │ │ │ - * converts a style value holding a combination of PropertyName and Literal │ │ │ │ │ - * into a Literal, taking the property values from the passed features. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * value - {String} value to parse. If this string contains a construct like │ │ │ │ │ - * "foo ${bar}", then "foo " will be taken as literal, and "${bar}" │ │ │ │ │ - * will be replaced by the value of the "bar" attribute of the passed │ │ │ │ │ - * feature. │ │ │ │ │ - * context - {Object} context to take attribute values from │ │ │ │ │ - * feature - {} 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. │ │ │ │ │ - * │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: getLayerPxFromViewPortPx │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * options - {Object} Optional object whose properties will be set on the │ │ │ │ │ - * instance. │ │ │ │ │ + * viewPortPx - {} │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {} │ │ │ │ │ + * {} ViewPort Pixel translated into Layer Pixel │ │ │ │ │ + * coordinates │ │ │ │ │ */ │ │ │ │ │ - initialize: function(options) { │ │ │ │ │ - OpenLayers.Util.extend(this, options); │ │ │ │ │ + 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; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIMethod: destroy │ │ │ │ │ - * Remove reference to anything added. │ │ │ │ │ - */ │ │ │ │ │ - destroy: function() {}, │ │ │ │ │ + // │ │ │ │ │ + // TRANSLATION: LonLat <-> LayerPx │ │ │ │ │ + // │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: evaluate │ │ │ │ │ - * Evaluates this filter in a specific context. Instances or subclasses │ │ │ │ │ - * are supposed to override this method. │ │ │ │ │ + * Method: getLonLatFromLayerPx │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * context - {Object} Context to use in evaluating the filter. If a vector │ │ │ │ │ - * feature is provided, the feature.attributes will be used as context. │ │ │ │ │ - * │ │ │ │ │ + * px - {} │ │ │ │ │ + * │ │ │ │ │ * Returns: │ │ │ │ │ - * {Boolean} The filter applies. │ │ │ │ │ + * {} │ │ │ │ │ */ │ │ │ │ │ - evaluate: function(context) { │ │ │ │ │ - return true; │ │ │ │ │ + getLonLatFromLayerPx: function(px) { │ │ │ │ │ + //adjust for displacement of layerContainerDiv │ │ │ │ │ + px = this.getViewPortPxFromLayerPx(px); │ │ │ │ │ + return this.getLonLatFromViewPortPx(px); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: clone │ │ │ │ │ - * Clones this filter. Should be implemented by subclasses. │ │ │ │ │ + * APIMethod: getLayerPxFromLonLat │ │ │ │ │ * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * lonlat - {} lonlat │ │ │ │ │ + * │ │ │ │ │ * Returns: │ │ │ │ │ - * {} Clone of this filter. │ │ │ │ │ + * {} An OpenLayers.Pixel which is the passed-in │ │ │ │ │ + * , translated into layer pixels │ │ │ │ │ + * by the current base layer │ │ │ │ │ */ │ │ │ │ │ - clone: function() { │ │ │ │ │ - return null; │ │ │ │ │ + getLayerPxFromLonLat: function(lonlat) { │ │ │ │ │ + //adjust for displacement of layerContainerDiv │ │ │ │ │ + var px = this.getPixelFromLonLat(lonlat); │ │ │ │ │ + return this.getLayerPxFromViewPortPx(px); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: toString │ │ │ │ │ + * 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. │ │ │ │ │ * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {String} Include in your build to get a CQL │ │ │ │ │ - * representation of the filter returned. Otherwise "[Object object]" │ │ │ │ │ - * will be returned. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - toString: function() { │ │ │ │ │ - var string; │ │ │ │ │ - if (OpenLayers.Format && OpenLayers.Format.CQL) { │ │ │ │ │ - string = OpenLayers.Format.CQL.prototype.write(this); │ │ │ │ │ + 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 { │ │ │ │ │ - string = Object.prototype.toString.call(this); │ │ │ │ │ + style.left = x + 'px'; │ │ │ │ │ + style.top = y + 'px'; │ │ │ │ │ + // We previously might have had needTransform, so remove transform │ │ │ │ │ + if (transform !== null) { │ │ │ │ │ + style[transform] = ''; │ │ │ │ │ + } │ │ │ │ │ } │ │ │ │ │ - return string; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Filter" │ │ │ │ │ + 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/Filter/Spatial.js │ │ │ │ │ + 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/Filter.js │ │ │ │ │ + * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ │ + * @requires OpenLayers/Map.js │ │ │ │ │ + * @requires OpenLayers/Projection.js │ │ │ │ │ */ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Class: OpenLayers.Filter.Spatial │ │ │ │ │ - * This class represents a spatial filter. │ │ │ │ │ - * Currently implemented: BBOX, DWithin and Intersects │ │ │ │ │ - * │ │ │ │ │ - * Inherits from: │ │ │ │ │ - * - │ │ │ │ │ + * Class: OpenLayers.Layer │ │ │ │ │ */ │ │ │ │ │ -OpenLayers.Filter.Spatial = OpenLayers.Class(OpenLayers.Filter, { │ │ │ │ │ +OpenLayers.Layer = OpenLayers.Class({ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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 │ │ │ │ │ + * APIProperty: id │ │ │ │ │ + * {String} │ │ │ │ │ */ │ │ │ │ │ - type: null, │ │ │ │ │ + id: null, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIProperty: property │ │ │ │ │ - * {String} Name of the context property to compare. │ │ │ │ │ + /** │ │ │ │ │ + * APIProperty: name │ │ │ │ │ + * {String} │ │ │ │ │ */ │ │ │ │ │ - property: null, │ │ │ │ │ + name: null, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * APIProperty: div │ │ │ │ │ + * {DOMElement} │ │ │ │ │ + */ │ │ │ │ │ + div: 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. │ │ │ │ │ + * APIProperty: opacity │ │ │ │ │ + * {Float} The layer's opacity. Float number between 0.0 and 1.0. Default │ │ │ │ │ + * is 1. │ │ │ │ │ */ │ │ │ │ │ - value: null, │ │ │ │ │ + opacity: 1, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: distance │ │ │ │ │ - * {Number} The distance to use in a DWithin spatial filter. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - distance: null, │ │ │ │ │ + alwaysInRange: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: distanceUnits │ │ │ │ │ - * {String} The units to use for the distance, e.g. 'm'. │ │ │ │ │ + * Constant: RESOLUTION_PROPERTIES │ │ │ │ │ + * {Array} The properties that are used for calculating resolutions │ │ │ │ │ + * information. │ │ │ │ │ */ │ │ │ │ │ - distanceUnits: null, │ │ │ │ │ + RESOLUTION_PROPERTIES: [ │ │ │ │ │ + 'scales', 'resolutions', │ │ │ │ │ + 'maxScale', 'minScale', │ │ │ │ │ + 'maxResolution', 'minResolution', │ │ │ │ │ + 'numZoomLevels', 'maxZoomLevel' │ │ │ │ │ + ], │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Constructor: OpenLayers.Filter.Spatial │ │ │ │ │ - * Creates a spatial filter. │ │ │ │ │ + /** │ │ │ │ │ + * APIProperty: events │ │ │ │ │ + * {} │ │ │ │ │ * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * options - {Object} An optional object with properties to set on the │ │ │ │ │ - * filter. │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {} │ │ │ │ │ + * 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, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: evaluate │ │ │ │ │ - * Evaluates this filter for a specific feature. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * feature - {} feature to apply the filter to. │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Boolean} The feature meets filter criteria. │ │ │ │ │ + * APIProperty: map │ │ │ │ │ + * {} This variable is set when the layer is added to │ │ │ │ │ + * the map, via the accessor function setMap(). │ │ │ │ │ */ │ │ │ │ │ - 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; │ │ │ │ │ - }, │ │ │ │ │ + map: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: clone │ │ │ │ │ - * Clones this filter. │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {} Clone of this filter. │ │ │ │ │ + * APIProperty: isBaseLayer │ │ │ │ │ + * {Boolean} Whether or not the layer is a base layer. This should be set │ │ │ │ │ + * individually by all subclasses. Default is false │ │ │ │ │ */ │ │ │ │ │ - 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 │ │ │ │ │ - ====================================================================== */ │ │ │ │ │ + isBaseLayer: false, │ │ │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ - * full text of the license. */ │ │ │ │ │ + /** │ │ │ │ │ + * 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, │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * @requires OpenLayers/Filter.js │ │ │ │ │ - */ │ │ │ │ │ + /** │ │ │ │ │ + * APIProperty: visibility │ │ │ │ │ + * {Boolean} The layer should be displayed in the map. Default is true. │ │ │ │ │ + */ │ │ │ │ │ + visibility: true, │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * 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: attribution │ │ │ │ │ + * {String} Attribution string, displayed when an │ │ │ │ │ + * has been added to the map. │ │ │ │ │ + */ │ │ │ │ │ + attribution: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: fids │ │ │ │ │ - * {Array(String)} Feature Ids to evaluate this rule against. │ │ │ │ │ - * To be passed inside the params object. │ │ │ │ │ + * Property: inRange │ │ │ │ │ + * {Boolean} The current map resolution is within the layer's min/max │ │ │ │ │ + * range. This is set in whenever the zoom │ │ │ │ │ + * changes. │ │ │ │ │ */ │ │ │ │ │ - fids: null, │ │ │ │ │ + inRange: false, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: type │ │ │ │ │ - * {String} Type to identify this filter. │ │ │ │ │ + /** │ │ │ │ │ + * Propery: imageSize │ │ │ │ │ + * {} For layers with a gutter, the image is larger than │ │ │ │ │ + * the tile by twice the gutter in each dimension. │ │ │ │ │ */ │ │ │ │ │ - type: "FID", │ │ │ │ │ + imageSize: null, │ │ │ │ │ + │ │ │ │ │ + // OPTIONS │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Constructor: OpenLayers.Filter.FeatureId │ │ │ │ │ - * Creates an ogc:FeatureId rule. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * options - {Object} An optional object with properties to set on the │ │ │ │ │ - * rule │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {} │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - initialize: function(options) { │ │ │ │ │ - this.fids = []; │ │ │ │ │ - OpenLayers.Filter.prototype.initialize.apply(this, [options]); │ │ │ │ │ - }, │ │ │ │ │ + options: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: evaluate │ │ │ │ │ - * evaluates this rule for a specific feature │ │ │ │ │ + * 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. │ │ │ │ │ * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * feature - {} feature to apply the rule to. │ │ │ │ │ - * For vector features, the check is run against the fid, │ │ │ │ │ - * for plain features against the id. │ │ │ │ │ + * 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. │ │ │ │ │ * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Boolean} true if the rule applies, false if it does not │ │ │ │ │ */ │ │ │ │ │ - evaluate: function(feature) { │ │ │ │ │ - for (var i = 0, len = this.fids.length; i < len; i++) { │ │ │ │ │ - var fid = feature.fid || feature.id; │ │ │ │ │ - if (fid == this.fids[i]) { │ │ │ │ │ - return true; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - return false; │ │ │ │ │ - }, │ │ │ │ │ + projection: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: clone │ │ │ │ │ - * Clones this filter. │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {} Clone of this filter. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - clone: function() { │ │ │ │ │ - var filter = new OpenLayers.Filter.FeatureId(); │ │ │ │ │ - OpenLayers.Util.extend(filter, this); │ │ │ │ │ - filter.fids = this.fids.slice(); │ │ │ │ │ - return filter; │ │ │ │ │ - }, │ │ │ │ │ + units: null, │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Filter.FeatureId" │ │ │ │ │ -}); │ │ │ │ │ -/* ====================================================================== │ │ │ │ │ - OpenLayers/Format/WFST/v1.js │ │ │ │ │ - ====================================================================== */ │ │ │ │ │ + /** │ │ │ │ │ + * 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, │ │ │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ - * full text of the license. */ │ │ │ │ │ + /** │ │ │ │ │ + * 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, │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * @requires OpenLayers/Format/XML.js │ │ │ │ │ - * @requires OpenLayers/Format/WFST.js │ │ │ │ │ - * @requires OpenLayers/Filter/Spatial.js │ │ │ │ │ - * @requires OpenLayers/Filter/FeatureId.js │ │ │ │ │ - */ │ │ │ │ │ + /** │ │ │ │ │ + * 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, │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * Class: OpenLayers.Format.WFST.v1 │ │ │ │ │ - * Superclass for WFST parsers. │ │ │ │ │ - * │ │ │ │ │ - * Inherits from: │ │ │ │ │ - * - │ │ │ │ │ - */ │ │ │ │ │ -OpenLayers.Format.WFST.v1 = OpenLayers.Class(OpenLayers.Format.XML, { │ │ │ │ │ + /** │ │ │ │ │ + * 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, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: namespaces │ │ │ │ │ - * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - 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" │ │ │ │ │ - }, │ │ │ │ │ + maxResolution: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: defaultPrefix │ │ │ │ │ + * APIProperty: minResolution │ │ │ │ │ + * {Float} │ │ │ │ │ */ │ │ │ │ │ - defaultPrefix: "wfs", │ │ │ │ │ + minResolution: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: version │ │ │ │ │ - * {String} WFS version number. │ │ │ │ │ + * APIProperty: numZoomLevels │ │ │ │ │ + * {Integer} │ │ │ │ │ */ │ │ │ │ │ - version: null, │ │ │ │ │ + numZoomLevels: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: schemaLocation │ │ │ │ │ - * {String} Schema location for a particular minor version. │ │ │ │ │ + * APIProperty: minScale │ │ │ │ │ + * {Float} │ │ │ │ │ */ │ │ │ │ │ - schemaLocations: null, │ │ │ │ │ + minScale: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: srsName │ │ │ │ │ - * {String} URI for spatial reference system. │ │ │ │ │ + * APIProperty: maxScale │ │ │ │ │ + * {Float} │ │ │ │ │ */ │ │ │ │ │ - srsName: null, │ │ │ │ │ + maxScale: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: extractAttributes │ │ │ │ │ - * {Boolean} Extract attributes from GML. Default is true. │ │ │ │ │ + * APIProperty: displayOutsideMaxExtent │ │ │ │ │ + * {Boolean} Request map tiles that are completely outside of the max │ │ │ │ │ + * extent for this layer. Defaults to false. │ │ │ │ │ */ │ │ │ │ │ - extractAttributes: true, │ │ │ │ │ + displayOutsideMaxExtent: false, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - xy: true, │ │ │ │ │ + wrapDateLine: false, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: stateName │ │ │ │ │ - * {Object} Maps feature states to node names. │ │ │ │ │ + * Property: metadata │ │ │ │ │ + * {Object} This object can be used to store additional information on a │ │ │ │ │ + * layer object. │ │ │ │ │ */ │ │ │ │ │ - stateName: null, │ │ │ │ │ + metadata: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Constructor: OpenLayers.Format.WFST.v1 │ │ │ │ │ - * Instances of this class are not created directly. Use the │ │ │ │ │ - * or │ │ │ │ │ - * constructor instead. │ │ │ │ │ + * Constructor: OpenLayers.Layer │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ │ - * this instance. │ │ │ │ │ + * name - {String} The layer name │ │ │ │ │ + * options - {Object} Hashtable of extra options to tag onto the layer │ │ │ │ │ */ │ │ │ │ │ - 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]); │ │ │ │ │ - }, │ │ │ │ │ + initialize: function(name, 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; │ │ │ │ │ + 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); │ │ │ │ │ } │ │ │ │ │ + │ │ │ │ │ } │ │ │ │ │ - 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. │ │ │ │ │ + * 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: │ │ │ │ │ - * 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. │ │ │ │ │ + * setNewBaseLayer - {Boolean} Set a new base layer when this layer has │ │ │ │ │ + * been destroyed. Default is true. │ │ │ │ │ */ │ │ │ │ │ - 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); │ │ │ │ │ + destroy: function(setNewBaseLayer) { │ │ │ │ │ + if (setNewBaseLayer == null) { │ │ │ │ │ + setNewBaseLayer = true; │ │ │ │ │ } │ │ │ │ │ - if (obj.features && options.output === "features") { │ │ │ │ │ - obj = obj.features; │ │ │ │ │ + if (this.map != null) { │ │ │ │ │ + this.map.removeLayer(this, setNewBaseLayer); │ │ │ │ │ } │ │ │ │ │ - return obj; │ │ │ │ │ - }, │ │ │ │ │ + this.projection = null; │ │ │ │ │ + this.map = null; │ │ │ │ │ + this.name = null; │ │ │ │ │ + this.div = null; │ │ │ │ │ + this.options = 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: { │ │ │ │ │ - "wfs": { │ │ │ │ │ - "FeatureCollection": function(node, obj) { │ │ │ │ │ - obj.features = []; │ │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ │ + if (this.events) { │ │ │ │ │ + if (this.eventListeners) { │ │ │ │ │ + this.events.un(this.eventListeners); │ │ │ │ │ } │ │ │ │ │ + this.events.destroy(); │ │ │ │ │ } │ │ │ │ │ + this.eventListeners = null; │ │ │ │ │ + this.events = null; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * Method: clone │ │ │ │ │ * │ │ │ │ │ * 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. │ │ │ │ │ + * obj - {} The layer to be cloned │ │ │ │ │ * │ │ │ │ │ * 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. │ │ │ │ │ + * {} An exact clone of this │ │ │ │ │ */ │ │ │ │ │ - writers: { │ │ │ │ │ - "wfs": { │ │ │ │ │ - "GetFeature": function(options) { │ │ │ │ │ - var node = this.createElementNSPlus("wfs:GetFeature", { │ │ │ │ │ - attributes: { │ │ │ │ │ - service: "WFS", │ │ │ │ │ - version: this.version, │ │ │ │ │ - handle: options && options.handle, │ │ │ │ │ - outputFormat: options && options.outputFormat, │ │ │ │ │ - maxFeatures: options && options.maxFeatures, │ │ │ │ │ - "xsi:schemaLocation": this.schemaLocationAttr(options) │ │ │ │ │ - } │ │ │ │ │ - }); │ │ │ │ │ - if (typeof this.featureType == "string") { │ │ │ │ │ - this.writeNode("Query", options, node); │ │ │ │ │ - } else { │ │ │ │ │ - for (var i = 0, len = this.featureType.length; i < len; i++) { │ │ │ │ │ - options.featureType = this.featureType[i]; │ │ │ │ │ - this.writeNode("Query", options, node); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "Transaction": function(obj) { │ │ │ │ │ - obj = obj || {}; │ │ │ │ │ - var options = obj.options || {}; │ │ │ │ │ - var node = this.createElementNSPlus("wfs:Transaction", { │ │ │ │ │ - attributes: { │ │ │ │ │ - service: "WFS", │ │ │ │ │ - version: this.version, │ │ │ │ │ - handle: options.handle │ │ │ │ │ - } │ │ │ │ │ - }); │ │ │ │ │ - var i, len; │ │ │ │ │ - var features = obj.features; │ │ │ │ │ - if (features) { │ │ │ │ │ - // temporarily re-assigning geometry types │ │ │ │ │ - if (options.multi === true) { │ │ │ │ │ - OpenLayers.Util.extend(this.geometryTypes, { │ │ │ │ │ - "OpenLayers.Geometry.Point": "MultiPoint", │ │ │ │ │ - "OpenLayers.Geometry.LineString": (this.multiCurve === true) ? "MultiCurve" : "MultiLineString", │ │ │ │ │ - "OpenLayers.Geometry.Polygon": (this.multiSurface === true) ? "MultiSurface" : "MultiPolygon" │ │ │ │ │ - }); │ │ │ │ │ - } │ │ │ │ │ - var name, feature; │ │ │ │ │ - for (i = 0, len = features.length; i < len; ++i) { │ │ │ │ │ - feature = features[i]; │ │ │ │ │ - name = this.stateName[feature.state]; │ │ │ │ │ - if (name) { │ │ │ │ │ - this.writeNode(name, { │ │ │ │ │ - feature: feature, │ │ │ │ │ - options: options │ │ │ │ │ - }, node); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - // switch back to original geometry types assignment │ │ │ │ │ - if (options.multi === true) { │ │ │ │ │ - this.setGeometryTypes(); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - if (options.nativeElements) { │ │ │ │ │ - for (i = 0, len = options.nativeElements.length; i < len; ++i) { │ │ │ │ │ - this.writeNode("wfs:Native", │ │ │ │ │ - options.nativeElements[i], node); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "Native": function(nativeElement) { │ │ │ │ │ - var node = this.createElementNSPlus("wfs:Native", { │ │ │ │ │ - attributes: { │ │ │ │ │ - vendorId: nativeElement.vendorId, │ │ │ │ │ - safeToIgnore: nativeElement.safeToIgnore │ │ │ │ │ - }, │ │ │ │ │ - value: nativeElement.value │ │ │ │ │ - }); │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "Insert": function(obj) { │ │ │ │ │ - var feature = obj.feature; │ │ │ │ │ - var options = obj.options; │ │ │ │ │ - var node = this.createElementNSPlus("wfs:Insert", { │ │ │ │ │ - attributes: { │ │ │ │ │ - handle: options && options.handle │ │ │ │ │ - } │ │ │ │ │ - }); │ │ │ │ │ - this.srsName = this.getSrsName(feature); │ │ │ │ │ - this.writeNode("feature:_typeName", feature, node); │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "Update": function(obj) { │ │ │ │ │ - var feature = obj.feature; │ │ │ │ │ - var options = obj.options; │ │ │ │ │ - var node = this.createElementNSPlus("wfs:Update", { │ │ │ │ │ - attributes: { │ │ │ │ │ - handle: options && options.handle, │ │ │ │ │ - typeName: (this.featureNS ? this.featurePrefix + ":" : "") + │ │ │ │ │ - this.featureType │ │ │ │ │ - } │ │ │ │ │ - }); │ │ │ │ │ - if (this.featureNS) { │ │ │ │ │ - node.setAttribute("xmlns:" + this.featurePrefix, this.featureNS); │ │ │ │ │ - } │ │ │ │ │ + clone: function(obj) { │ │ │ │ │ │ │ │ │ │ - // add in geometry │ │ │ │ │ - var modified = feature.modified; │ │ │ │ │ - if (this.geometryName !== null && (!modified || modified.geometry !== undefined)) { │ │ │ │ │ - this.srsName = this.getSrsName(feature); │ │ │ │ │ - this.writeNode( │ │ │ │ │ - "Property", { │ │ │ │ │ - name: this.geometryName, │ │ │ │ │ - value: feature.geometry │ │ │ │ │ - }, node │ │ │ │ │ - ); │ │ │ │ │ - } │ │ │ │ │ + if (obj == null) { │ │ │ │ │ + obj = new OpenLayers.Layer(this.name, this.getOptions()); │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ - // add in attributes │ │ │ │ │ - for (var key in feature.attributes) { │ │ │ │ │ - if (feature.attributes[key] !== undefined && │ │ │ │ │ - (!modified || !modified.attributes || │ │ │ │ │ - (modified.attributes && modified.attributes[key] !== undefined))) { │ │ │ │ │ - this.writeNode( │ │ │ │ │ - "Property", { │ │ │ │ │ - name: key, │ │ │ │ │ - value: feature.attributes[key] │ │ │ │ │ - }, node │ │ │ │ │ - ); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ + // catch any randomly tagged-on properties │ │ │ │ │ + OpenLayers.Util.applyDefaults(obj, this); │ │ │ │ │ │ │ │ │ │ - // add feature id filter │ │ │ │ │ - this.writeNode("ogc:Filter", new OpenLayers.Filter.FeatureId({ │ │ │ │ │ - fids: [feature.fid] │ │ │ │ │ - }), node); │ │ │ │ │ + // a cloned layer should never have its map property set │ │ │ │ │ + // because it has not been added to a map yet. │ │ │ │ │ + obj.map = null; │ │ │ │ │ │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "Property": function(obj) { │ │ │ │ │ - var node = this.createElementNSPlus("wfs:Property"); │ │ │ │ │ - this.writeNode("Name", obj.name, node); │ │ │ │ │ - if (obj.value !== null) { │ │ │ │ │ - this.writeNode("Value", obj.value, node); │ │ │ │ │ - } │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "Name": function(name) { │ │ │ │ │ - return this.createElementNSPlus("wfs:Name", { │ │ │ │ │ - value: name │ │ │ │ │ - }); │ │ │ │ │ - }, │ │ │ │ │ - "Value": function(obj) { │ │ │ │ │ - var node; │ │ │ │ │ - if (obj instanceof OpenLayers.Geometry) { │ │ │ │ │ - node = this.createElementNSPlus("wfs:Value"); │ │ │ │ │ - var geom = this.writeNode("feature:_geometry", obj).firstChild; │ │ │ │ │ - node.appendChild(geom); │ │ │ │ │ - } else { │ │ │ │ │ - node = this.createElementNSPlus("wfs:Value", { │ │ │ │ │ - value: obj │ │ │ │ │ - }); │ │ │ │ │ - } │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "Delete": function(obj) { │ │ │ │ │ - var feature = obj.feature; │ │ │ │ │ - var options = obj.options; │ │ │ │ │ - var node = this.createElementNSPlus("wfs:Delete", { │ │ │ │ │ - attributes: { │ │ │ │ │ - handle: options && options.handle, │ │ │ │ │ - typeName: (this.featureNS ? this.featurePrefix + ":" : "") + │ │ │ │ │ - this.featureType │ │ │ │ │ - } │ │ │ │ │ - }); │ │ │ │ │ - if (this.featureNS) { │ │ │ │ │ - node.setAttribute("xmlns:" + this.featurePrefix, this.featureNS); │ │ │ │ │ - } │ │ │ │ │ - this.writeNode("ogc:Filter", new OpenLayers.Filter.FeatureId({ │ │ │ │ │ - fids: [feature.fid] │ │ │ │ │ - }), node); │ │ │ │ │ - return node; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ + return obj; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: schemaLocationAttr │ │ │ │ │ - * Generate the xsi:schemaLocation attribute value. │ │ │ │ │ - * │ │ │ │ │ + * 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: │ │ │ │ │ - * {String} The xsi:schemaLocation attribute or undefined if none. │ │ │ │ │ + * {Object} the of the layer, representing the current state. │ │ │ │ │ */ │ │ │ │ │ - schemaLocationAttr: function(options) { │ │ │ │ │ - options = OpenLayers.Util.extend({ │ │ │ │ │ - featurePrefix: this.featurePrefix, │ │ │ │ │ - schema: this.schema │ │ │ │ │ - }, options); │ │ │ │ │ - var schemaLocations = OpenLayers.Util.extend({}, this.schemaLocations); │ │ │ │ │ - if (options.schema) { │ │ │ │ │ - schemaLocations[options.featurePrefix] = options.schema; │ │ │ │ │ + getOptions: function() { │ │ │ │ │ + var options = {}; │ │ │ │ │ + for (var o in this.options) { │ │ │ │ │ + options[o] = this[o]; │ │ │ │ │ } │ │ │ │ │ - var parts = []; │ │ │ │ │ - var uri; │ │ │ │ │ - for (var key in schemaLocations) { │ │ │ │ │ - uri = this.namespaces[key]; │ │ │ │ │ - if (uri) { │ │ │ │ │ - parts.push(uri + " " + schemaLocations[key]); │ │ │ │ │ + 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" │ │ │ │ │ + }); │ │ │ │ │ } │ │ │ │ │ } │ │ │ │ │ - var value = parts.join(" ") || undefined; │ │ │ │ │ - return value; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: setFilterProperty │ │ │ │ │ - * Set the property of each spatial filter. │ │ │ │ │ - * │ │ │ │ │ + * APIMethod: addOptions │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * filter - {} │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - setFilterProperty: function(filter) { │ │ │ │ │ - if (filter.filters) { │ │ │ │ │ - for (var i = 0, len = filter.filters.length; i < len; ++i) { │ │ │ │ │ - OpenLayers.Format.WFST.v1.prototype.setFilterProperty.call(this, filter.filters[i]); │ │ │ │ │ + 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); │ │ │ │ │ } │ │ │ │ │ - } else { │ │ │ │ │ - if (filter instanceof OpenLayers.Filter.Spatial && !filter.property) { │ │ │ │ │ - // got a spatial filter without property, so set it │ │ │ │ │ - filter.property = this.geometryName; │ │ │ │ │ + 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); │ │ │ │ │ } │ │ │ │ │ } │ │ │ │ │ - }, │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.WFST.v1" │ │ │ │ │ - │ │ │ │ │ -}); │ │ │ │ │ -/* ====================================================================== │ │ │ │ │ - OpenLayers/Filter/Logical.js │ │ │ │ │ - ====================================================================== */ │ │ │ │ │ + // update our copy for clone │ │ │ │ │ + OpenLayers.Util.extend(this.options, newOptions); │ │ │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ - * full text of the license. */ │ │ │ │ │ + // 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(); │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * @requires OpenLayers/Filter.js │ │ │ │ │ - */ │ │ │ │ │ + // 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) { │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * Class: OpenLayers.Filter.Logical │ │ │ │ │ - * This class represents ogc:And, ogc:Or and ogc:Not rules. │ │ │ │ │ - * │ │ │ │ │ - * Inherits from: │ │ │ │ │ - * - │ │ │ │ │ - */ │ │ │ │ │ -OpenLayers.Filter.Logical = OpenLayers.Class(OpenLayers.Filter, { │ │ │ │ │ + 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; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: filters │ │ │ │ │ - * {Array()} Child filters for this filter. │ │ │ │ │ + * APIMethod: onMapResize │ │ │ │ │ + * This function can be implemented by subclasses │ │ │ │ │ */ │ │ │ │ │ - filters: null, │ │ │ │ │ + onMapResize: function() { │ │ │ │ │ + //this function can be implemented by subclasses │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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). │ │ │ │ │ + * APIMethod: redraw │ │ │ │ │ + * Redraws the layer. Returns true if the layer was redrawn, false if not. │ │ │ │ │ * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * options - {Object} An optional object with properties to set on the │ │ │ │ │ - * filter. │ │ │ │ │ - * │ │ │ │ │ * Returns: │ │ │ │ │ - * {} │ │ │ │ │ + * {Boolean} The layer was redrawn. │ │ │ │ │ */ │ │ │ │ │ - initialize: function(options) { │ │ │ │ │ - this.filters = []; │ │ │ │ │ - OpenLayers.Filter.prototype.initialize.apply(this, [options]); │ │ │ │ │ - }, │ │ │ │ │ + redraw: function() { │ │ │ │ │ + var redrawn = false; │ │ │ │ │ + if (this.map) { │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIMethod: destroy │ │ │ │ │ - * Remove reference to child filters. │ │ │ │ │ - */ │ │ │ │ │ - destroy: function() { │ │ │ │ │ - this.filters = null; │ │ │ │ │ - OpenLayers.Filter.prototype.destroy.apply(this); │ │ │ │ │ + // 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; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: evaluate │ │ │ │ │ - * Evaluates this filter in a specific context. │ │ │ │ │ + * Method: moveTo │ │ │ │ │ * │ │ │ │ │ * 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. │ │ │ │ │ + * bounds - {} │ │ │ │ │ + * zoomChanged - {Boolean} Tells when zoom has changed, as layers have to │ │ │ │ │ + * do some init work in that case. │ │ │ │ │ + * dragging - {Boolean} │ │ │ │ │ */ │ │ │ │ │ - evaluate: function(context) { │ │ │ │ │ - var i, len; │ │ │ │ │ - switch (this.type) { │ │ │ │ │ - case OpenLayers.Filter.Logical.AND: │ │ │ │ │ - for (i = 0, len = this.filters.length; i < len; i++) { │ │ │ │ │ - if (this.filters[i].evaluate(context) == false) { │ │ │ │ │ - return false; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - return true; │ │ │ │ │ - │ │ │ │ │ - case OpenLayers.Filter.Logical.OR: │ │ │ │ │ - for (i = 0, len = this.filters.length; i < len; i++) { │ │ │ │ │ - if (this.filters[i].evaluate(context) == true) { │ │ │ │ │ - return true; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - return false; │ │ │ │ │ - │ │ │ │ │ - case OpenLayers.Filter.Logical.NOT: │ │ │ │ │ - return (!this.filters[0].evaluate(context)); │ │ │ │ │ + moveTo: function(bounds, zoomChanged, dragging) { │ │ │ │ │ + var display = this.visibility; │ │ │ │ │ + if (!this.isBaseLayer) { │ │ │ │ │ + display = display && this.inRange; │ │ │ │ │ } │ │ │ │ │ - return undefined; │ │ │ │ │ + this.display(display); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: clone │ │ │ │ │ - * Clones this filter. │ │ │ │ │ + * 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. │ │ │ │ │ * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {} Clone of this filter. │ │ │ │ │ + * Here we take care to bring over any of the necessary default │ │ │ │ │ + * properties from the map. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * map - {} │ │ │ │ │ */ │ │ │ │ │ - clone: function() { │ │ │ │ │ - var filters = []; │ │ │ │ │ - for (var i = 0, len = this.filters.length; i < len; ++i) { │ │ │ │ │ - filters.push(this.filters[i].clone()); │ │ │ │ │ - } │ │ │ │ │ - return new OpenLayers.Filter.Logical({ │ │ │ │ │ - type: this.type, │ │ │ │ │ - filters: filters │ │ │ │ │ - }); │ │ │ │ │ - }, │ │ │ │ │ + setMap: function(map) { │ │ │ │ │ + if (this.map == null) { │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Filter.Logical" │ │ │ │ │ -}); │ │ │ │ │ + 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; │ │ │ │ │ │ │ │ │ │ -OpenLayers.Filter.Logical.AND = "&&"; │ │ │ │ │ -OpenLayers.Filter.Logical.OR = "||"; │ │ │ │ │ -OpenLayers.Filter.Logical.NOT = "!"; │ │ │ │ │ -/* ====================================================================== │ │ │ │ │ - OpenLayers/Filter/Comparison.js │ │ │ │ │ - ====================================================================== */ │ │ │ │ │ + this.projection = this.projection || this.map.projection; │ │ │ │ │ + if (typeof this.projection == "string") { │ │ │ │ │ + this.projection = new OpenLayers.Projection(this.projection); │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ - * full text of the license. */ │ │ │ │ │ + // 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; │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * @requires OpenLayers/Filter.js │ │ │ │ │ - */ │ │ │ │ │ + this.initResolutions(); │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * Class: OpenLayers.Filter.Comparison │ │ │ │ │ - * This class represents a comparison filter. │ │ │ │ │ - * │ │ │ │ │ - * Inherits from: │ │ │ │ │ - * - │ │ │ │ │ - */ │ │ │ │ │ -OpenLayers.Filter.Comparison = OpenLayers.Class(OpenLayers.Filter, { │ │ │ │ │ + if (!this.isBaseLayer) { │ │ │ │ │ + this.inRange = this.calculateInRange(); │ │ │ │ │ + var show = ((this.visibility) && (this.inRange)); │ │ │ │ │ + this.div.style.display = show ? "" : "none"; │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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, │ │ │ │ │ + // deal with gutters │ │ │ │ │ + this.setTileSize(); │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: property │ │ │ │ │ - * {String} │ │ │ │ │ - * name of the context property to compare │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - property: null, │ │ │ │ │ + afterAdd: function() {}, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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}" │ │ │ │ │ + * 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 - {} │ │ │ │ │ */ │ │ │ │ │ - value: null, │ │ │ │ │ + removeMap: function(map) { │ │ │ │ │ + //to be overridden by subclasses │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - matchCase: true, │ │ │ │ │ + getImageSize: function(bounds) { │ │ │ │ │ + return (this.imageSize || this.tileSize); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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}" │ │ │ │ │ + * APIMethod: setTileSize │ │ │ │ │ + * Set the tile size based on the map size. This also sets layer.imageSize │ │ │ │ │ + * or use by Tile.Image. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * size - {} │ │ │ │ │ */ │ │ │ │ │ - lowerBoundary: null, │ │ │ │ │ + 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)); │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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 │ │ │ │ │ + * APIMethod: getVisibility │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {} │ │ │ │ │ + * {Boolean} The layer should be displayed (if in range). │ │ │ │ │ */ │ │ │ │ │ - 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; │ │ │ │ │ - } │ │ │ │ │ + getVisibility: function() { │ │ │ │ │ + return this.visibility; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIMethod: evaluate │ │ │ │ │ - * Evaluates this filter in a specific context. │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: setVisibility │ │ │ │ │ + * Set the visibility flag for the layer and hide/show & redraw │ │ │ │ │ + * accordingly. Fire event unless otherwise specified │ │ │ │ │ * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * context - {Object} Context to use in evaluating the filter. If a vector │ │ │ │ │ - * feature is provided, the feature.attributes will be used as context. │ │ │ │ │ + * 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. │ │ │ │ │ * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Boolean} The filter applies. │ │ │ │ │ + * Parameters: │ │ │ │ │ + * visibility - {Boolean} Whether or not to display the layer (if in range) │ │ │ │ │ */ │ │ │ │ │ - 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; │ │ │ │ │ + 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"); │ │ │ │ │ } │ │ │ │ │ - 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. │ │ │ │ │ + /** │ │ │ │ │ + * 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: │ │ │ │ │ - * 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 │ │ │ │ │ + * display - {Boolean} │ │ │ │ │ */ │ │ │ │ │ - value2regex: function(wildCard, singleChar, escapeChar) { │ │ │ │ │ - if (wildCard == ".") { │ │ │ │ │ - throw new Error("'.' is an unsupported wildCard character for " + │ │ │ │ │ - "OpenLayers.Filter.Comparison"); │ │ │ │ │ + display: function(display) { │ │ │ │ │ + if (display != (this.div.style.display != "none")) { │ │ │ │ │ + this.div.style.display = (display && this.calculateInRange()) ? "block" : "none"; │ │ │ │ │ } │ │ │ │ │ - │ │ │ │ │ - │ │ │ │ │ - // 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. │ │ │ │ │ + * APIMethod: calculateInRange │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {String} A string value. │ │ │ │ │ + * {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. │ │ │ │ │ */ │ │ │ │ │ - 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, "*"); │ │ │ │ │ + calculateInRange: function() { │ │ │ │ │ + var inRange = false; │ │ │ │ │ │ │ │ │ │ - return value; │ │ │ │ │ + if (this.alwaysInRange) { │ │ │ │ │ + inRange = true; │ │ │ │ │ + } else { │ │ │ │ │ + if (this.map) { │ │ │ │ │ + var resolution = this.map.getResolution(); │ │ │ │ │ + inRange = ((resolution >= this.minResolution) && │ │ │ │ │ + (resolution <= this.maxResolution)); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + return inRange; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIMethod: clone │ │ │ │ │ - * Clones this filter. │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: setIsBaseLayer │ │ │ │ │ * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {} Clone of this filter. │ │ │ │ │ + * Parameters: │ │ │ │ │ + * isBaseLayer - {Boolean} │ │ │ │ │ */ │ │ │ │ │ - clone: function() { │ │ │ │ │ - return OpenLayers.Util.extend(new OpenLayers.Filter.Comparison(), this); │ │ │ │ │ + setIsBaseLayer: function(isBaseLayer) { │ │ │ │ │ + if (isBaseLayer != this.isBaseLayer) { │ │ │ │ │ + this.isBaseLayer = isBaseLayer; │ │ │ │ │ + if (this.map != null) { │ │ │ │ │ + this.map.events.triggerEvent("changebaselayer", { │ │ │ │ │ + layer: 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", │ │ │ │ │ + /********************************************************/ │ │ │ │ │ + /* */ │ │ │ │ │ + /* Baselayer Functions */ │ │ │ │ │ + /* */ │ │ │ │ │ + /********************************************************/ │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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. │ │ │ │ │ + /** │ │ │ │ │ + * 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() { │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIMethod: read │ │ │ │ │ - * Read and Filter doc and return an object representing the Filter. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * data - {String | DOMElement} Data to read. │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {} A filter object. │ │ │ │ │ - */ │ │ │ │ │ + // 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! │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.Filter" │ │ │ │ │ -}); │ │ │ │ │ -/* ====================================================================== │ │ │ │ │ - OpenLayers/Filter/Function.js │ │ │ │ │ - ====================================================================== */ │ │ │ │ │ + var i, len, p; │ │ │ │ │ + var props = {}, │ │ │ │ │ + alwaysInRange = true; │ │ │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ - * full text of the license. */ │ │ │ │ │ + // get resolution data from layer config │ │ │ │ │ + // (we also set alwaysInRange in the layer as appropriate) │ │ │ │ │ + for (i = 0, len = this.RESOLUTION_PROPERTIES.length; i < len; i++) { │ │ │ │ │ + p = this.RESOLUTION_PROPERTIES[i]; │ │ │ │ │ + props[p] = this.options[p]; │ │ │ │ │ + if (alwaysInRange && this.options[p]) { │ │ │ │ │ + alwaysInRange = false; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + if (this.options.alwaysInRange == null) { │ │ │ │ │ + this.alwaysInRange = alwaysInRange; │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * @requires OpenLayers/Filter.js │ │ │ │ │ - */ │ │ │ │ │ + // if we don't have resolutions then attempt to derive them from scales │ │ │ │ │ + if (props.resolutions == null) { │ │ │ │ │ + props.resolutions = this.resolutionsFromScales(props.scales); │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * 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, { │ │ │ │ │ + // if we still don't have resolutions then attempt to calculate them │ │ │ │ │ + if (props.resolutions == null) { │ │ │ │ │ + props.resolutions = this.calculateResolutions(props); │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIProperty: name │ │ │ │ │ - * {String} Name of the function. │ │ │ │ │ - */ │ │ │ │ │ - name: null, │ │ │ │ │ + // if we couldn't calculate resolutions then we look at we have │ │ │ │ │ + // in the map │ │ │ │ │ + if (props.resolutions == null) { │ │ │ │ │ + for (i = 0, len = this.RESOLUTION_PROPERTIES.length; i < len; i++) { │ │ │ │ │ + p = this.RESOLUTION_PROPERTIES[i]; │ │ │ │ │ + props[p] = this.options[p] != null ? │ │ │ │ │ + this.options[p] : this.map[p]; │ │ │ │ │ + } │ │ │ │ │ + if (props.resolutions == null) { │ │ │ │ │ + props.resolutions = this.resolutionsFromScales(props.scales); │ │ │ │ │ + } │ │ │ │ │ + if (props.resolutions == null) { │ │ │ │ │ + props.resolutions = this.calculateResolutions(props); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIProperty: params │ │ │ │ │ - * {Array( || String || Number)} Function parameters │ │ │ │ │ - * For now support only other Functions, String or Number │ │ │ │ │ - */ │ │ │ │ │ - params: null, │ │ │ │ │ + // ok, we new need to set properties in the instance │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Constructor: OpenLayers.Filter.Function │ │ │ │ │ - * Creates a filter function. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * options - {Object} An optional object with properties to set on the │ │ │ │ │ - * function. │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {} │ │ │ │ │ - */ │ │ │ │ │ + // get maxResolution from the config if it's defined there │ │ │ │ │ + var maxResolution; │ │ │ │ │ + if (this.options.maxResolution && │ │ │ │ │ + this.options.maxResolution !== "auto") { │ │ │ │ │ + maxResolution = this.options.maxResolution; │ │ │ │ │ + } │ │ │ │ │ + if (this.options.minScale) { │ │ │ │ │ + maxResolution = OpenLayers.Util.getResolutionFromScale( │ │ │ │ │ + this.options.minScale, this.units); │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Filter.Function" │ │ │ │ │ -}); │ │ │ │ │ + // get minResolution from the config if it's defined there │ │ │ │ │ + var minResolution; │ │ │ │ │ + if (this.options.minResolution && │ │ │ │ │ + this.options.minResolution !== "auto") { │ │ │ │ │ + minResolution = this.options.minResolution; │ │ │ │ │ + } │ │ │ │ │ + if (this.options.maxScale) { │ │ │ │ │ + minResolution = OpenLayers.Util.getResolutionFromScale( │ │ │ │ │ + this.options.maxScale, this.units); │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ -/* ====================================================================== │ │ │ │ │ - OpenLayers/BaseTypes/Date.js │ │ │ │ │ - ====================================================================== */ │ │ │ │ │ + if (props.resolutions) { │ │ │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ - * full text of the license. */ │ │ │ │ │ + //sort resolutions array descendingly │ │ │ │ │ + props.resolutions.sort(function(a, b) { │ │ │ │ │ + return (b - a); │ │ │ │ │ + }); │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * @requires OpenLayers/SingleFile.js │ │ │ │ │ - */ │ │ │ │ │ + // if we still don't have a maxResolution get it from the │ │ │ │ │ + // resolutions array │ │ │ │ │ + if (!maxResolution) { │ │ │ │ │ + maxResolution = props.resolutions[0]; │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * 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 = { │ │ │ │ │ + // if we still don't have a minResolution get it from the │ │ │ │ │ + // resolutions array │ │ │ │ │ + if (!minResolution) { │ │ │ │ │ + var lastIdx = props.resolutions.length - 1; │ │ │ │ │ + minResolution = props.resolutions[lastIdx]; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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)?$/, │ │ │ │ │ + this.resolutions = props.resolutions; │ │ │ │ │ + if (this.resolutions) { │ │ │ │ │ + len = this.resolutions.length; │ │ │ │ │ + this.scales = new Array(len); │ │ │ │ │ + for (i = 0; i < len; i++) { │ │ │ │ │ + this.scales[i] = OpenLayers.Util.getScaleFromResolution( │ │ │ │ │ + this.resolutions[i], this.units); │ │ │ │ │ + } │ │ │ │ │ + this.numZoomLevels = len; │ │ │ │ │ + } │ │ │ │ │ + this.minResolution = minResolution; │ │ │ │ │ + if (minResolution) { │ │ │ │ │ + this.maxScale = OpenLayers.Util.getScaleFromResolution( │ │ │ │ │ + minResolution, this.units); │ │ │ │ │ + } │ │ │ │ │ + this.maxResolution = maxResolution; │ │ │ │ │ + if (maxResolution) { │ │ │ │ │ + this.minScale = OpenLayers.Util.getScaleFromResolution( │ │ │ │ │ + maxResolution, this.units); │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * Method: resolutionsFromScales │ │ │ │ │ + * Derive resolutions from scales. │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * date - {Date} A date object. │ │ │ │ │ + * scales - {Array(Number)} Scales │ │ │ │ │ * │ │ │ │ │ - * 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. │ │ │ │ │ + * Returns │ │ │ │ │ + * {Array(Number)} Resolutions │ │ │ │ │ */ │ │ │ │ │ - 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; │ │ │ │ │ - }; │ │ │ │ │ + resolutionsFromScales: function(scales) { │ │ │ │ │ + if (scales == null) { │ │ │ │ │ + return; │ │ │ │ │ } │ │ │ │ │ - │ │ │ │ │ - })(), │ │ │ │ │ + var resolutions, i, len; │ │ │ │ │ + len = scales.length; │ │ │ │ │ + resolutions = new Array(len); │ │ │ │ │ + for (i = 0; i < len; i++) { │ │ │ │ │ + resolutions[i] = OpenLayers.Util.getResolutionFromScale( │ │ │ │ │ + scales[i], this.units); │ │ │ │ │ + } │ │ │ │ │ + return resolutions; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * Method: calculateResolutions │ │ │ │ │ + * Calculate resolutions based on the provided properties. │ │ │ │ │ * │ │ │ │ │ * 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"). │ │ │ │ │ + * props - {Object} Properties │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {Date} A date object. If the string could not be parsed, an invalid │ │ │ │ │ - * date is returned (i.e. isNaN(date.getTime())). │ │ │ │ │ + * {Array({Number})} Array of resolutions. │ │ │ │ │ */ │ │ │ │ │ - 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"); │ │ │ │ │ + calculateResolutions: function(props) { │ │ │ │ │ + │ │ │ │ │ + var viewSize, wRes, hRes; │ │ │ │ │ + │ │ │ │ │ + // determine maxResolution │ │ │ │ │ + var maxResolution = props.maxResolution; │ │ │ │ │ + if (props.minScale != null) { │ │ │ │ │ + maxResolution = │ │ │ │ │ + OpenLayers.Util.getResolutionFromScale(props.minScale, │ │ │ │ │ + this.units); │ │ │ │ │ + } else if (maxResolution == "auto" && this.maxExtent != null) { │ │ │ │ │ + viewSize = this.map.getSize(); │ │ │ │ │ + wRes = this.maxExtent.getWidth() / viewSize.w; │ │ │ │ │ + hRes = this.maxExtent.getHeight() / viewSize.h; │ │ │ │ │ + maxResolution = Math.max(wRes, hRes); │ │ │ │ │ } │ │ │ │ │ - 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 │ │ │ │ │ - */ │ │ │ │ │ + // determine minResolution │ │ │ │ │ + var minResolution = props.minResolution; │ │ │ │ │ + if (props.maxScale != null) { │ │ │ │ │ + minResolution = │ │ │ │ │ + OpenLayers.Util.getResolutionFromScale(props.maxScale, │ │ │ │ │ + this.units); │ │ │ │ │ + } else if (props.minResolution == "auto" && this.minExtent != null) { │ │ │ │ │ + viewSize = this.map.getSize(); │ │ │ │ │ + wRes = this.minExtent.getWidth() / viewSize.w; │ │ │ │ │ + hRes = this.minExtent.getHeight() / viewSize.h; │ │ │ │ │ + minResolution = Math.max(wRes, hRes); │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * Class: OpenLayers.Format.Filter.v1 │ │ │ │ │ - * Superclass for Filter version 1 parsers. │ │ │ │ │ - * │ │ │ │ │ - * Inherits from: │ │ │ │ │ - * - │ │ │ │ │ - */ │ │ │ │ │ -OpenLayers.Format.Filter.v1 = OpenLayers.Class(OpenLayers.Format.XML, { │ │ │ │ │ + if (typeof maxResolution !== "number" && │ │ │ │ │ + typeof minResolution !== "number" && │ │ │ │ │ + this.maxExtent != null) { │ │ │ │ │ + // maxResolution for default grid sets assumes that at zoom │ │ │ │ │ + // level zero, the whole world fits on one tile. │ │ │ │ │ + var tileSize = this.map.getTileSize(); │ │ │ │ │ + maxResolution = Math.max( │ │ │ │ │ + this.maxExtent.getWidth() / tileSize.w, │ │ │ │ │ + this.maxExtent.getHeight() / tileSize.h │ │ │ │ │ + ); │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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" │ │ │ │ │ - }, │ │ │ │ │ + // determine numZoomLevels │ │ │ │ │ + var maxZoomLevel = props.maxZoomLevel; │ │ │ │ │ + var numZoomLevels = props.numZoomLevels; │ │ │ │ │ + if (typeof minResolution === "number" && │ │ │ │ │ + typeof maxResolution === "number" && numZoomLevels === undefined) { │ │ │ │ │ + var ratio = maxResolution / minResolution; │ │ │ │ │ + numZoomLevels = Math.floor(Math.log(ratio) / Math.log(2)) + 1; │ │ │ │ │ + } else if (numZoomLevels === undefined && maxZoomLevel != null) { │ │ │ │ │ + numZoomLevels = maxZoomLevel + 1; │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: defaultPrefix │ │ │ │ │ - */ │ │ │ │ │ - defaultPrefix: "ogc", │ │ │ │ │ + // are we able to calculate resolutions? │ │ │ │ │ + if (typeof numZoomLevels !== "number" || numZoomLevels <= 0 || │ │ │ │ │ + (typeof maxResolution !== "number" && │ │ │ │ │ + typeof minResolution !== "number")) { │ │ │ │ │ + return; │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: schemaLocation │ │ │ │ │ - * {String} Schema location for a particular minor version. │ │ │ │ │ - */ │ │ │ │ │ - schemaLocation: null, │ │ │ │ │ + // now we have numZoomLevels and at least one of maxResolution │ │ │ │ │ + // or minResolution, we can populate the resolutions array │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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]); │ │ │ │ │ + var resolutions = new Array(numZoomLevels); │ │ │ │ │ + var base = 2; │ │ │ │ │ + if (typeof minResolution == "number" && │ │ │ │ │ + typeof maxResolution == "number") { │ │ │ │ │ + // if maxResolution and minResolution are set, we calculate │ │ │ │ │ + // the base for exponential scaling that starts at │ │ │ │ │ + // maxResolution and ends at minResolution in numZoomLevels │ │ │ │ │ + // steps. │ │ │ │ │ + base = Math.pow( │ │ │ │ │ + (maxResolution / minResolution), │ │ │ │ │ + (1 / (numZoomLevels - 1)) │ │ │ │ │ + ); │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + var i; │ │ │ │ │ + if (typeof maxResolution === "number") { │ │ │ │ │ + for (i = 0; i < numZoomLevels; i++) { │ │ │ │ │ + resolutions[i] = maxResolution / Math.pow(base, i); │ │ │ │ │ + } │ │ │ │ │ + } else { │ │ │ │ │ + for (i = 0; i < numZoomLevels; i++) { │ │ │ │ │ + resolutions[numZoomLevels - 1 - i] = │ │ │ │ │ + minResolution * Math.pow(base, i); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + return resolutions; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: read │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * data - {DOMElement} A Filter document element. │ │ │ │ │ - * │ │ │ │ │ + * APIMethod: getResolution │ │ │ │ │ + * │ │ │ │ │ * Returns: │ │ │ │ │ - * {} A filter object. │ │ │ │ │ + * {Float} The currently selected resolution of the map, taken from the │ │ │ │ │ + * resolutions array, indexed by current zoom level. │ │ │ │ │ */ │ │ │ │ │ - read: function(data) { │ │ │ │ │ - var obj = {}; │ │ │ │ │ - this.readers.ogc["Filter"].apply(this, [data, obj]); │ │ │ │ │ - return obj.filter; │ │ │ │ │ + getResolution: function() { │ │ │ │ │ + var zoom = this.map.getZoom(); │ │ │ │ │ + return this.getResolutionForZoom(zoom); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: getExtent │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {} 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(); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: readSpatial │ │ │ │ │ - * │ │ │ │ │ - * Read a {} filter. │ │ │ │ │ + * APIMethod: getZoomForExtent │ │ │ │ │ * │ │ │ │ │ * 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. │ │ │ │ │ + * 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: │ │ │ │ │ - * {} The created filter. │ │ │ │ │ + * {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. │ │ │ │ │ */ │ │ │ │ │ - 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); │ │ │ │ │ + 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: encodeLiteral │ │ │ │ │ - * Generates the string representation of a value for use in │ │ │ │ │ - * elements. The default encoder writes Date values as ISO 8601 │ │ │ │ │ - * strings. │ │ │ │ │ - * │ │ │ │ │ + * APIMethod: getResolutionForZoom │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * value - {Object} Literal value to encode │ │ │ │ │ - * │ │ │ │ │ + * zoom - {Float} │ │ │ │ │ + * │ │ │ │ │ * Returns: │ │ │ │ │ - * {String} String representation of the provided value. │ │ │ │ │ + * {Float} A suitable resolution for the specified zoom. │ │ │ │ │ */ │ │ │ │ │ - encodeLiteral: function(value) { │ │ │ │ │ - if (value instanceof Date) { │ │ │ │ │ - value = OpenLayers.Date.toISOString(value); │ │ │ │ │ + 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 value; │ │ │ │ │ + return resolution; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: writeOgcExpression │ │ │ │ │ - * Limited support for writing OGC expressions. Currently it supports │ │ │ │ │ - * ( || String || Number) │ │ │ │ │ - * │ │ │ │ │ + * APIMethod: getZoomForResolution │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * value - ( || String || Number) │ │ │ │ │ - * node - {DOMElement} A parent DOM element │ │ │ │ │ - * │ │ │ │ │ + * 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: │ │ │ │ │ - * {DOMElement} Updated node element. │ │ │ │ │ + * {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. │ │ │ │ │ */ │ │ │ │ │ - writeOgcExpression: function(value, node) { │ │ │ │ │ - if (value instanceof OpenLayers.Filter.Function) { │ │ │ │ │ - this.writeNode("Function", value, node); │ │ │ │ │ + getZoomForResolution: function(resolution, closest) { │ │ │ │ │ + var zoom, i, len; │ │ │ │ │ + if (this.map.fractionalZoom) { │ │ │ │ │ + var lowZoom = 0; │ │ │ │ │ + var highZoom = this.resolutions.length - 1; │ │ │ │ │ + var highRes = this.resolutions[lowZoom]; │ │ │ │ │ + var lowRes = this.resolutions[highZoom]; │ │ │ │ │ + var res; │ │ │ │ │ + for (i = 0, len = this.resolutions.length; i < len; ++i) { │ │ │ │ │ + res = this.resolutions[i]; │ │ │ │ │ + if (res >= resolution) { │ │ │ │ │ + highRes = res; │ │ │ │ │ + lowZoom = i; │ │ │ │ │ + } │ │ │ │ │ + if (res <= resolution) { │ │ │ │ │ + lowRes = res; │ │ │ │ │ + highZoom = i; │ │ │ │ │ + break; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + var dRes = highRes - lowRes; │ │ │ │ │ + if (dRes > 0) { │ │ │ │ │ + zoom = lowZoom + ((highRes - resolution) / dRes); │ │ │ │ │ + } else { │ │ │ │ │ + zoom = lowZoom; │ │ │ │ │ + } │ │ │ │ │ } else { │ │ │ │ │ - this.writeNode("Literal", value, node); │ │ │ │ │ + var diff; │ │ │ │ │ + var minDiff = Number.POSITIVE_INFINITY; │ │ │ │ │ + for (i = 0, len = this.resolutions.length; i < len; i++) { │ │ │ │ │ + if (closest) { │ │ │ │ │ + diff = Math.abs(this.resolutions[i] - resolution); │ │ │ │ │ + if (diff > minDiff) { │ │ │ │ │ + break; │ │ │ │ │ + } │ │ │ │ │ + minDiff = diff; │ │ │ │ │ + } else { │ │ │ │ │ + if (this.resolutions[i] < resolution) { │ │ │ │ │ + break; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + zoom = Math.max(0, i - 1); │ │ │ │ │ } │ │ │ │ │ - return node; │ │ │ │ │ + return zoom; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: write │ │ │ │ │ - * │ │ │ │ │ + * APIMethod: getLonLatFromViewPortPx │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * filter - {} A filter object. │ │ │ │ │ + * viewPortPx - {|Object} An OpenLayers.Pixel or │ │ │ │ │ + * an object with a 'x' │ │ │ │ │ + * and 'y' properties. │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {DOMElement} An ogc:Filter element. │ │ │ │ │ + * {} An OpenLayers.LonLat which is the passed-in │ │ │ │ │ + * view port , translated into lon/lat by the layer. │ │ │ │ │ */ │ │ │ │ │ - write: function(filter) { │ │ │ │ │ - return this.writers.ogc["Filter"].apply(this, [filter]); │ │ │ │ │ + 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; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - writers: { │ │ │ │ │ - "ogc": { │ │ │ │ │ - "Filter": function(filter) { │ │ │ │ │ - var node = this.createElementNSPlus("ogc:Filter"); │ │ │ │ │ - this.writeNode(this.getFilterType(filter), filter, node); │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "_featureIds": function(filter) { │ │ │ │ │ - var node = this.createDocumentFragment(); │ │ │ │ │ - for (var i = 0, ii = filter.fids.length; i < ii; ++i) { │ │ │ │ │ - this.writeNode("ogc:FeatureId", filter.fids[i], node); │ │ │ │ │ - } │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "FeatureId": function(fid) { │ │ │ │ │ - return this.createElementNSPlus("ogc:FeatureId", { │ │ │ │ │ - attributes: { │ │ │ │ │ - fid: fid │ │ │ │ │ - } │ │ │ │ │ - }); │ │ │ │ │ - }, │ │ │ │ │ - "And": function(filter) { │ │ │ │ │ - var node = this.createElementNSPlus("ogc:And"); │ │ │ │ │ - var childFilter; │ │ │ │ │ - for (var i = 0, ii = filter.filters.length; i < ii; ++i) { │ │ │ │ │ - childFilter = filter.filters[i]; │ │ │ │ │ - this.writeNode( │ │ │ │ │ - this.getFilterType(childFilter), childFilter, node │ │ │ │ │ - ); │ │ │ │ │ - } │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "Or": function(filter) { │ │ │ │ │ - var node = this.createElementNSPlus("ogc:Or"); │ │ │ │ │ - var childFilter; │ │ │ │ │ - for (var i = 0, ii = filter.filters.length; i < ii; ++i) { │ │ │ │ │ - childFilter = filter.filters[i]; │ │ │ │ │ - this.writeNode( │ │ │ │ │ - this.getFilterType(childFilter), childFilter, node │ │ │ │ │ - ); │ │ │ │ │ + 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; │ │ │ │ │ } │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "Not": function(filter) { │ │ │ │ │ - var node = this.createElementNSPlus("ogc:Not"); │ │ │ │ │ - var childFilter = filter.filters[0]; │ │ │ │ │ - this.writeNode( │ │ │ │ │ - this.getFilterType(childFilter), childFilter, node │ │ │ │ │ - ); │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "PropertyIsLessThan": function(filter) { │ │ │ │ │ - var node = this.createElementNSPlus("ogc:PropertyIsLessThan"); │ │ │ │ │ - // no ogc:expression handling for PropertyName for now │ │ │ │ │ - this.writeNode("PropertyName", filter, node); │ │ │ │ │ - // handle Literals or Functions for now │ │ │ │ │ - this.writeOgcExpression(filter.value, node); │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "PropertyIsGreaterThan": function(filter) { │ │ │ │ │ - var node = this.createElementNSPlus("ogc:PropertyIsGreaterThan"); │ │ │ │ │ - // no ogc:expression handling for PropertyName for now │ │ │ │ │ - this.writeNode("PropertyName", filter, node); │ │ │ │ │ - // handle Literals or Functions for now │ │ │ │ │ - this.writeOgcExpression(filter.value, node); │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "PropertyIsLessThanOrEqualTo": function(filter) { │ │ │ │ │ - var node = this.createElementNSPlus("ogc:PropertyIsLessThanOrEqualTo"); │ │ │ │ │ - // no ogc:expression handling for PropertyName for now │ │ │ │ │ - this.writeNode("PropertyName", filter, node); │ │ │ │ │ - // handle Literals or Functions for now │ │ │ │ │ - this.writeOgcExpression(filter.value, node); │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "PropertyIsGreaterThanOrEqualTo": function(filter) { │ │ │ │ │ - var node = this.createElementNSPlus("ogc:PropertyIsGreaterThanOrEqualTo"); │ │ │ │ │ - // no ogc:expression handling for PropertyName for now │ │ │ │ │ - this.writeNode("PropertyName", filter, node); │ │ │ │ │ - // handle Literals or Functions for now │ │ │ │ │ - this.writeOgcExpression(filter.value, node); │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "PropertyIsBetween": function(filter) { │ │ │ │ │ - var node = this.createElementNSPlus("ogc:PropertyIsBetween"); │ │ │ │ │ - // no ogc:expression handling for PropertyName for now │ │ │ │ │ - this.writeNode("PropertyName", filter, node); │ │ │ │ │ - this.writeNode("LowerBoundary", filter, node); │ │ │ │ │ - this.writeNode("UpperBoundary", filter, node); │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "PropertyName": function(filter) { │ │ │ │ │ - // no ogc:expression handling for now │ │ │ │ │ - return this.createElementNSPlus("ogc:PropertyName", { │ │ │ │ │ - value: filter.property │ │ │ │ │ - }); │ │ │ │ │ - }, │ │ │ │ │ - "Literal": function(value) { │ │ │ │ │ - var encode = this.encodeLiteral || │ │ │ │ │ - OpenLayers.Format.Filter.v1.prototype.encodeLiteral; │ │ │ │ │ - return this.createElementNSPlus("ogc:Literal", { │ │ │ │ │ - value: encode(value) │ │ │ │ │ - }); │ │ │ │ │ - }, │ │ │ │ │ - "LowerBoundary": function(filter) { │ │ │ │ │ - // handle Literals or Functions for now │ │ │ │ │ - var node = this.createElementNSPlus("ogc:LowerBoundary"); │ │ │ │ │ - this.writeOgcExpression(filter.lowerBoundary, node); │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "UpperBoundary": function(filter) { │ │ │ │ │ - // handle Literals or Functions for now │ │ │ │ │ - var node = this.createElementNSPlus("ogc:UpperBoundary"); │ │ │ │ │ - this.writeNode("Literal", filter.upperBoundary, node); │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "INTERSECTS": function(filter) { │ │ │ │ │ - return this.writeSpatial(filter, "Intersects"); │ │ │ │ │ - }, │ │ │ │ │ - "WITHIN": function(filter) { │ │ │ │ │ - return this.writeSpatial(filter, "Within"); │ │ │ │ │ - }, │ │ │ │ │ - "CONTAINS": function(filter) { │ │ │ │ │ - return this.writeSpatial(filter, "Contains"); │ │ │ │ │ - }, │ │ │ │ │ - "DWITHIN": function(filter) { │ │ │ │ │ - var node = this.writeSpatial(filter, "DWithin"); │ │ │ │ │ - this.writeNode("Distance", filter, node); │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "Distance": function(filter) { │ │ │ │ │ - return this.createElementNSPlus("ogc:Distance", { │ │ │ │ │ - attributes: { │ │ │ │ │ - units: filter.distanceUnits │ │ │ │ │ - }, │ │ │ │ │ - value: filter.distance │ │ │ │ │ - }); │ │ │ │ │ - }, │ │ │ │ │ - "Function": function(filter) { │ │ │ │ │ - var node = this.createElementNSPlus("ogc:Function", { │ │ │ │ │ - attributes: { │ │ │ │ │ - name: filter.name │ │ │ │ │ - } │ │ │ │ │ + OpenLayers.Util.modifyDOMElement(element, null, null, null, │ │ │ │ │ + null, null, null, opacity); │ │ │ │ │ + } │ │ │ │ │ + if (this.map != null) { │ │ │ │ │ + this.map.events.triggerEvent("changelayer", { │ │ │ │ │ + layer: this, │ │ │ │ │ + property: "opacity" │ │ │ │ │ }); │ │ │ │ │ - var params = filter.params; │ │ │ │ │ - for (var i = 0, len = params.length; i < len; i++) { │ │ │ │ │ - this.writeOgcExpression(params[i], node); │ │ │ │ │ - } │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "PropertyIsNull": function(filter) { │ │ │ │ │ - var node = this.createElementNSPlus("ogc:PropertyIsNull"); │ │ │ │ │ - this.writeNode("PropertyName", filter, node); │ │ │ │ │ - return node; │ │ │ │ │ } │ │ │ │ │ } │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: getFilterType │ │ │ │ │ + * Method: getZIndex │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Integer} the z-index of this layer │ │ │ │ │ */ │ │ │ │ │ - getFilterType: function(filter) { │ │ │ │ │ - var filterType = this.filterMap[filter.type]; │ │ │ │ │ - if (!filterType) { │ │ │ │ │ - throw "Filter writing not supported for rule type: " + filter.type; │ │ │ │ │ - } │ │ │ │ │ - return filterType; │ │ │ │ │ + getZIndex: function() { │ │ │ │ │ + return this.div.style.zIndex; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: filterMap │ │ │ │ │ - * {Object} Contains a member for each filter type. Values are node names │ │ │ │ │ - * for corresponding OGC Filter child elements. │ │ │ │ │ + * Method: setZIndex │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * zIndex - {Integer} │ │ │ │ │ */ │ │ │ │ │ - filterMap: { │ │ │ │ │ - "&&": "And", │ │ │ │ │ - "||": "Or", │ │ │ │ │ - "!": "Not", │ │ │ │ │ - "==": "PropertyIsEqualTo", │ │ │ │ │ - "!=": "PropertyIsNotEqualTo", │ │ │ │ │ - "<": "PropertyIsLessThan", │ │ │ │ │ - ">": "PropertyIsGreaterThan", │ │ │ │ │ - "<=": "PropertyIsLessThanOrEqualTo", │ │ │ │ │ - ">=": "PropertyIsGreaterThanOrEqualTo", │ │ │ │ │ - "..": "PropertyIsBetween", │ │ │ │ │ - "~": "PropertyIsLike", │ │ │ │ │ - "NULL": "PropertyIsNull", │ │ │ │ │ - "BBOX": "BBOX", │ │ │ │ │ - "DWITHIN": "DWITHIN", │ │ │ │ │ - "WITHIN": "WITHIN", │ │ │ │ │ - "CONTAINS": "CONTAINS", │ │ │ │ │ - "INTERSECTS": "INTERSECTS", │ │ │ │ │ - "FID": "_featureIds" │ │ │ │ │ + setZIndex: function(zIndex) { │ │ │ │ │ + this.div.style.zIndex = zIndex; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.Filter.v1" │ │ │ │ │ + /** │ │ │ │ │ + * 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/Format/GML.js │ │ │ │ │ + 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/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 │ │ │ │ │ + * @requires OpenLayers/Layer.js │ │ │ │ │ */ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Class: OpenLayers.Format.GML │ │ │ │ │ - * Read/Write GML. Create a new instance with the │ │ │ │ │ - * constructor. Supports the GML simple features profile. │ │ │ │ │ + * Class: OpenLayers.Layer.HTTPRequest │ │ │ │ │ * │ │ │ │ │ - * Inherits from: │ │ │ │ │ - * - │ │ │ │ │ + * 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", │ │ │ │ │ +OpenLayers.Layer.HTTPRequest = OpenLayers.Class(OpenLayers.Layer, { │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: collectionName │ │ │ │ │ - * {String} Name of featureCollection element. │ │ │ │ │ - */ │ │ │ │ │ - collectionName: "FeatureCollection", │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * APIProperty: gmlns │ │ │ │ │ - * {String} GML Namespace. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - gmlns: "http://www.opengis.net/gml", │ │ │ │ │ + URL_HASH_FACTOR: (Math.sqrt(5) - 1) / 2, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIProperty: extractAttributes │ │ │ │ │ - * {Boolean} Extract attributes from GML. │ │ │ │ │ + /** │ │ │ │ │ + * Property: url │ │ │ │ │ + * {Array(String) or String} This is either an array of url strings or │ │ │ │ │ + * a single url string. │ │ │ │ │ */ │ │ │ │ │ - extractAttributes: true, │ │ │ │ │ + url: 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. │ │ │ │ │ + /** │ │ │ │ │ + * Property: params │ │ │ │ │ + * {Object} Hashtable of key/value parameters │ │ │ │ │ */ │ │ │ │ │ - xy: true, │ │ │ │ │ + params: null, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Constructor: OpenLayers.Format.GML │ │ │ │ │ - * Create a new parser for GML. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ │ - * this instance. │ │ │ │ │ + /** │ │ │ │ │ + * 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. │ │ │ │ │ + * │ │ │ │ │ */ │ │ │ │ │ - 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]); │ │ │ │ │ - }, │ │ │ │ │ + reproject: false, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: read │ │ │ │ │ - * Read data from a string, and return a list of features. │ │ │ │ │ + * Constructor: OpenLayers.Layer.HTTPRequest │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * data - {String} or {DOMElement} data to read/parse. │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Array()} An array of features. │ │ │ │ │ + * name - {String} │ │ │ │ │ + * url - {Array(String) or String} │ │ │ │ │ + * params - {Object} │ │ │ │ │ + * options - {Object} Hashtable of extra options to tag onto the layer │ │ │ │ │ */ │ │ │ │ │ - read: function(data) { │ │ │ │ │ - if (typeof data == "string") { │ │ │ │ │ - data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); │ │ │ │ │ - } │ │ │ │ │ - var featureNodes = this.getElementsByTagNameNS(data.documentElement, │ │ │ │ │ - this.gmlns, │ │ │ │ │ - this.featureName); │ │ │ │ │ - var features = []; │ │ │ │ │ - for (var i = 0; i < featureNodes.length; i++) { │ │ │ │ │ - var feature = this.parseFeature(featureNodes[i]); │ │ │ │ │ - if (feature) { │ │ │ │ │ - features.push(feature); │ │ │ │ │ - } │ │ │ │ │ + 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); │ │ │ │ │ } │ │ │ │ │ - return features; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: parseFeature │ │ │ │ │ - * This function is the core of the GML parsing code in OpenLayers. │ │ │ │ │ - * It creates the geometries that are then attached to the returned │ │ │ │ │ - * feature, and calls parseAttributes() to get attribute data out. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * node - {DOMElement} A GML feature node. │ │ │ │ │ + * APIMethod: destroy │ │ │ │ │ */ │ │ │ │ │ - parseFeature: function(node) { │ │ │ │ │ - // only accept one geometry per feature - look for highest "order" │ │ │ │ │ - var order = ["MultiPolygon", "Polygon", │ │ │ │ │ - "MultiLineString", "LineString", │ │ │ │ │ - "MultiPoint", "Point", "Envelope" │ │ │ │ │ - ]; │ │ │ │ │ - // FIXME: In case we parse a feature with no geometry, but boundedBy an Envelope, │ │ │ │ │ - // this code creates a geometry derived from the Envelope. This is not correct. │ │ │ │ │ - var type, nodeList, geometry, parser; │ │ │ │ │ - for (var i = 0; i < order.length; ++i) { │ │ │ │ │ - type = order[i]; │ │ │ │ │ - nodeList = this.getElementsByTagNameNS(node, this.gmlns, type); │ │ │ │ │ - if (nodeList.length > 0) { │ │ │ │ │ - // only deal with first geometry of this type │ │ │ │ │ - parser = this.parseGeometry[type.toLowerCase()]; │ │ │ │ │ - if (parser) { │ │ │ │ │ - geometry = parser.apply(this, [nodeList[0]]); │ │ │ │ │ - if (this.internalProjection && this.externalProjection) { │ │ │ │ │ - geometry.transform(this.externalProjection, │ │ │ │ │ - this.internalProjection); │ │ │ │ │ - } │ │ │ │ │ - } else { │ │ │ │ │ - throw new TypeError("Unsupported geometry type: " + type); │ │ │ │ │ - } │ │ │ │ │ - // stop looking for different geometry types │ │ │ │ │ - break; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - │ │ │ │ │ - var bounds; │ │ │ │ │ - var boxNodes = this.getElementsByTagNameNS(node, this.gmlns, "Box"); │ │ │ │ │ - for (i = 0; i < boxNodes.length; ++i) { │ │ │ │ │ - var boxNode = boxNodes[i]; │ │ │ │ │ - var box = this.parseGeometry["box"].apply(this, [boxNode]); │ │ │ │ │ - var parentNode = boxNode.parentNode; │ │ │ │ │ - var parentName = parentNode.localName || │ │ │ │ │ - parentNode.nodeName.split(":").pop(); │ │ │ │ │ - if (parentName === "boundedBy") { │ │ │ │ │ - bounds = box; │ │ │ │ │ - } else { │ │ │ │ │ - geometry = box.toGeometry(); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - │ │ │ │ │ - // construct feature (optionally with attributes) │ │ │ │ │ - var attributes; │ │ │ │ │ - if (this.extractAttributes) { │ │ │ │ │ - attributes = this.parseAttributes(node); │ │ │ │ │ - } │ │ │ │ │ - var feature = new OpenLayers.Feature.Vector(geometry, attributes); │ │ │ │ │ - feature.bounds = bounds; │ │ │ │ │ - │ │ │ │ │ - feature.gml = { │ │ │ │ │ - featureType: node.firstChild.nodeName.split(":")[1], │ │ │ │ │ - featureNS: node.firstChild.namespaceURI, │ │ │ │ │ - featureNSPrefix: node.firstChild.prefix │ │ │ │ │ - }; │ │ │ │ │ - │ │ │ │ │ - // assign fid - this can come from a "fid" or "id" attribute │ │ │ │ │ - var childNode = node.firstChild; │ │ │ │ │ - var fid; │ │ │ │ │ - while (childNode) { │ │ │ │ │ - if (childNode.nodeType == 1) { │ │ │ │ │ - fid = childNode.getAttribute("fid") || │ │ │ │ │ - childNode.getAttribute("id"); │ │ │ │ │ - if (fid) { │ │ │ │ │ - break; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - childNode = childNode.nextSibling; │ │ │ │ │ - } │ │ │ │ │ - feature.fid = fid; │ │ │ │ │ - return feature; │ │ │ │ │ + destroy: function() { │ │ │ │ │ + this.url = null; │ │ │ │ │ + this.params = null; │ │ │ │ │ + OpenLayers.Layer.prototype.destroy.apply(this, arguments); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: parseGeometry │ │ │ │ │ - * Properties of this object are the functions that parse geometries based │ │ │ │ │ - * on their type. │ │ │ │ │ + * APIMethod: clone │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * obj - {Object} │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {} An exact clone of this │ │ │ │ │ + * │ │ │ │ │ */ │ │ │ │ │ - parseGeometry: { │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Method: parseGeometry.point │ │ │ │ │ - * Given a GML node representing a point geometry, create an OpenLayers │ │ │ │ │ - * point geometry. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * node - {DOMElement} A GML node. │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {} 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 < nodeList.length; ++i) { │ │ │ │ │ - point = this.parseGeometry.point.apply(this, [nodeList[i]]); │ │ │ │ │ - if (point) { │ │ │ │ │ - components.push(point); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - return new OpenLayers.Geometry.MultiPoint(components); │ │ │ │ │ - }, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Method: parseGeometry.linestring │ │ │ │ │ - * Given a GML node representing a linestring geometry, create an │ │ │ │ │ - * OpenLayers linestring geometry. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * node - {DOMElement} A GML node. │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {} 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 < coords.length / dim; ++i) { │ │ │ │ │ - j = i * dim; │ │ │ │ │ - x = coords[j]; │ │ │ │ │ - y = coords[j + 1]; │ │ │ │ │ - z = (dim == 2) ? null : coords[j + 2]; │ │ │ │ │ - if (this.xy) { │ │ │ │ │ - points.push(new OpenLayers.Geometry.Point(x, y, z)); │ │ │ │ │ - } else { │ │ │ │ │ - points.push(new OpenLayers.Geometry.Point(y, x, z)); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - │ │ │ │ │ - // look for │ │ │ │ │ - if (coords.length == 0) { │ │ │ │ │ - nodeList = this.getElementsByTagNameNS(node, this.gmlns, │ │ │ │ │ - "coordinates"); │ │ │ │ │ - if (nodeList.length > 0) { │ │ │ │ │ - coordString = this.getChildValue(nodeList[0]); │ │ │ │ │ - coordString = coordString.replace(this.regExes.trimSpace, │ │ │ │ │ - ""); │ │ │ │ │ - coordString = coordString.replace(this.regExes.trimComma, │ │ │ │ │ - ","); │ │ │ │ │ - var pointList = coordString.split(this.regExes.splitSpace); │ │ │ │ │ - for (var i = 0; i < pointList.length; ++i) { │ │ │ │ │ - coords = pointList[i].split(","); │ │ │ │ │ - if (coords.length == 2) { │ │ │ │ │ - coords[2] = null; │ │ │ │ │ - } │ │ │ │ │ - if (this.xy) { │ │ │ │ │ - points.push(new OpenLayers.Geometry.Point(coords[0], │ │ │ │ │ - coords[1], │ │ │ │ │ - coords[2])); │ │ │ │ │ - } else { │ │ │ │ │ - points.push(new OpenLayers.Geometry.Point(coords[1], │ │ │ │ │ - coords[0], │ │ │ │ │ - coords[2])); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - │ │ │ │ │ - var line = null; │ │ │ │ │ - if (points.length != 0) { │ │ │ │ │ - if (ring) { │ │ │ │ │ - line = new OpenLayers.Geometry.LinearRing(points); │ │ │ │ │ - } else { │ │ │ │ │ - line = new OpenLayers.Geometry.LineString(points); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - return line; │ │ │ │ │ - }, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Method: parseGeometry.multilinestring │ │ │ │ │ - * Given a GML node representing a multilinestring geometry, create an │ │ │ │ │ - * OpenLayers multilinestring geometry. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * node - {DOMElement} A GML node. │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {} A multilinestring geometry. │ │ │ │ │ - */ │ │ │ │ │ - multilinestring: function(node) { │ │ │ │ │ - var nodeList = this.getElementsByTagNameNS(node, this.gmlns, │ │ │ │ │ - "LineString"); │ │ │ │ │ - var components = []; │ │ │ │ │ - if (nodeList.length > 0) { │ │ │ │ │ - var line; │ │ │ │ │ - for (var i = 0; i < nodeList.length; ++i) { │ │ │ │ │ - line = this.parseGeometry.linestring.apply(this, │ │ │ │ │ - [nodeList[i]]); │ │ │ │ │ - if (line) { │ │ │ │ │ - components.push(line); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - return new OpenLayers.Geometry.MultiLineString(components); │ │ │ │ │ - }, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Method: parseGeometry.polygon │ │ │ │ │ - * Given a GML node representing a polygon geometry, create an │ │ │ │ │ - * OpenLayers polygon geometry. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * node - {DOMElement} A GML node. │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {} A polygon geometry. │ │ │ │ │ - */ │ │ │ │ │ - polygon: function(node) { │ │ │ │ │ - var nodeList = this.getElementsByTagNameNS(node, this.gmlns, │ │ │ │ │ - "LinearRing"); │ │ │ │ │ - var components = []; │ │ │ │ │ - if (nodeList.length > 0) { │ │ │ │ │ - // this assumes exterior ring first, inner rings after │ │ │ │ │ - var ring; │ │ │ │ │ - for (var i = 0; i < nodeList.length; ++i) { │ │ │ │ │ - ring = this.parseGeometry.linestring.apply(this, │ │ │ │ │ - [nodeList[i], true]); │ │ │ │ │ - if (ring) { │ │ │ │ │ - components.push(ring); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - return new OpenLayers.Geometry.Polygon(components); │ │ │ │ │ - }, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Method: parseGeometry.multipolygon │ │ │ │ │ - * Given a GML node representing a multipolygon geometry, create an │ │ │ │ │ - * OpenLayers multipolygon geometry. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * node - {DOMElement} A GML node. │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {} A multipolygon geometry. │ │ │ │ │ - */ │ │ │ │ │ - multipolygon: function(node) { │ │ │ │ │ - var nodeList = this.getElementsByTagNameNS(node, this.gmlns, │ │ │ │ │ - "Polygon"); │ │ │ │ │ - var components = []; │ │ │ │ │ - if (nodeList.length > 0) { │ │ │ │ │ - var polygon; │ │ │ │ │ - for (var i = 0; i < nodeList.length; ++i) { │ │ │ │ │ - polygon = this.parseGeometry.polygon.apply(this, │ │ │ │ │ - [nodeList[i]]); │ │ │ │ │ - if (polygon) { │ │ │ │ │ - components.push(polygon); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - return new OpenLayers.Geometry.MultiPolygon(components); │ │ │ │ │ - }, │ │ │ │ │ - │ │ │ │ │ - envelope: function(node) { │ │ │ │ │ - var components = []; │ │ │ │ │ - var coordString; │ │ │ │ │ - var envelope; │ │ │ │ │ - │ │ │ │ │ - var lpoint = this.getElementsByTagNameNS(node, this.gmlns, "lowerCorner"); │ │ │ │ │ - if (lpoint.length > 0) { │ │ │ │ │ - var coords = []; │ │ │ │ │ - │ │ │ │ │ - if (lpoint.length > 0) { │ │ │ │ │ - coordString = lpoint[0].firstChild.nodeValue; │ │ │ │ │ - coordString = coordString.replace(this.regExes.trimSpace, ""); │ │ │ │ │ - coords = coordString.split(this.regExes.splitSpace); │ │ │ │ │ - } │ │ │ │ │ - │ │ │ │ │ - if (coords.length == 2) { │ │ │ │ │ - coords[2] = null; │ │ │ │ │ - } │ │ │ │ │ - if (this.xy) { │ │ │ │ │ - var lowerPoint = new OpenLayers.Geometry.Point(coords[0], coords[1], coords[2]); │ │ │ │ │ - } else { │ │ │ │ │ - var lowerPoint = new OpenLayers.Geometry.Point(coords[1], coords[0], coords[2]); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - │ │ │ │ │ - var upoint = this.getElementsByTagNameNS(node, this.gmlns, "upperCorner"); │ │ │ │ │ - if (upoint.length > 0) { │ │ │ │ │ - var coords = []; │ │ │ │ │ - │ │ │ │ │ - if (upoint.length > 0) { │ │ │ │ │ - coordString = upoint[0].firstChild.nodeValue; │ │ │ │ │ - coordString = coordString.replace(this.regExes.trimSpace, ""); │ │ │ │ │ - coords = coordString.split(this.regExes.splitSpace); │ │ │ │ │ - } │ │ │ │ │ - │ │ │ │ │ - if (coords.length == 2) { │ │ │ │ │ - coords[2] = null; │ │ │ │ │ - } │ │ │ │ │ - if (this.xy) { │ │ │ │ │ - var upperPoint = new OpenLayers.Geometry.Point(coords[0], coords[1], coords[2]); │ │ │ │ │ - } else { │ │ │ │ │ - var upperPoint = new OpenLayers.Geometry.Point(coords[1], coords[0], coords[2]); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ + clone: function(obj) { │ │ │ │ │ │ │ │ │ │ - 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)); │ │ │ │ │ + if (obj == null) { │ │ │ │ │ + obj = new OpenLayers.Layer.HTTPRequest(this.name, │ │ │ │ │ + this.url, │ │ │ │ │ + this.params, │ │ │ │ │ + this.getOptions()); │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ - var ring = new OpenLayers.Geometry.LinearRing(components); │ │ │ │ │ - envelope = new OpenLayers.Geometry.Polygon([ring]); │ │ │ │ │ - } │ │ │ │ │ - return envelope; │ │ │ │ │ - }, │ │ │ │ │ + //get all additions from superclasses │ │ │ │ │ + obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]); │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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])); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ + // copy/set any non-init, non-simple values here │ │ │ │ │ │ │ │ │ │ + return obj; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: parseAttributes │ │ │ │ │ - * │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: setUrl │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * node - {DOMElement} │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Object} An attributes object. │ │ │ │ │ + * newUrl - {String} │ │ │ │ │ */ │ │ │ │ │ - parseAttributes: function(node) { │ │ │ │ │ - var attributes = {}; │ │ │ │ │ - // assume attributes are children of the first type 1 child │ │ │ │ │ - var childNode = node.firstChild; │ │ │ │ │ - var children, i, child, grandchildren, grandchild, name, value; │ │ │ │ │ - while (childNode) { │ │ │ │ │ - if (childNode.nodeType == 1) { │ │ │ │ │ - // attributes are type 1 children with one type 3 child │ │ │ │ │ - children = childNode.childNodes; │ │ │ │ │ - for (i = 0; i < children.length; ++i) { │ │ │ │ │ - child = children[i]; │ │ │ │ │ - if (child.nodeType == 1) { │ │ │ │ │ - grandchildren = child.childNodes; │ │ │ │ │ - if (grandchildren.length == 1) { │ │ │ │ │ - grandchild = grandchildren[0]; │ │ │ │ │ - if (grandchild.nodeType == 3 || │ │ │ │ │ - grandchild.nodeType == 4) { │ │ │ │ │ - name = (child.prefix) ? │ │ │ │ │ - child.nodeName.split(":")[1] : │ │ │ │ │ - child.nodeName; │ │ │ │ │ - value = grandchild.nodeValue.replace( │ │ │ │ │ - this.regExes.trimSpace, ""); │ │ │ │ │ - attributes[name] = value; │ │ │ │ │ - } │ │ │ │ │ - } else { │ │ │ │ │ - // If child has no childNodes (grandchildren), │ │ │ │ │ - // set an attribute with null value. │ │ │ │ │ - // e.g. becomes │ │ │ │ │ - // {fieldname: null} │ │ │ │ │ - attributes[child.nodeName.split(":").pop()] = null; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - break; │ │ │ │ │ - } │ │ │ │ │ - childNode = childNode.nextSibling; │ │ │ │ │ - } │ │ │ │ │ - return attributes; │ │ │ │ │ + setUrl: function(newUrl) { │ │ │ │ │ + this.url = newUrl; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: write │ │ │ │ │ - * Generate a GML document string given a list of features. │ │ │ │ │ + * APIMethod: mergeNewParams │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * features - {Array()} List of features to │ │ │ │ │ - * serialize into a string. │ │ │ │ │ + * newParams - {Object} │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {String} A string representing the GML document. │ │ │ │ │ + * redrawn: {Boolean} whether the layer was actually redrawn. │ │ │ │ │ */ │ │ │ │ │ - write: function(features) { │ │ │ │ │ - if (!(OpenLayers.Util.isArray(features))) { │ │ │ │ │ - features = [features]; │ │ │ │ │ - } │ │ │ │ │ - var gml = this.createElementNS("http://www.opengis.net/wfs", │ │ │ │ │ - "wfs:" + this.collectionName); │ │ │ │ │ - for (var i = 0; i < features.length; i++) { │ │ │ │ │ - gml.appendChild(this.createFeatureXML(features[i])); │ │ │ │ │ + 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 OpenLayers.Format.XML.prototype.write.apply(this, [gml]); │ │ │ │ │ + return ret; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: createFeatureXML │ │ │ │ │ - * Accept an OpenLayers.Feature.Vector, and build a GML node for it. │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: redraw │ │ │ │ │ + * Redraws the layer. Returns true if the layer was redrawn, false if not. │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * feature - {} The feature to be built as GML. │ │ │ │ │ + * force - {Boolean} Force redraw by adding random parameter. │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {DOMElement} A node reprensting the feature in GML. │ │ │ │ │ + * {Boolean} The layer was redrawn. │ │ │ │ │ */ │ │ │ │ │ - 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); │ │ │ │ │ + redraw: function(force) { │ │ │ │ │ + if (force) { │ │ │ │ │ + return this.mergeNewParams({ │ │ │ │ │ + "_olSalt": Math.random() │ │ │ │ │ + }); │ │ │ │ │ + } else { │ │ │ │ │ + return OpenLayers.Layer.prototype.redraw.apply(this, []); │ │ │ │ │ } │ │ │ │ │ - featureNode.appendChild(featureContainer); │ │ │ │ │ - return featureNode; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: buildGeometryNode │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - buildGeometryNode: function(geometry) { │ │ │ │ │ - if (this.externalProjection && this.internalProjection) { │ │ │ │ │ - geometry = geometry.clone(); │ │ │ │ │ - geometry.transform(this.internalProjection, │ │ │ │ │ - this.externalProjection); │ │ │ │ │ + selectUrl: function(paramString, urls) { │ │ │ │ │ + var product = 1; │ │ │ │ │ + for (var i = 0, len = paramString.length; i < len; i++) { │ │ │ │ │ + product *= paramString.charCodeAt(i) * this.URL_HASH_FACTOR; │ │ │ │ │ + product -= Math.floor(product); │ │ │ │ │ } │ │ │ │ │ - var className = geometry.CLASS_NAME; │ │ │ │ │ - var type = className.substring(className.lastIndexOf(".") + 1); │ │ │ │ │ - var builder = this.buildGeometry[type.toLowerCase()]; │ │ │ │ │ - return builder.apply(this, [geometry]); │ │ │ │ │ + return urls[Math.floor(product * urls.length)]; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: buildGeometry │ │ │ │ │ - * Object containing methods to do the actual geometry node building │ │ │ │ │ - * based on geometry type. │ │ │ │ │ + /** │ │ │ │ │ + * Method: getFullRequestString │ │ │ │ │ + * Combine url with layer's params and these newParams. │ │ │ │ │ + * │ │ │ │ │ + * does checking on the serverPath variable, allowing for cases when it │ │ │ │ │ + * is supplied with trailing ? or &, as well as cases where not. │ │ │ │ │ + * │ │ │ │ │ + * return in formatted string like this: │ │ │ │ │ + * "server?key1=value1&key2=value2&key3=value3" │ │ │ │ │ + * │ │ │ │ │ + * WARNING: The altUrl parameter is deprecated and will be removed in 3.0. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * newParams - {Object} │ │ │ │ │ + * altUrl - {String} Use this as the url instead of the layer's url │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {String} │ │ │ │ │ */ │ │ │ │ │ - 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 < points.length; i++) { │ │ │ │ │ - pointMember = this.createElementNS(this.gmlns, │ │ │ │ │ - "gml:pointMember"); │ │ │ │ │ - pointGeom = this.buildGeometry.point.apply(this, │ │ │ │ │ - [points[i]]); │ │ │ │ │ - pointMember.appendChild(pointGeom); │ │ │ │ │ - gml.appendChild(pointMember); │ │ │ │ │ - } │ │ │ │ │ - return gml; │ │ │ │ │ - }, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Method: buildGeometry.linestring │ │ │ │ │ - * Given an OpenLayers linestring geometry, create a GML linestring. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * geometry - {} 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 < lines.length; ++i) { │ │ │ │ │ - lineMember = this.createElementNS(this.gmlns, │ │ │ │ │ - "gml:lineStringMember"); │ │ │ │ │ - lineGeom = this.buildGeometry.linestring.apply(this, │ │ │ │ │ - [lines[i]]); │ │ │ │ │ - lineMember.appendChild(lineGeom); │ │ │ │ │ - gml.appendChild(lineMember); │ │ │ │ │ - } │ │ │ │ │ - return gml; │ │ │ │ │ - }, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Method: buildGeometry.linearring │ │ │ │ │ - * Given an OpenLayers linearring geometry, create a GML linearring. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * geometry - {} 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 < rings.length; ++i) { │ │ │ │ │ - type = (i == 0) ? "outerBoundaryIs" : "innerBoundaryIs"; │ │ │ │ │ - ringMember = this.createElementNS(this.gmlns, │ │ │ │ │ - "gml:" + type); │ │ │ │ │ - ringGeom = this.buildGeometry.linearring.apply(this, │ │ │ │ │ - [rings[i]]); │ │ │ │ │ - ringMember.appendChild(ringGeom); │ │ │ │ │ - gml.appendChild(ringMember); │ │ │ │ │ - } │ │ │ │ │ - return gml; │ │ │ │ │ - }, │ │ │ │ │ + getFullRequestString: function(newParams, altUrl) { │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: buildGeometry.multipolygon │ │ │ │ │ - * Given an OpenLayers multipolygon geometry, create a GML multipolygon. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * geometry - {} A multipolygon │ │ │ │ │ - * geometry. │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {DOMElement} A GML multipolygon node. │ │ │ │ │ - */ │ │ │ │ │ - multipolygon: function(geometry) { │ │ │ │ │ - var gml = this.createElementNS(this.gmlns, "gml:MultiPolygon"); │ │ │ │ │ - var polys = geometry.components; │ │ │ │ │ - var polyMember, polyGeom; │ │ │ │ │ - for (var i = 0; i < polys.length; ++i) { │ │ │ │ │ - polyMember = this.createElementNS(this.gmlns, │ │ │ │ │ - "gml:polygonMember"); │ │ │ │ │ - polyGeom = this.buildGeometry.polygon.apply(this, │ │ │ │ │ - [polys[i]]); │ │ │ │ │ - polyMember.appendChild(polyGeom); │ │ │ │ │ - gml.appendChild(polyMember); │ │ │ │ │ - } │ │ │ │ │ - return gml; │ │ │ │ │ + // if not altUrl passed in, use layer's url │ │ │ │ │ + var url = altUrl || this.url; │ │ │ │ │ │ │ │ │ │ - }, │ │ │ │ │ + // create a new params hashtable with all the layer params and the │ │ │ │ │ + // new params together. then convert to string │ │ │ │ │ + var allParams = OpenLayers.Util.extend({}, this.params); │ │ │ │ │ + allParams = OpenLayers.Util.extend(allParams, newParams); │ │ │ │ │ + var paramsString = OpenLayers.Util.getParameterString(allParams); │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: buildGeometry.bounds │ │ │ │ │ - * Given an OpenLayers bounds, create a GML box. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * bounds - {} A bounds object. │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {DOMElement} A GML box node. │ │ │ │ │ - */ │ │ │ │ │ - bounds: function(bounds) { │ │ │ │ │ - var gml = this.createElementNS(this.gmlns, "gml:Box"); │ │ │ │ │ - gml.appendChild(this.buildCoordinatesNode(bounds)); │ │ │ │ │ - return gml; │ │ │ │ │ + // if url is not a string, it should be an array of strings, │ │ │ │ │ + // in which case we will deterministically select one of them in │ │ │ │ │ + // order to evenly distribute requests to different urls. │ │ │ │ │ + // │ │ │ │ │ + if (OpenLayers.Util.isArray(url)) { │ │ │ │ │ + url = this.selectUrl(paramsString, url); │ │ │ │ │ } │ │ │ │ │ - }, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * 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 < points.length; i++) { │ │ │ │ │ - parts.push(points[i].x + "," + points[i].y); │ │ │ │ │ + // ignore parameters that are already in the url search string │ │ │ │ │ + var urlParams = │ │ │ │ │ + OpenLayers.Util.upperCaseObject(OpenLayers.Util.getParameters(url)); │ │ │ │ │ + for (var key in allParams) { │ │ │ │ │ + if (key.toUpperCase() in urlParams) { │ │ │ │ │ + delete allParams[key]; │ │ │ │ │ } │ │ │ │ │ } │ │ │ │ │ + paramsString = OpenLayers.Util.getParameterString(allParams); │ │ │ │ │ │ │ │ │ │ - var txtNode = this.createTextNode(parts.join(" ")); │ │ │ │ │ - coordinatesNode.appendChild(txtNode); │ │ │ │ │ - │ │ │ │ │ - return coordinatesNode; │ │ │ │ │ + return OpenLayers.Util.urlAppend(url, paramsString); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.GML" │ │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.HTTPRequest" │ │ │ │ │ }); │ │ │ │ │ /* ====================================================================== │ │ │ │ │ - OpenLayers/Format/GML/Base.js │ │ │ │ │ + OpenLayers/Tile.js │ │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * @requires OpenLayers/Format/XML.js │ │ │ │ │ - * @requires OpenLayers/Format/GML.js │ │ │ │ │ - */ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Though required in the full build, if the GML format is excluded, we set │ │ │ │ │ - * the namespace here. │ │ │ │ │ + * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ │ + * @requires OpenLayers/Util.js │ │ │ │ │ */ │ │ │ │ │ -if (!OpenLayers.Format.GML) { │ │ │ │ │ - OpenLayers.Format.GML = {}; │ │ │ │ │ -} │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Class: OpenLayers.Format.GML.Base │ │ │ │ │ - * Superclass for GML parsers. │ │ │ │ │ - * │ │ │ │ │ - * Inherits from: │ │ │ │ │ - * - │ │ │ │ │ + * Class: OpenLayers.Tile │ │ │ │ │ + * This is a class designed to designate a single tile, however │ │ │ │ │ + * it is explicitly designed to do relatively little. Tiles store │ │ │ │ │ + * information about themselves -- such as the URL that they are related │ │ │ │ │ + * to, and their size - but do not add themselves to the layer div │ │ │ │ │ + * automatically, for example. Create a new tile with the │ │ │ │ │ + * constructor, or a subclass. │ │ │ │ │ + * │ │ │ │ │ + * TBD 3.0 - remove reference to url in above paragraph │ │ │ │ │ + * │ │ │ │ │ */ │ │ │ │ │ -OpenLayers.Format.GML.Base = OpenLayers.Class(OpenLayers.Format.XML, { │ │ │ │ │ +OpenLayers.Tile = OpenLayers.Class({ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: namespaces │ │ │ │ │ - * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - 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 │ │ │ │ │ - }, │ │ │ │ │ + events: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: defaultPrefix │ │ │ │ │ + * 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) │ │ │ │ │ */ │ │ │ │ │ - defaultPrefix: "gml", │ │ │ │ │ + eventListeners: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: schemaLocation │ │ │ │ │ - * {String} Schema location for a particular minor version. │ │ │ │ │ + * Property: id │ │ │ │ │ + * {String} null │ │ │ │ │ */ │ │ │ │ │ - schemaLocation: null, │ │ │ │ │ + id: null, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIProperty: featureType │ │ │ │ │ - * {Array(String) or String} The local (without prefix) feature typeName(s). │ │ │ │ │ + /** │ │ │ │ │ + * Property: layer │ │ │ │ │ + * {} layer the tile is attached to │ │ │ │ │ */ │ │ │ │ │ - featureType: null, │ │ │ │ │ + layer: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: featureNS │ │ │ │ │ - * {String} The feature namespace. Must be set in the options at │ │ │ │ │ - * construction. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - featureNS: null, │ │ │ │ │ + url: null, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIProperty: geometry │ │ │ │ │ - * {String} Name of geometry element. Defaults to "geometry". If null, it │ │ │ │ │ - * will be set on when the first geometry is parsed. │ │ │ │ │ + /** │ │ │ │ │ + * APIProperty: bounds │ │ │ │ │ + * {} null │ │ │ │ │ */ │ │ │ │ │ - geometryName: "geometry", │ │ │ │ │ + bounds: null, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIProperty: extractAttributes │ │ │ │ │ - * {Boolean} Extract attributes from GML. Default is true. │ │ │ │ │ + /** │ │ │ │ │ + * Property: size │ │ │ │ │ + * {} null │ │ │ │ │ */ │ │ │ │ │ - extractAttributes: true, │ │ │ │ │ + size: null, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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. │ │ │ │ │ + /** │ │ │ │ │ + * Property: position │ │ │ │ │ + * {} Top Left pixel of the tile │ │ │ │ │ */ │ │ │ │ │ - srsName: null, │ │ │ │ │ + position: 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. │ │ │ │ │ + * Property: isLoading │ │ │ │ │ + * {Boolean} Is the tile loading? │ │ │ │ │ */ │ │ │ │ │ - xy: true, │ │ │ │ │ + isLoading: false, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: geometryTypes │ │ │ │ │ - * {Object} Maps OpenLayers geometry class names to GML element names. │ │ │ │ │ - * Use before accessing this property. │ │ │ │ │ + /** 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. │ │ │ │ │ */ │ │ │ │ │ - geometryTypes: null, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: singleFeatureType │ │ │ │ │ - * {Boolean} True if there is only 1 featureType, and not an array │ │ │ │ │ - * of featuretypes. │ │ │ │ │ + /** │ │ │ │ │ + * Constructor: OpenLayers.Tile │ │ │ │ │ + * Constructor for a new instance. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * layer - {} layer that the tile will go in. │ │ │ │ │ + * position - {} │ │ │ │ │ + * bounds - {} │ │ │ │ │ + * url - {} │ │ │ │ │ + * size - {} │ │ │ │ │ + * options - {Object} │ │ │ │ │ */ │ │ │ │ │ - singleFeatureType: null, │ │ │ │ │ + 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(); │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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. │ │ │ │ │ - */ │ │ │ │ │ + //give the tile a unique id based on its BBOX. │ │ │ │ │ + this.id = OpenLayers.Util.createUniqueID("Tile_"); │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: regExes │ │ │ │ │ - * Compiled regular expressions for manipulating strings. │ │ │ │ │ - */ │ │ │ │ │ - regExes: { │ │ │ │ │ - trimSpace: (/^\s*|\s*$/g), │ │ │ │ │ - removeSpace: (/\s*/g), │ │ │ │ │ - splitSpace: (/\s+/), │ │ │ │ │ - trimComma: (/\s*,\s*/g), │ │ │ │ │ - featureMember: (/^(.*:)?featureMembers?$/) │ │ │ │ │ + OpenLayers.Util.extend(this, options); │ │ │ │ │ + │ │ │ │ │ + this.events = new OpenLayers.Events(this); │ │ │ │ │ + if (this.eventListeners instanceof Object) { │ │ │ │ │ + this.events.on(this.eventListeners); │ │ │ │ │ + } │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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). │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - initialize: function(options) { │ │ │ │ │ - OpenLayers.Format.XML.prototype.initialize.apply(this, [options]); │ │ │ │ │ - this.setGeometryTypes(); │ │ │ │ │ - if (options && options.featureNS) { │ │ │ │ │ - this.setNamespace("feature", options.featureNS); │ │ │ │ │ + unload: function() { │ │ │ │ │ + if (this.isLoading) { │ │ │ │ │ + this.isLoading = false; │ │ │ │ │ + this.events.triggerEvent("unload"); │ │ │ │ │ } │ │ │ │ │ - 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. │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: destroy │ │ │ │ │ + * Nullify references to prevent circular references and memory leaks. │ │ │ │ │ */ │ │ │ │ │ - read: function(data) { │ │ │ │ │ - if (typeof data == "string") { │ │ │ │ │ - data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); │ │ │ │ │ - } │ │ │ │ │ - if (data && data.nodeType == 9) { │ │ │ │ │ - data = data.documentElement; │ │ │ │ │ - } │ │ │ │ │ - var features = []; │ │ │ │ │ - this.readNode(data, { │ │ │ │ │ - features: features │ │ │ │ │ - }, true); │ │ │ │ │ - if (features.length == 0) { │ │ │ │ │ - // look for gml:featureMember elements │ │ │ │ │ - var elements = this.getElementsByTagNameNS( │ │ │ │ │ - data, this.namespaces.gml, "featureMember" │ │ │ │ │ - ); │ │ │ │ │ - if (elements.length) { │ │ │ │ │ - for (var i = 0, len = elements.length; i < len; ++i) { │ │ │ │ │ - this.readNode(elements[i], { │ │ │ │ │ - features: features │ │ │ │ │ - }, true); │ │ │ │ │ - } │ │ │ │ │ - } else { │ │ │ │ │ - // look for gml:featureMembers elements (this is v3, but does no harm here) │ │ │ │ │ - var elements = this.getElementsByTagNameNS( │ │ │ │ │ - data, this.namespaces.gml, "featureMembers" │ │ │ │ │ - ); │ │ │ │ │ - if (elements.length) { │ │ │ │ │ - // there can be only one │ │ │ │ │ - this.readNode(elements[0], { │ │ │ │ │ - features: features │ │ │ │ │ - }, true); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ + destroy: function() { │ │ │ │ │ + this.layer = null; │ │ │ │ │ + this.bounds = null; │ │ │ │ │ + this.size = null; │ │ │ │ │ + this.position = null; │ │ │ │ │ + │ │ │ │ │ + if (this.eventListeners) { │ │ │ │ │ + this.events.un(this.eventListeners); │ │ │ │ │ } │ │ │ │ │ - return features; │ │ │ │ │ + this.events.destroy(); │ │ │ │ │ + this.eventListeners = null; │ │ │ │ │ + this.events = null; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * 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: │ │ │ │ │ - * 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. │ │ │ │ │ - * │ │ │ │ │ + * 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: │ │ │ │ │ - * {Object} The input object, modified (or a new one if none was provided). │ │ │ │ │ + * {Boolean} Whether or not the tile should actually be drawn. Returns null │ │ │ │ │ + * if a beforedraw listener returned false. │ │ │ │ │ */ │ │ │ │ │ - readNode: function(node, obj, first) { │ │ │ │ │ - // on subsequent calls of format.read(), we want to reset auto- │ │ │ │ │ - // configured properties and auto-configure again. │ │ │ │ │ - if (first === true && this.autoConfig === true) { │ │ │ │ │ - this.featureType = null; │ │ │ │ │ - delete this.namespaceAlias[this.featureNS]; │ │ │ │ │ - delete this.namespaces["feature"]; │ │ │ │ │ - this.featureNS = null; │ │ │ │ │ + draw: function(force) { │ │ │ │ │ + if (!force) { │ │ │ │ │ + //clear tile's contents and mark as not drawn │ │ │ │ │ + this.clear(); │ │ │ │ │ } │ │ │ │ │ - // featureType auto-configuration │ │ │ │ │ - if (!this.featureNS && (!(node.prefix in this.namespaces) && │ │ │ │ │ - node.parentNode.namespaceURI == this.namespaces["gml"] && │ │ │ │ │ - this.regExes.featureMember.test(node.parentNode.nodeName))) { │ │ │ │ │ - this.featureType = node.nodeName.split(":").pop(); │ │ │ │ │ - this.setNamespace("feature", node.namespaceURI); │ │ │ │ │ - this.featureNS = node.namespaceURI; │ │ │ │ │ - this.autoConfig = true; │ │ │ │ │ + var draw = this.shouldDraw(); │ │ │ │ │ + if (draw && !force && this.events.triggerEvent("beforedraw") === false) { │ │ │ │ │ + draw = null; │ │ │ │ │ } │ │ │ │ │ - return OpenLayers.Format.XML.prototype.readNode.apply(this, [node, obj]); │ │ │ │ │ + return draw; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - readers: { │ │ │ │ │ - "gml": { │ │ │ │ │ - "_inherit": function(node, obj, container) { │ │ │ │ │ - // To be implemented by version specific parsers │ │ │ │ │ - }, │ │ │ │ │ - "featureMember": function(node, obj) { │ │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ │ - }, │ │ │ │ │ - "featureMembers": function(node, obj) { │ │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ │ - }, │ │ │ │ │ - "name": function(node, obj) { │ │ │ │ │ - obj.name = this.getChildValue(node); │ │ │ │ │ - }, │ │ │ │ │ - "boundedBy": function(node, obj) { │ │ │ │ │ - var container = {}; │ │ │ │ │ - this.readChildNodes(node, container); │ │ │ │ │ - if (container.components && container.components.length > 0) { │ │ │ │ │ - obj.bounds = container.components[0]; │ │ │ │ │ - } │ │ │ │ │ - }, │ │ │ │ │ - "Point": function(node, container) { │ │ │ │ │ - var obj = { │ │ │ │ │ - points: [] │ │ │ │ │ - }; │ │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ │ - if (!container.components) { │ │ │ │ │ - container.components = []; │ │ │ │ │ - } │ │ │ │ │ - container.components.push(obj.points[0]); │ │ │ │ │ - }, │ │ │ │ │ - "coordinates": function(node, obj) { │ │ │ │ │ - var str = this.getChildValue(node).replace( │ │ │ │ │ - this.regExes.trimSpace, "" │ │ │ │ │ - ); │ │ │ │ │ - str = str.replace(this.regExes.trimComma, ","); │ │ │ │ │ - var pointList = str.split(this.regExes.splitSpace); │ │ │ │ │ - var coords; │ │ │ │ │ - var numPoints = pointList.length; │ │ │ │ │ - var points = new Array(numPoints); │ │ │ │ │ - for (var i = 0; i < numPoints; ++i) { │ │ │ │ │ - coords = pointList[i].split(","); │ │ │ │ │ - if (this.xy) { │ │ │ │ │ - points[i] = new OpenLayers.Geometry.Point( │ │ │ │ │ - coords[0], coords[1], coords[2] │ │ │ │ │ - ); │ │ │ │ │ - } else { │ │ │ │ │ - points[i] = new OpenLayers.Geometry.Point( │ │ │ │ │ - coords[1], coords[0], coords[2] │ │ │ │ │ - ); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - obj.points = points; │ │ │ │ │ - }, │ │ │ │ │ - "coord": function(node, obj) { │ │ │ │ │ - var coord = {}; │ │ │ │ │ - this.readChildNodes(node, coord); │ │ │ │ │ - if (!obj.points) { │ │ │ │ │ - obj.points = []; │ │ │ │ │ - } │ │ │ │ │ - obj.points.push(new OpenLayers.Geometry.Point( │ │ │ │ │ - coord.x, coord.y, coord.z │ │ │ │ │ - )); │ │ │ │ │ - }, │ │ │ │ │ - "X": function(node, coord) { │ │ │ │ │ - coord.x = this.getChildValue(node); │ │ │ │ │ - }, │ │ │ │ │ - "Y": function(node, coord) { │ │ │ │ │ - coord.y = this.getChildValue(node); │ │ │ │ │ - }, │ │ │ │ │ - "Z": function(node, coord) { │ │ │ │ │ - coord.z = this.getChildValue(node); │ │ │ │ │ - }, │ │ │ │ │ - "MultiPoint": function(node, container) { │ │ │ │ │ - var obj = { │ │ │ │ │ - components: [] │ │ │ │ │ - }; │ │ │ │ │ - this.readers.gml._inherit.apply(this, [node, obj, container]); │ │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ │ - container.components = [ │ │ │ │ │ - new OpenLayers.Geometry.MultiPoint(obj.components) │ │ │ │ │ - ]; │ │ │ │ │ - }, │ │ │ │ │ - "pointMember": function(node, obj) { │ │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ │ - }, │ │ │ │ │ - "LineString": function(node, container) { │ │ │ │ │ - var obj = {}; │ │ │ │ │ - this.readers.gml._inherit.apply(this, [node, obj, container]); │ │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ │ - if (!container.components) { │ │ │ │ │ - container.components = []; │ │ │ │ │ - } │ │ │ │ │ - container.components.push( │ │ │ │ │ - new OpenLayers.Geometry.LineString(obj.points) │ │ │ │ │ - ); │ │ │ │ │ - }, │ │ │ │ │ - "MultiLineString": function(node, container) { │ │ │ │ │ - var obj = { │ │ │ │ │ - components: [] │ │ │ │ │ - }; │ │ │ │ │ - this.readers.gml._inherit.apply(this, [node, obj, container]); │ │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ │ - container.components = [ │ │ │ │ │ - new OpenLayers.Geometry.MultiLineString(obj.components) │ │ │ │ │ - ]; │ │ │ │ │ - }, │ │ │ │ │ - "lineStringMember": function(node, obj) { │ │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ │ - }, │ │ │ │ │ - "Polygon": function(node, container) { │ │ │ │ │ - var obj = { │ │ │ │ │ - outer: null, │ │ │ │ │ - inner: [] │ │ │ │ │ - }; │ │ │ │ │ - this.readers.gml._inherit.apply(this, [node, obj, container]); │ │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ │ - obj.inner.unshift(obj.outer); │ │ │ │ │ - if (!container.components) { │ │ │ │ │ - container.components = []; │ │ │ │ │ - } │ │ │ │ │ - container.components.push( │ │ │ │ │ - new OpenLayers.Geometry.Polygon(obj.inner) │ │ │ │ │ - ); │ │ │ │ │ - }, │ │ │ │ │ - "LinearRing": function(node, obj) { │ │ │ │ │ - var container = {}; │ │ │ │ │ - this.readers.gml._inherit.apply(this, [node, container]); │ │ │ │ │ - this.readChildNodes(node, container); │ │ │ │ │ - obj.components = [new OpenLayers.Geometry.LinearRing( │ │ │ │ │ - container.points │ │ │ │ │ - )]; │ │ │ │ │ - }, │ │ │ │ │ - "MultiPolygon": function(node, container) { │ │ │ │ │ - var obj = { │ │ │ │ │ - components: [] │ │ │ │ │ - }; │ │ │ │ │ - this.readers.gml._inherit.apply(this, [node, obj, container]); │ │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ │ - container.components = [ │ │ │ │ │ - new OpenLayers.Geometry.MultiPolygon(obj.components) │ │ │ │ │ - ]; │ │ │ │ │ - }, │ │ │ │ │ - "polygonMember": function(node, obj) { │ │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ │ - }, │ │ │ │ │ - "GeometryCollection": function(node, container) { │ │ │ │ │ - var obj = { │ │ │ │ │ - components: [] │ │ │ │ │ - }; │ │ │ │ │ - this.readers.gml._inherit.apply(this, [node, obj, container]); │ │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ │ - container.components = [ │ │ │ │ │ - new OpenLayers.Geometry.Collection(obj.components) │ │ │ │ │ - ]; │ │ │ │ │ - }, │ │ │ │ │ - "geometryMember": function(node, obj) { │ │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ │ - } │ │ │ │ │ - }, │ │ │ │ │ - "feature": { │ │ │ │ │ - "*": function(node, obj) { │ │ │ │ │ - // The node can either be named like the featureType, or it │ │ │ │ │ - // can be a child of the feature:featureType. Children can be │ │ │ │ │ - // geometry or attributes. │ │ │ │ │ - var name; │ │ │ │ │ - var local = node.localName || node.nodeName.split(":").pop(); │ │ │ │ │ - // Since an attribute can have the same name as the feature type │ │ │ │ │ - // we only want to read the node as a feature if the parent │ │ │ │ │ - // node can have feature nodes as children. In this case, the │ │ │ │ │ - // obj.features property is set. │ │ │ │ │ - if (obj.features) { │ │ │ │ │ - if (!this.singleFeatureType && │ │ │ │ │ - (OpenLayers.Util.indexOf(this.featureType, local) !== -1)) { │ │ │ │ │ - name = "_typeName"; │ │ │ │ │ - } else if (local === this.featureType) { │ │ │ │ │ - name = "_typeName"; │ │ │ │ │ - } │ │ │ │ │ - } else { │ │ │ │ │ - // Assume attribute elements have one child node and that the child │ │ │ │ │ - // is a text node. Otherwise assume it is a geometry node. │ │ │ │ │ - if (node.childNodes.length == 0 || │ │ │ │ │ - (node.childNodes.length == 1 && node.firstChild.nodeType == 3)) { │ │ │ │ │ - if (this.extractAttributes) { │ │ │ │ │ - name = "_attribute"; │ │ │ │ │ - } │ │ │ │ │ - } else { │ │ │ │ │ - name = "_geometry"; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - if (name) { │ │ │ │ │ - this.readers.feature[name].apply(this, [node, obj]); │ │ │ │ │ - } │ │ │ │ │ - }, │ │ │ │ │ - "_typeName": function(node, obj) { │ │ │ │ │ - var container = { │ │ │ │ │ - components: [], │ │ │ │ │ - attributes: {} │ │ │ │ │ - }; │ │ │ │ │ - this.readChildNodes(node, container); │ │ │ │ │ - // look for common gml namespaced elements │ │ │ │ │ - if (container.name) { │ │ │ │ │ - container.attributes.name = container.name; │ │ │ │ │ - } │ │ │ │ │ - var feature = new OpenLayers.Feature.Vector( │ │ │ │ │ - container.components[0], container.attributes │ │ │ │ │ - ); │ │ │ │ │ - if (!this.singleFeatureType) { │ │ │ │ │ - feature.type = node.nodeName.split(":").pop(); │ │ │ │ │ - feature.namespace = node.namespaceURI; │ │ │ │ │ - } │ │ │ │ │ - var fid = node.getAttribute("fid") || │ │ │ │ │ - this.getAttributeNS(node, this.namespaces["gml"], "id"); │ │ │ │ │ - if (fid) { │ │ │ │ │ - feature.fid = fid; │ │ │ │ │ - } │ │ │ │ │ - if (this.internalProjection && this.externalProjection && │ │ │ │ │ - feature.geometry) { │ │ │ │ │ - feature.geometry.transform( │ │ │ │ │ - this.externalProjection, this.internalProjection │ │ │ │ │ - ); │ │ │ │ │ - } │ │ │ │ │ - if (container.bounds) { │ │ │ │ │ - feature.bounds = container.bounds; │ │ │ │ │ - } │ │ │ │ │ - obj.features.push(feature); │ │ │ │ │ - }, │ │ │ │ │ - "_geometry": function(node, obj) { │ │ │ │ │ - if (!this.geometryName) { │ │ │ │ │ - this.geometryName = node.nodeName.split(":").pop(); │ │ │ │ │ - } │ │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ │ - }, │ │ │ │ │ - "_attribute": function(node, obj) { │ │ │ │ │ - var local = node.localName || node.nodeName.split(":").pop(); │ │ │ │ │ - var value = this.getChildValue(node); │ │ │ │ │ - obj.attributes[local] = value; │ │ │ │ │ - } │ │ │ │ │ - }, │ │ │ │ │ - "wfs": { │ │ │ │ │ - "FeatureCollection": function(node, obj) { │ │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ │ + 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: write │ │ │ │ │ + * Method: setBounds │ │ │ │ │ + * Sets the bounds on this instance │ │ │ │ │ * │ │ │ │ │ * 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. │ │ │ │ │ + * bounds {} │ │ │ │ │ */ │ │ │ │ │ - write: function(features) { │ │ │ │ │ - var name; │ │ │ │ │ - if (OpenLayers.Util.isArray(features)) { │ │ │ │ │ - name = "featureMembers"; │ │ │ │ │ - } else { │ │ │ │ │ - name = "featureMember"; │ │ │ │ │ + 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 │ │ │ │ │ + }); │ │ │ │ │ } │ │ │ │ │ - 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]); │ │ │ │ │ + this.bounds = bounds; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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. │ │ │ │ │ + /** │ │ │ │ │ + * Method: moveTo │ │ │ │ │ + * Reposition the tile. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * bounds - {} │ │ │ │ │ + * position - {} │ │ │ │ │ + * redraw - {Boolean} Call draw method on tile after moving. │ │ │ │ │ + * Default is true │ │ │ │ │ */ │ │ │ │ │ - writers: { │ │ │ │ │ - "gml": { │ │ │ │ │ - "featureMember": function(feature) { │ │ │ │ │ - var node = this.createElementNSPlus("gml:featureMember"); │ │ │ │ │ - this.writeNode("feature:_typeName", feature, node); │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "MultiPoint": function(geometry) { │ │ │ │ │ - var node = this.createElementNSPlus("gml:MultiPoint"); │ │ │ │ │ - var components = geometry.components || [geometry]; │ │ │ │ │ - for (var i = 0, ii = components.length; i < ii; ++i) { │ │ │ │ │ - this.writeNode("pointMember", components[i], node); │ │ │ │ │ - } │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "pointMember": function(geometry) { │ │ │ │ │ - var node = this.createElementNSPlus("gml:pointMember"); │ │ │ │ │ - this.writeNode("Point", geometry, node); │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "MultiLineString": function(geometry) { │ │ │ │ │ - var node = this.createElementNSPlus("gml:MultiLineString"); │ │ │ │ │ - var components = geometry.components || [geometry]; │ │ │ │ │ - for (var i = 0, ii = components.length; i < ii; ++i) { │ │ │ │ │ - this.writeNode("lineStringMember", components[i], node); │ │ │ │ │ - } │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "lineStringMember": function(geometry) { │ │ │ │ │ - var node = this.createElementNSPlus("gml:lineStringMember"); │ │ │ │ │ - this.writeNode("LineString", geometry, node); │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "MultiPolygon": function(geometry) { │ │ │ │ │ - var node = this.createElementNSPlus("gml:MultiPolygon"); │ │ │ │ │ - var components = geometry.components || [geometry]; │ │ │ │ │ - for (var i = 0, ii = components.length; i < ii; ++i) { │ │ │ │ │ - this.writeNode( │ │ │ │ │ - "polygonMember", components[i], node │ │ │ │ │ - ); │ │ │ │ │ - } │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "polygonMember": function(geometry) { │ │ │ │ │ - var node = this.createElementNSPlus("gml:polygonMember"); │ │ │ │ │ - this.writeNode("Polygon", geometry, node); │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "GeometryCollection": function(geometry) { │ │ │ │ │ - var node = this.createElementNSPlus("gml:GeometryCollection"); │ │ │ │ │ - for (var i = 0, len = geometry.components.length; i < len; ++i) { │ │ │ │ │ - this.writeNode("geometryMember", geometry.components[i], node); │ │ │ │ │ - } │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "geometryMember": function(geometry) { │ │ │ │ │ - var node = this.createElementNSPlus("gml:geometryMember"); │ │ │ │ │ - var child = this.writeNode("feature:_geometry", geometry); │ │ │ │ │ - node.appendChild(child.firstChild); │ │ │ │ │ - return node; │ │ │ │ │ - } │ │ │ │ │ - }, │ │ │ │ │ - "feature": { │ │ │ │ │ - "_typeName": function(feature) { │ │ │ │ │ - var node = this.createElementNSPlus("feature:" + this.featureType, { │ │ │ │ │ - attributes: { │ │ │ │ │ - fid: feature.fid │ │ │ │ │ - } │ │ │ │ │ - }); │ │ │ │ │ - if (feature.geometry) { │ │ │ │ │ - this.writeNode("feature:_geometry", feature.geometry, node); │ │ │ │ │ - } │ │ │ │ │ - for (var name in feature.attributes) { │ │ │ │ │ - var value = feature.attributes[name]; │ │ │ │ │ - if (value != null) { │ │ │ │ │ - this.writeNode( │ │ │ │ │ - "feature:_attribute", { │ │ │ │ │ - name: name, │ │ │ │ │ - value: value │ │ │ │ │ - }, node │ │ │ │ │ - ); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "_geometry": function(geometry) { │ │ │ │ │ - if (this.externalProjection && this.internalProjection) { │ │ │ │ │ - geometry = geometry.clone().transform( │ │ │ │ │ - this.internalProjection, this.externalProjection │ │ │ │ │ - ); │ │ │ │ │ - } │ │ │ │ │ - var node = this.createElementNSPlus( │ │ │ │ │ - "feature:" + this.geometryName │ │ │ │ │ - ); │ │ │ │ │ - var type = this.geometryTypes[geometry.CLASS_NAME]; │ │ │ │ │ - var child = this.writeNode("gml:" + type, geometry, node); │ │ │ │ │ - if (this.srsName) { │ │ │ │ │ - child.setAttribute("srsName", this.srsName); │ │ │ │ │ - } │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "_attribute": function(obj) { │ │ │ │ │ - return this.createElementNSPlus("feature:" + obj.name, { │ │ │ │ │ - value: obj.value │ │ │ │ │ - }); │ │ │ │ │ - } │ │ │ │ │ - }, │ │ │ │ │ - "wfs": { │ │ │ │ │ - "FeatureCollection": function(features) { │ │ │ │ │ - /** │ │ │ │ │ - * This is only here because GML2 only describes abstract │ │ │ │ │ - * feature collections. Typically, you would not be using │ │ │ │ │ - * the GML format to write wfs elements. This just provides │ │ │ │ │ - * some way to write out lists of features. GML3 defines the │ │ │ │ │ - * featureMembers element, so that is used by default instead. │ │ │ │ │ - */ │ │ │ │ │ - var node = this.createElementNSPlus("wfs:FeatureCollection"); │ │ │ │ │ - for (var i = 0, len = features.length; i < len; ++i) { │ │ │ │ │ - this.writeNode("gml:featureMember", features[i], node); │ │ │ │ │ - } │ │ │ │ │ - return node; │ │ │ │ │ - } │ │ │ │ │ + moveTo: function(bounds, position, redraw) { │ │ │ │ │ + if (redraw == null) { │ │ │ │ │ + redraw = true; │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + this.setBounds(bounds); │ │ │ │ │ + this.position = position.clone(); │ │ │ │ │ + if (redraw) { │ │ │ │ │ + this.draw(); │ │ │ │ │ } │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: setGeometryTypes │ │ │ │ │ - * Sets the mapping. │ │ │ │ │ + /** │ │ │ │ │ + * Method: clear │ │ │ │ │ + * Clear the tile of any bounds/position-related data so that it can │ │ │ │ │ + * be reused in a new location. │ │ │ │ │ */ │ │ │ │ │ - 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" │ │ │ │ │ - }; │ │ │ │ │ + clear: function(draw) { │ │ │ │ │ + // to be extended by subclasses │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.GML.Base" │ │ │ │ │ - │ │ │ │ │ + CLASS_NAME: "OpenLayers.Tile" │ │ │ │ │ }); │ │ │ │ │ /* ====================================================================== │ │ │ │ │ - OpenLayers/Format/GML/v3.js │ │ │ │ │ + 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/Format/GML/Base.js │ │ │ │ │ + * @requires OpenLayers/Tile.js │ │ │ │ │ + * @requires OpenLayers/Animation.js │ │ │ │ │ + * @requires OpenLayers/Util.js │ │ │ │ │ */ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Class: OpenLayers.Format.GML.v3 │ │ │ │ │ - * Parses GML version 3. │ │ │ │ │ + * 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.Format.GML.v3 = OpenLayers.Class(OpenLayers.Format.GML.Base, { │ │ │ │ │ +OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile, { │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: schemaLocation │ │ │ │ │ - * {String} Schema location for a particular minor version. The writers │ │ │ │ │ - * conform with the Simple Features Profile for GML. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - 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). │ │ │ │ │ + /** │ │ │ │ │ + * APIProperty: url │ │ │ │ │ + * {String} The URL of the image being requested. No default. Filled in by │ │ │ │ │ + * layer.getURL() function. May be modified by loadstart listeners. │ │ │ │ │ */ │ │ │ │ │ - curve: false, │ │ │ │ │ + url: null, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Property: imgDiv │ │ │ │ │ + * {HTMLImageElement} The image for this tile. │ │ │ │ │ + */ │ │ │ │ │ + imgDiv: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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). │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - multiCurve: true, │ │ │ │ │ + frame: null, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Property: imageReloadAttempts │ │ │ │ │ + * {Integer} Attempts to load the image. │ │ │ │ │ + */ │ │ │ │ │ + imageReloadAttempts: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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). │ │ │ │ │ + * Property: layerAlphaHack │ │ │ │ │ + * {Boolean} True if the png alpha hack needs to be applied on the layer's div. │ │ │ │ │ */ │ │ │ │ │ - surface: false, │ │ │ │ │ + layerAlphaHack: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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). │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - multiSurface: true, │ │ │ │ │ + asyncRequestId: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * 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. │ │ │ │ │ * │ │ │ │ │ - * Valid options properties: │ │ │ │ │ - * featureType - {String} Local (without prefix) feature typeName (required). │ │ │ │ │ - * featureNS - {String} Feature namespace (required). │ │ │ │ │ - * geometryName - {String} Geometry element name. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - initialize: function(options) { │ │ │ │ │ - OpenLayers.Format.GML.Base.prototype.initialize.apply(this, [options]); │ │ │ │ │ - }, │ │ │ │ │ + maxGetUrlLength: 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. │ │ │ │ │ + * Property: canvasContext │ │ │ │ │ + * {CanvasRenderingContext2D} A canvas context associated with │ │ │ │ │ + * the tile image. │ │ │ │ │ */ │ │ │ │ │ - readers: { │ │ │ │ │ - "gml": OpenLayers.Util.applyDefaults({ │ │ │ │ │ - "_inherit": function(node, obj, container) { │ │ │ │ │ - // SRSReferenceGroup attributes │ │ │ │ │ - var dim = parseInt(node.getAttribute("srsDimension"), 10) || │ │ │ │ │ - (container && container.srsDimension); │ │ │ │ │ - if (dim) { │ │ │ │ │ - obj.srsDimension = dim; │ │ │ │ │ - } │ │ │ │ │ - }, │ │ │ │ │ - "featureMembers": function(node, obj) { │ │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ │ - }, │ │ │ │ │ - "Curve": function(node, container) { │ │ │ │ │ - var obj = { │ │ │ │ │ - points: [] │ │ │ │ │ - }; │ │ │ │ │ - this.readers.gml._inherit.apply(this, [node, obj, container]); │ │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ │ - if (!container.components) { │ │ │ │ │ - container.components = []; │ │ │ │ │ - } │ │ │ │ │ - container.components.push( │ │ │ │ │ - new OpenLayers.Geometry.LineString(obj.points) │ │ │ │ │ - ); │ │ │ │ │ - }, │ │ │ │ │ - "segments": function(node, obj) { │ │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ │ - }, │ │ │ │ │ - "LineStringSegment": function(node, container) { │ │ │ │ │ - var obj = {}; │ │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ │ - if (obj.points) { │ │ │ │ │ - Array.prototype.push.apply(container.points, obj.points); │ │ │ │ │ - } │ │ │ │ │ - }, │ │ │ │ │ - "pos": function(node, obj) { │ │ │ │ │ - var str = this.getChildValue(node).replace( │ │ │ │ │ - this.regExes.trimSpace, "" │ │ │ │ │ - ); │ │ │ │ │ - var coords = str.split(this.regExes.splitSpace); │ │ │ │ │ - var point; │ │ │ │ │ - if (this.xy) { │ │ │ │ │ - point = new OpenLayers.Geometry.Point( │ │ │ │ │ - coords[0], coords[1], coords[2] │ │ │ │ │ - ); │ │ │ │ │ - } else { │ │ │ │ │ - point = new OpenLayers.Geometry.Point( │ │ │ │ │ - coords[1], coords[0], coords[2] │ │ │ │ │ - ); │ │ │ │ │ - } │ │ │ │ │ - obj.points = [point]; │ │ │ │ │ - }, │ │ │ │ │ - "posList": function(node, obj) { │ │ │ │ │ - var str = this.getChildValue(node).replace( │ │ │ │ │ - this.regExes.trimSpace, "" │ │ │ │ │ - ); │ │ │ │ │ - var coords = str.split(this.regExes.splitSpace); │ │ │ │ │ - // The "dimension" attribute is from the GML 3.0.1 spec. │ │ │ │ │ - var dim = obj.srsDimension || │ │ │ │ │ - parseInt(node.getAttribute("srsDimension") || node.getAttribute("dimension"), 10) || 2; │ │ │ │ │ - var j, x, y, z; │ │ │ │ │ - var numPoints = coords.length / dim; │ │ │ │ │ - var points = new Array(numPoints); │ │ │ │ │ - for (var i = 0, len = coords.length; i < len; i += dim) { │ │ │ │ │ - x = coords[i]; │ │ │ │ │ - y = coords[i + 1]; │ │ │ │ │ - z = (dim == 2) ? undefined : coords[i + 2]; │ │ │ │ │ - if (this.xy) { │ │ │ │ │ - points[i / dim] = new OpenLayers.Geometry.Point(x, y, z); │ │ │ │ │ - } else { │ │ │ │ │ - points[i / dim] = new OpenLayers.Geometry.Point(y, x, z); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - obj.points = points; │ │ │ │ │ - }, │ │ │ │ │ - "Surface": function(node, obj) { │ │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ │ - }, │ │ │ │ │ - "patches": function(node, obj) { │ │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ │ - }, │ │ │ │ │ - "PolygonPatch": function(node, obj) { │ │ │ │ │ - this.readers.gml.Polygon.apply(this, [node, obj]); │ │ │ │ │ - }, │ │ │ │ │ - "exterior": function(node, container) { │ │ │ │ │ - var obj = {}; │ │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ │ - container.outer = obj.components[0]; │ │ │ │ │ - }, │ │ │ │ │ - "interior": function(node, container) { │ │ │ │ │ - var obj = {}; │ │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ │ - container.inner.push(obj.components[0]); │ │ │ │ │ - }, │ │ │ │ │ - "MultiCurve": function(node, container) { │ │ │ │ │ - var obj = { │ │ │ │ │ - components: [] │ │ │ │ │ - }; │ │ │ │ │ - this.readers.gml._inherit.apply(this, [node, obj, container]); │ │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ │ - if (obj.components.length > 0) { │ │ │ │ │ - container.components = [ │ │ │ │ │ - new OpenLayers.Geometry.MultiLineString(obj.components) │ │ │ │ │ - ]; │ │ │ │ │ - } │ │ │ │ │ - }, │ │ │ │ │ - "curveMember": function(node, obj) { │ │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ │ - }, │ │ │ │ │ - "MultiSurface": function(node, container) { │ │ │ │ │ - var obj = { │ │ │ │ │ - components: [] │ │ │ │ │ - }; │ │ │ │ │ - this.readers.gml._inherit.apply(this, [node, obj, container]); │ │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ │ - if (obj.components.length > 0) { │ │ │ │ │ - container.components = [ │ │ │ │ │ - new OpenLayers.Geometry.MultiPolygon(obj.components) │ │ │ │ │ - ]; │ │ │ │ │ - } │ │ │ │ │ - }, │ │ │ │ │ - "surfaceMember": function(node, obj) { │ │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ │ - }, │ │ │ │ │ - "surfaceMembers": function(node, obj) { │ │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ │ - }, │ │ │ │ │ - "pointMembers": function(node, obj) { │ │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ │ - }, │ │ │ │ │ - "lineStringMembers": function(node, obj) { │ │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ │ - }, │ │ │ │ │ - "polygonMembers": function(node, obj) { │ │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ │ - }, │ │ │ │ │ - "geometryMembers": function(node, obj) { │ │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ │ - }, │ │ │ │ │ - "Envelope": function(node, container) { │ │ │ │ │ - var obj = { │ │ │ │ │ - points: new Array(2) │ │ │ │ │ - }; │ │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ │ - if (!container.components) { │ │ │ │ │ - container.components = []; │ │ │ │ │ - } │ │ │ │ │ - var min = obj.points[0]; │ │ │ │ │ - var max = obj.points[1]; │ │ │ │ │ - container.components.push( │ │ │ │ │ - new OpenLayers.Bounds(min.x, min.y, max.x, max.y) │ │ │ │ │ - ); │ │ │ │ │ - }, │ │ │ │ │ - "lowerCorner": function(node, container) { │ │ │ │ │ - var obj = {}; │ │ │ │ │ - this.readers.gml.pos.apply(this, [node, obj]); │ │ │ │ │ - container.points[0] = obj.points[0]; │ │ │ │ │ - }, │ │ │ │ │ - "upperCorner": function(node, container) { │ │ │ │ │ - var obj = {}; │ │ │ │ │ - this.readers.gml.pos.apply(this, [node, obj]); │ │ │ │ │ - container.points[1] = obj.points[0]; │ │ │ │ │ - } │ │ │ │ │ - }, OpenLayers.Format.GML.Base.prototype.readers["gml"]), │ │ │ │ │ - "feature": OpenLayers.Format.GML.Base.prototype.readers["feature"], │ │ │ │ │ - "wfs": OpenLayers.Format.GML.Base.prototype.readers["wfs"] │ │ │ │ │ - }, │ │ │ │ │ + canvasContext: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: write │ │ │ │ │ - * │ │ │ │ │ + * 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: │ │ │ │ │ - * 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. │ │ │ │ │ + * layer - {} layer that the tile will go in. │ │ │ │ │ + * position - {} │ │ │ │ │ + * bounds - {} │ │ │ │ │ + * url - {} Deprecated. Remove me in 3.0. │ │ │ │ │ + * size - {} │ │ │ │ │ + * options - {Object} │ │ │ │ │ */ │ │ │ │ │ - write: function(features) { │ │ │ │ │ - var name; │ │ │ │ │ - if (OpenLayers.Util.isArray(features)) { │ │ │ │ │ - name = "featureMembers"; │ │ │ │ │ - } else { │ │ │ │ │ - name = "featureMember"; │ │ │ │ │ + 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"; │ │ │ │ │ } │ │ │ │ │ - var root = this.writeNode("gml:" + name, features); │ │ │ │ │ - this.setAttributeNS( │ │ │ │ │ - root, this.namespaces["xsi"], │ │ │ │ │ - "xsi:schemaLocation", this.schemaLocation │ │ │ │ │ - ); │ │ │ │ │ + if (this.maxGetUrlLength != null) { │ │ │ │ │ + OpenLayers.Util.extend(this, OpenLayers.Tile.Image.IFrame); │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - return OpenLayers.Format.XML.prototype.write.apply(this, [root]); │ │ │ │ │ + /** │ │ │ │ │ + * 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); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - writers: { │ │ │ │ │ - "gml": OpenLayers.Util.applyDefaults({ │ │ │ │ │ - "featureMembers": function(features) { │ │ │ │ │ - var node = this.createElementNSPlus("gml:featureMembers"); │ │ │ │ │ - for (var i = 0, len = features.length; i < len; ++i) { │ │ │ │ │ - this.writeNode("feature:_typeName", features[i], node); │ │ │ │ │ - } │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "Point": function(geometry) { │ │ │ │ │ - var node = this.createElementNSPlus("gml:Point"); │ │ │ │ │ - this.writeNode("pos", geometry, node); │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "pos": function(point) { │ │ │ │ │ - // only 2d for simple features profile │ │ │ │ │ - var pos = (this.xy) ? │ │ │ │ │ - (point.x + " " + point.y) : (point.y + " " + point.x); │ │ │ │ │ - return this.createElementNSPlus("gml:pos", { │ │ │ │ │ - value: pos │ │ │ │ │ - }); │ │ │ │ │ - }, │ │ │ │ │ - "LineString": function(geometry) { │ │ │ │ │ - var node = this.createElementNSPlus("gml:LineString"); │ │ │ │ │ - this.writeNode("posList", geometry.components, node); │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "Curve": function(geometry) { │ │ │ │ │ - var node = this.createElementNSPlus("gml:Curve"); │ │ │ │ │ - this.writeNode("segments", geometry, node); │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "segments": function(geometry) { │ │ │ │ │ - var node = this.createElementNSPlus("gml:segments"); │ │ │ │ │ - this.writeNode("LineStringSegment", geometry, node); │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "LineStringSegment": function(geometry) { │ │ │ │ │ - var node = this.createElementNSPlus("gml:LineStringSegment"); │ │ │ │ │ - this.writeNode("posList", geometry.components, node); │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "posList": function(points) { │ │ │ │ │ - // only 2d for simple features profile │ │ │ │ │ - var len = points.length; │ │ │ │ │ - var parts = new Array(len); │ │ │ │ │ - var point; │ │ │ │ │ - for (var i = 0; i < len; ++i) { │ │ │ │ │ - point = points[i]; │ │ │ │ │ - if (this.xy) { │ │ │ │ │ - parts[i] = point.x + " " + point.y; │ │ │ │ │ - } else { │ │ │ │ │ - parts[i] = point.y + " " + point.x; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - return this.createElementNSPlus("gml:posList", { │ │ │ │ │ - value: parts.join(" ") │ │ │ │ │ - }); │ │ │ │ │ - }, │ │ │ │ │ - "Surface": function(geometry) { │ │ │ │ │ - var node = this.createElementNSPlus("gml:Surface"); │ │ │ │ │ - this.writeNode("patches", geometry, node); │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "patches": function(geometry) { │ │ │ │ │ - var node = this.createElementNSPlus("gml:patches"); │ │ │ │ │ - this.writeNode("PolygonPatch", geometry, node); │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "PolygonPatch": function(geometry) { │ │ │ │ │ - var node = this.createElementNSPlus("gml:PolygonPatch", { │ │ │ │ │ - attributes: { │ │ │ │ │ - interpolation: "planar" │ │ │ │ │ - } │ │ │ │ │ - }); │ │ │ │ │ - this.writeNode("exterior", geometry.components[0], node); │ │ │ │ │ - for (var i = 1, len = geometry.components.length; i < len; ++i) { │ │ │ │ │ - this.writeNode( │ │ │ │ │ - "interior", geometry.components[i], node │ │ │ │ │ - ); │ │ │ │ │ - } │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "Polygon": function(geometry) { │ │ │ │ │ - var node = this.createElementNSPlus("gml:Polygon"); │ │ │ │ │ - this.writeNode("exterior", geometry.components[0], node); │ │ │ │ │ - for (var i = 1, len = geometry.components.length; i < len; ++i) { │ │ │ │ │ - this.writeNode( │ │ │ │ │ - "interior", geometry.components[i], node │ │ │ │ │ - ); │ │ │ │ │ - } │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "exterior": function(ring) { │ │ │ │ │ - var node = this.createElementNSPlus("gml:exterior"); │ │ │ │ │ - this.writeNode("LinearRing", ring, node); │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "interior": function(ring) { │ │ │ │ │ - var node = this.createElementNSPlus("gml:interior"); │ │ │ │ │ - this.writeNode("LinearRing", ring, node); │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "LinearRing": function(ring) { │ │ │ │ │ - var node = this.createElementNSPlus("gml:LinearRing"); │ │ │ │ │ - this.writeNode("posList", ring.components, node); │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "MultiCurve": function(geometry) { │ │ │ │ │ - var node = this.createElementNSPlus("gml:MultiCurve"); │ │ │ │ │ - var components = geometry.components || [geometry]; │ │ │ │ │ - for (var i = 0, len = components.length; i < len; ++i) { │ │ │ │ │ - this.writeNode("curveMember", components[i], node); │ │ │ │ │ - } │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "curveMember": function(geometry) { │ │ │ │ │ - var node = this.createElementNSPlus("gml:curveMember"); │ │ │ │ │ - if (this.curve) { │ │ │ │ │ - this.writeNode("Curve", geometry, node); │ │ │ │ │ - } else { │ │ │ │ │ - this.writeNode("LineString", geometry, node); │ │ │ │ │ - } │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "MultiSurface": function(geometry) { │ │ │ │ │ - var node = this.createElementNSPlus("gml:MultiSurface"); │ │ │ │ │ - var components = geometry.components || [geometry]; │ │ │ │ │ - for (var i = 0, len = components.length; i < len; ++i) { │ │ │ │ │ - this.writeNode("surfaceMember", components[i], node); │ │ │ │ │ - } │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "surfaceMember": function(polygon) { │ │ │ │ │ - var node = this.createElementNSPlus("gml:surfaceMember"); │ │ │ │ │ - if (this.surface) { │ │ │ │ │ - this.writeNode("Surface", polygon, node); │ │ │ │ │ - } else { │ │ │ │ │ - this.writeNode("Polygon", polygon, node); │ │ │ │ │ - } │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "Envelope": function(bounds) { │ │ │ │ │ - var node = this.createElementNSPlus("gml:Envelope"); │ │ │ │ │ - this.writeNode("lowerCorner", bounds, node); │ │ │ │ │ - this.writeNode("upperCorner", bounds, node); │ │ │ │ │ - // srsName attribute is required for gml:Envelope │ │ │ │ │ - if (this.srsName) { │ │ │ │ │ - node.setAttribute("srsName", this.srsName); │ │ │ │ │ - } │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "lowerCorner": function(bounds) { │ │ │ │ │ - // only 2d for simple features profile │ │ │ │ │ - var pos = (this.xy) ? │ │ │ │ │ - (bounds.left + " " + bounds.bottom) : │ │ │ │ │ - (bounds.bottom + " " + bounds.left); │ │ │ │ │ - return this.createElementNSPlus("gml:lowerCorner", { │ │ │ │ │ - value: pos │ │ │ │ │ - }); │ │ │ │ │ - }, │ │ │ │ │ - "upperCorner": function(bounds) { │ │ │ │ │ - // only 2d for simple features profile │ │ │ │ │ - var pos = (this.xy) ? │ │ │ │ │ - (bounds.right + " " + bounds.top) : │ │ │ │ │ - (bounds.top + " " + bounds.right); │ │ │ │ │ - return this.createElementNSPlus("gml:upperCorner", { │ │ │ │ │ - value: pos │ │ │ │ │ - }); │ │ │ │ │ + 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); │ │ │ │ │ } │ │ │ │ │ - }, OpenLayers.Format.GML.Base.prototype.writers["gml"]), │ │ │ │ │ - "feature": OpenLayers.Format.GML.Base.prototype.writers["feature"], │ │ │ │ │ - "wfs": OpenLayers.Format.GML.Base.prototype.writers["wfs"] │ │ │ │ │ + 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: setGeometryTypes │ │ │ │ │ - * Sets the mapping. │ │ │ │ │ + * Method: renderTile │ │ │ │ │ + * Internal function to actually initialize the image tile, │ │ │ │ │ + * position it correctly, and set its url. │ │ │ │ │ */ │ │ │ │ │ - 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" │ │ │ │ │ - }; │ │ │ │ │ + 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(); │ │ │ │ │ + } │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.GML.v3" │ │ │ │ │ - │ │ │ │ │ -}); │ │ │ │ │ -/* ====================================================================== │ │ │ │ │ - OpenLayers/Format/Filter/v1_1_0.js │ │ │ │ │ - ====================================================================== */ │ │ │ │ │ + /** │ │ │ │ │ + * 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"; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ - * full text of the license. */ │ │ │ │ │ + /** │ │ │ │ │ + * 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; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * @requires OpenLayers/Format/Filter/v1.js │ │ │ │ │ - * @requires OpenLayers/Format/GML/v3.js │ │ │ │ │ - */ │ │ │ │ │ + /** │ │ │ │ │ + * Method: getImage │ │ │ │ │ + * Returns or creates and returns the tile image. │ │ │ │ │ + */ │ │ │ │ │ + getImage: function() { │ │ │ │ │ + if (!this.imgDiv) { │ │ │ │ │ + this.imgDiv = OpenLayers.Tile.Image.IMAGE.cloneNode(false); │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * 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, { │ │ │ │ │ + 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); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Constant: VERSION │ │ │ │ │ - * {String} 1.1.0 │ │ │ │ │ - */ │ │ │ │ │ - VERSION: "1.1.0", │ │ │ │ │ + return this.imgDiv; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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", │ │ │ │ │ + /** │ │ │ │ │ + * 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; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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] │ │ │ │ │ + /** │ │ │ │ │ + * 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 │ │ │ │ │ ); │ │ │ │ │ - }, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Property: readers │ │ │ │ │ - * Contains public functions, grouped by namespace prefix, that will │ │ │ │ │ - * be applied when a namespaced node is found matching the function │ │ │ │ │ - * name. The function will be applied in the scope of this parser │ │ │ │ │ - * with two arguments: the node being read and a context object passed │ │ │ │ │ - * from the parent. │ │ │ │ │ - */ │ │ │ │ │ - readers: { │ │ │ │ │ - "ogc": OpenLayers.Util.applyDefaults({ │ │ │ │ │ - "PropertyIsEqualTo": function(node, obj) { │ │ │ │ │ - var matchCase = node.getAttribute("matchCase"); │ │ │ │ │ - var filter = new OpenLayers.Filter.Comparison({ │ │ │ │ │ - type: OpenLayers.Filter.Comparison.EQUAL_TO, │ │ │ │ │ - matchCase: !(matchCase === "false" || matchCase === "0") │ │ │ │ │ - }); │ │ │ │ │ - this.readChildNodes(node, filter); │ │ │ │ │ - obj.filters.push(filter); │ │ │ │ │ - }, │ │ │ │ │ - "PropertyIsNotEqualTo": function(node, obj) { │ │ │ │ │ - var matchCase = node.getAttribute("matchCase"); │ │ │ │ │ - var filter = new OpenLayers.Filter.Comparison({ │ │ │ │ │ - type: OpenLayers.Filter.Comparison.NOT_EQUAL_TO, │ │ │ │ │ - matchCase: !(matchCase === "false" || matchCase === "0") │ │ │ │ │ - }); │ │ │ │ │ - this.readChildNodes(node, filter); │ │ │ │ │ - obj.filters.push(filter); │ │ │ │ │ - }, │ │ │ │ │ - "PropertyIsLike": function(node, obj) { │ │ │ │ │ - var filter = new OpenLayers.Filter.Comparison({ │ │ │ │ │ - type: OpenLayers.Filter.Comparison.LIKE │ │ │ │ │ - }); │ │ │ │ │ - this.readChildNodes(node, filter); │ │ │ │ │ - var wildCard = node.getAttribute("wildCard"); │ │ │ │ │ - var singleChar = node.getAttribute("singleChar"); │ │ │ │ │ - var esc = node.getAttribute("escapeChar"); │ │ │ │ │ - filter.value2regex(wildCard, singleChar, esc); │ │ │ │ │ - obj.filters.push(filter); │ │ │ │ │ - } │ │ │ │ │ - }, OpenLayers.Format.Filter.v1.prototype.readers["ogc"]), │ │ │ │ │ - "gml": OpenLayers.Format.GML.v3.prototype.readers["gml"], │ │ │ │ │ - "feature": OpenLayers.Format.GML.v3.prototype.readers["feature"] │ │ │ │ │ - }, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Property: writers │ │ │ │ │ - * As a compliment to the readers property, this structure contains public │ │ │ │ │ - * writing functions grouped by namespace alias and named like the │ │ │ │ │ - * node names they produce. │ │ │ │ │ - */ │ │ │ │ │ - writers: { │ │ │ │ │ - "ogc": OpenLayers.Util.applyDefaults({ │ │ │ │ │ - "PropertyIsEqualTo": function(filter) { │ │ │ │ │ - var node = this.createElementNSPlus("ogc:PropertyIsEqualTo", { │ │ │ │ │ - attributes: { │ │ │ │ │ - matchCase: filter.matchCase │ │ │ │ │ - } │ │ │ │ │ - }); │ │ │ │ │ - // no ogc:expression handling for PropertyName for now │ │ │ │ │ - this.writeNode("PropertyName", filter, node); │ │ │ │ │ - // handle Literals or Functions for now │ │ │ │ │ - this.writeOgcExpression(filter.value, node); │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "PropertyIsNotEqualTo": function(filter) { │ │ │ │ │ - var node = this.createElementNSPlus("ogc:PropertyIsNotEqualTo", { │ │ │ │ │ - attributes: { │ │ │ │ │ - matchCase: filter.matchCase │ │ │ │ │ - } │ │ │ │ │ - }); │ │ │ │ │ - // no ogc:expression handling for PropertyName for now │ │ │ │ │ - this.writeNode("PropertyName", filter, node); │ │ │ │ │ - // handle Literals or Functions for now │ │ │ │ │ - this.writeOgcExpression(filter.value, node); │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "PropertyIsLike": function(filter) { │ │ │ │ │ - var node = this.createElementNSPlus("ogc:PropertyIsLike", { │ │ │ │ │ - attributes: { │ │ │ │ │ - matchCase: filter.matchCase, │ │ │ │ │ - wildCard: "*", │ │ │ │ │ - singleChar: ".", │ │ │ │ │ - escapeChar: "!" │ │ │ │ │ - } │ │ │ │ │ - }); │ │ │ │ │ - // no ogc:expression handling for now │ │ │ │ │ - this.writeNode("PropertyName", filter, node); │ │ │ │ │ - // convert regex string to ogc string │ │ │ │ │ - this.writeNode("Literal", filter.regex2value(), node); │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "BBOX": function(filter) { │ │ │ │ │ - var node = this.createElementNSPlus("ogc:BBOX"); │ │ │ │ │ - // PropertyName is optional in 1.1.0 │ │ │ │ │ - filter.property && this.writeNode("PropertyName", filter, node); │ │ │ │ │ - var box = this.writeNode("gml:Envelope", filter.value); │ │ │ │ │ - if (filter.projection) { │ │ │ │ │ - box.setAttribute("srsName", filter.projection); │ │ │ │ │ - } │ │ │ │ │ - node.appendChild(box); │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "SortBy": function(sortProperties) { │ │ │ │ │ - var node = this.createElementNSPlus("ogc:SortBy"); │ │ │ │ │ - for (var i = 0, l = sortProperties.length; i < l; i++) { │ │ │ │ │ - this.writeNode( │ │ │ │ │ - "ogc:SortProperty", │ │ │ │ │ - sortProperties[i], │ │ │ │ │ - node │ │ │ │ │ - ); │ │ │ │ │ - } │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "SortProperty": function(sortProperty) { │ │ │ │ │ - var node = this.createElementNSPlus("ogc:SortProperty"); │ │ │ │ │ - this.writeNode( │ │ │ │ │ - "ogc:PropertyName", │ │ │ │ │ - sortProperty, │ │ │ │ │ - node │ │ │ │ │ - ); │ │ │ │ │ - this.writeNode( │ │ │ │ │ - "ogc:SortOrder", │ │ │ │ │ - (sortProperty.order == 'DESC') ? 'DESC' : 'ASC', │ │ │ │ │ - node │ │ │ │ │ - ); │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "SortOrder": function(value) { │ │ │ │ │ - var node = this.createElementNSPlus("ogc:SortOrder", { │ │ │ │ │ - value: value │ │ │ │ │ - }); │ │ │ │ │ - return node; │ │ │ │ │ - } │ │ │ │ │ - }, OpenLayers.Format.Filter.v1.prototype.writers["ogc"]), │ │ │ │ │ - "gml": OpenLayers.Format.GML.v3.prototype.writers["gml"], │ │ │ │ │ - "feature": OpenLayers.Format.GML.v3.prototype.writers["feature"] │ │ │ │ │ - }, │ │ │ │ │ + } 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: 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; │ │ │ │ │ + /** │ │ │ │ │ + * 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 { │ │ │ │ │ - child = this.writeNode("gml:Envelope", filter.value); │ │ │ │ │ - } │ │ │ │ │ - if (filter.projection) { │ │ │ │ │ - child.setAttribute("srsName", filter.projection); │ │ │ │ │ + img.removeAttribute("crossorigin"); │ │ │ │ │ } │ │ │ │ │ - node.appendChild(child); │ │ │ │ │ } │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.Filter.v1_1_0" │ │ │ │ │ + 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); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - }); │ │ │ │ │ -/* ====================================================================== │ │ │ │ │ - OpenLayers/Format/OWSCommon/v1_0_0.js │ │ │ │ │ - ====================================================================== */ │ │ │ │ │ + /** │ │ │ │ │ + * Method: getTile │ │ │ │ │ + * Get the tile's markup. │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {DOMElement} The tile's markup │ │ │ │ │ + */ │ │ │ │ │ + getTile: function() { │ │ │ │ │ + return this.frame ? this.frame : this.getImage(); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ - * full text of the license. */ │ │ │ │ │ + /** │ │ │ │ │ + * 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; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * @requires OpenLayers/Format/OWSCommon/v1.js │ │ │ │ │ - */ │ │ │ │ │ + /** │ │ │ │ │ + * 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"); │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * 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, { │ │ │ │ │ + if (this.layerAlphaHack === true) { │ │ │ │ │ + img.style.filter = │ │ │ │ │ + "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + │ │ │ │ │ + img.src + "', sizingMethod='scale')"; │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: namespaces │ │ │ │ │ - * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ │ + * Method: onImageError │ │ │ │ │ + * Handler for the image onerror event │ │ │ │ │ */ │ │ │ │ │ - namespaces: { │ │ │ │ │ - ows: "http://www.opengis.net/ows", │ │ │ │ │ - xlink: "http://www.w3.org/1999/xlink" │ │ │ │ │ + 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(); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * Method: stopLoading │ │ │ │ │ + * Stops a loading sequence so won't be executed. │ │ │ │ │ */ │ │ │ │ │ - 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) │ │ │ │ │ + stopLoading: function() { │ │ │ │ │ + OpenLayers.Event.stopObservingElement(this.imgDiv); │ │ │ │ │ + window.clearTimeout(this._loadTimeout); │ │ │ │ │ + delete this._loadTimeout; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * 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} │ │ │ │ │ */ │ │ │ │ │ - writers: { │ │ │ │ │ - "ows": OpenLayers.Format.OWSCommon.v1.prototype.writers.ows │ │ │ │ │ + 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.Format.OWSCommon.v1_0_0" │ │ │ │ │ + CLASS_NAME: "OpenLayers.Tile.Image" │ │ │ │ │ │ │ │ │ │ }); │ │ │ │ │ -/* ====================================================================== │ │ │ │ │ - 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: │ │ │ │ │ - * - │ │ │ │ │ - * - │ │ │ │ │ +/** │ │ │ │ │ + * Constant: OpenLayers.Tile.Image.IMAGE │ │ │ │ │ + * {HTMLImageElement} The image for a tile. │ │ │ │ │ */ │ │ │ │ │ -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 < len; i++) { │ │ │ │ │ - this.writeNode( │ │ │ │ │ - "wfs:PropertyName", { │ │ │ │ │ - property: options.propertyNames[i] │ │ │ │ │ - }, │ │ │ │ │ - node │ │ │ │ │ - ); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - if (options.filter) { │ │ │ │ │ - OpenLayers.Format.WFST.v1_1_0.prototype.setFilterProperty.call(this, options.filter); │ │ │ │ │ - this.writeNode("ogc:Filter", options.filter, node); │ │ │ │ │ - } │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "PropertyName": function(obj) { │ │ │ │ │ - return this.createElementNSPlus("wfs:PropertyName", { │ │ │ │ │ - value: obj.property │ │ │ │ │ - }); │ │ │ │ │ - } │ │ │ │ │ - }, OpenLayers.Format.WFST.v1.prototype.writers["wfs"]), │ │ │ │ │ - "gml": OpenLayers.Format.GML.v3.prototype.writers["gml"], │ │ │ │ │ - "feature": OpenLayers.Format.GML.v3.prototype.writers["feature"], │ │ │ │ │ - "ogc": OpenLayers.Format.Filter.v1_1_0.prototype.writers["ogc"] │ │ │ │ │ - }, │ │ │ │ │ +OpenLayers.Tile.Image.IMAGE = (function() { │ │ │ │ │ + var img = new Image(); │ │ │ │ │ + img.className = "olTileImage"; │ │ │ │ │ + // avoid image gallery menu in IE6 │ │ │ │ │ + img.galleryImg = "no"; │ │ │ │ │ + return img; │ │ │ │ │ +}()); │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.WFST.v1_1_0" │ │ │ │ │ - }); │ │ │ │ │ /* ====================================================================== │ │ │ │ │ - OpenLayers/Format/WPSExecute.js │ │ │ │ │ + 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/Format/XML.js │ │ │ │ │ - * @requires OpenLayers/Format/OWSCommon/v1_1_0.js │ │ │ │ │ - * @requires OpenLayers/Format/WCSGetCoverage.js │ │ │ │ │ - * @requires OpenLayers/Format/WFST/v1_1_0.js │ │ │ │ │ + * @requires OpenLayers/Layer/HTTPRequest.js │ │ │ │ │ + * @requires OpenLayers/Tile/Image.js │ │ │ │ │ */ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Class: OpenLayers.Format.WPSExecute version 1.0.0 │ │ │ │ │ + * 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.Format.WPSExecute = OpenLayers.Class(OpenLayers.Format.XML, │ │ │ │ │ - OpenLayers.Format.Filter.v1_1_0, { │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Property: namespaces │ │ │ │ │ - * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ │ - */ │ │ │ │ │ - namespaces: { │ │ │ │ │ - ows: "http://www.opengis.net/ows/1.1", │ │ │ │ │ - gml: "http://www.opengis.net/gml", │ │ │ │ │ - wps: "http://www.opengis.net/wps/1.0.0", │ │ │ │ │ - wfs: "http://www.opengis.net/wfs", │ │ │ │ │ - ogc: "http://www.opengis.net/ogc", │ │ │ │ │ - wcs: "http://www.opengis.net/wcs", │ │ │ │ │ - xlink: "http://www.w3.org/1999/xlink", │ │ │ │ │ - xsi: "http://www.w3.org/2001/XMLSchema-instance" │ │ │ │ │ - }, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Property: regExes │ │ │ │ │ - * Compiled regular expressions for manipulating strings. │ │ │ │ │ - */ │ │ │ │ │ - regExes: { │ │ │ │ │ - trimSpace: (/^\s*|\s*$/g), │ │ │ │ │ - removeSpace: (/\s*/g), │ │ │ │ │ - splitSpace: (/\s+/), │ │ │ │ │ - trimComma: (/\s*,\s*/g) │ │ │ │ │ - }, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Constant: VERSION │ │ │ │ │ - * {String} 1.0.0 │ │ │ │ │ - */ │ │ │ │ │ - VERSION: "1.0.0", │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Property: schemaLocation │ │ │ │ │ - * {String} Schema location │ │ │ │ │ - */ │ │ │ │ │ - schemaLocation: "http://www.opengis.net/wps/1.0.0 http://schemas.opengis.net/wps/1.0.0/wpsAll.xsd", │ │ │ │ │ - │ │ │ │ │ - schemaLocationAttr: function(options) { │ │ │ │ │ - return undefined; │ │ │ │ │ - }, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Constructor: OpenLayers.Format.WPSExecute │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ │ - * this instance. │ │ │ │ │ - */ │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Method: write │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * options - {Object} Optional object. │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {String} An WPS Execute request XML string. │ │ │ │ │ - */ │ │ │ │ │ - write: function(options) { │ │ │ │ │ - var doc; │ │ │ │ │ - if (window.ActiveXObject) { │ │ │ │ │ - doc = new ActiveXObject("Microsoft.XMLDOM"); │ │ │ │ │ - this.xmldom = doc; │ │ │ │ │ - } else { │ │ │ │ │ - doc = document.implementation.createDocument("", "", null); │ │ │ │ │ - } │ │ │ │ │ - var node = this.writeNode("wps:Execute", options, doc); │ │ │ │ │ - this.setAttributeNS( │ │ │ │ │ - node, this.namespaces.xsi, │ │ │ │ │ - "xsi:schemaLocation", this.schemaLocation │ │ │ │ │ - ); │ │ │ │ │ - return OpenLayers.Format.XML.prototype.write.apply(this, [node]); │ │ │ │ │ - }, │ │ │ │ │ +OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIMethod: read │ │ │ │ │ - * Parse a WPS Execute and return an object with its information. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * data - {String} or {DOMElement} data to read/parse. │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Object} │ │ │ │ │ - */ │ │ │ │ │ - read: function(data) { │ │ │ │ │ - if (typeof data == "string") { │ │ │ │ │ - data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); │ │ │ │ │ - } │ │ │ │ │ - if (data && data.nodeType == 9) { │ │ │ │ │ - data = data.documentElement; │ │ │ │ │ - } │ │ │ │ │ - var info = {}; │ │ │ │ │ - this.readNode(data, info); │ │ │ │ │ - return info; │ │ │ │ │ - }, │ │ │ │ │ + /** │ │ │ │ │ + * APIProperty: tileSize │ │ │ │ │ + * {} │ │ │ │ │ + */ │ │ │ │ │ + tileSize: null, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: writers │ │ │ │ │ - * As a compliment to the readers property, this structure contains public │ │ │ │ │ - * writing functions grouped by namespace alias and named like the │ │ │ │ │ - * node names they produce. │ │ │ │ │ - */ │ │ │ │ │ - writers: { │ │ │ │ │ - "wps": { │ │ │ │ │ - "Execute": function(options) { │ │ │ │ │ - var node = this.createElementNSPlus("wps:Execute", { │ │ │ │ │ - attributes: { │ │ │ │ │ - version: this.VERSION, │ │ │ │ │ - service: 'WPS' │ │ │ │ │ - } │ │ │ │ │ - }); │ │ │ │ │ - this.writeNode("ows:Identifier", options.identifier, node); │ │ │ │ │ - this.writeNode("wps:DataInputs", options.dataInputs, node); │ │ │ │ │ - this.writeNode("wps:ResponseForm", options.responseForm, node); │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "ResponseForm": function(responseForm) { │ │ │ │ │ - var node = this.createElementNSPlus("wps:ResponseForm", {}); │ │ │ │ │ - if (responseForm.rawDataOutput) { │ │ │ │ │ - this.writeNode("wps:RawDataOutput", responseForm.rawDataOutput, node); │ │ │ │ │ - } │ │ │ │ │ - if (responseForm.responseDocument) { │ │ │ │ │ - this.writeNode("wps:ResponseDocument", responseForm.responseDocument, node); │ │ │ │ │ - } │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "ResponseDocument": function(responseDocument) { │ │ │ │ │ - var node = this.createElementNSPlus("wps:ResponseDocument", { │ │ │ │ │ - attributes: { │ │ │ │ │ - storeExecuteResponse: responseDocument.storeExecuteResponse, │ │ │ │ │ - lineage: responseDocument.lineage, │ │ │ │ │ - status: responseDocument.status │ │ │ │ │ - } │ │ │ │ │ - }); │ │ │ │ │ - if (responseDocument.outputs) { │ │ │ │ │ - for (var i = 0, len = responseDocument.outputs.length; i < len; i++) { │ │ │ │ │ - this.writeNode("wps:Output", responseDocument.outputs[i], node); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "Output": function(output) { │ │ │ │ │ - var node = this.createElementNSPlus("wps:Output", { │ │ │ │ │ - attributes: { │ │ │ │ │ - asReference: output.asReference, │ │ │ │ │ - mimeType: output.mimeType, │ │ │ │ │ - encoding: output.encoding, │ │ │ │ │ - schema: output.schema │ │ │ │ │ - } │ │ │ │ │ - }); │ │ │ │ │ - this.writeNode("ows:Identifier", output.identifier, node); │ │ │ │ │ - this.writeNode("ows:Title", output.title, node); │ │ │ │ │ - this.writeNode("ows:Abstract", output["abstract"], node); │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "RawDataOutput": function(rawDataOutput) { │ │ │ │ │ - var node = this.createElementNSPlus("wps:RawDataOutput", { │ │ │ │ │ - attributes: { │ │ │ │ │ - mimeType: rawDataOutput.mimeType, │ │ │ │ │ - encoding: rawDataOutput.encoding, │ │ │ │ │ - schema: rawDataOutput.schema │ │ │ │ │ - } │ │ │ │ │ - }); │ │ │ │ │ - this.writeNode("ows:Identifier", rawDataOutput.identifier, node); │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "DataInputs": function(dataInputs) { │ │ │ │ │ - var node = this.createElementNSPlus("wps:DataInputs", {}); │ │ │ │ │ - for (var i = 0, ii = dataInputs.length; i < ii; ++i) { │ │ │ │ │ - this.writeNode("wps:Input", dataInputs[i], node); │ │ │ │ │ - } │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "Input": function(input) { │ │ │ │ │ - var node = this.createElementNSPlus("wps:Input", {}); │ │ │ │ │ - this.writeNode("ows:Identifier", input.identifier, node); │ │ │ │ │ - if (input.title) { │ │ │ │ │ - this.writeNode("ows:Title", input.title, node); │ │ │ │ │ - } │ │ │ │ │ - if (input.data) { │ │ │ │ │ - this.writeNode("wps:Data", input.data, node); │ │ │ │ │ - } │ │ │ │ │ - if (input.reference) { │ │ │ │ │ - this.writeNode("wps:Reference", input.reference, node); │ │ │ │ │ - } │ │ │ │ │ - if (input.boundingBoxData) { │ │ │ │ │ - this.writeNode("wps:BoundingBoxData", input.boundingBoxData, node); │ │ │ │ │ - } │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "Data": function(data) { │ │ │ │ │ - var node = this.createElementNSPlus("wps:Data", {}); │ │ │ │ │ - if (data.literalData) { │ │ │ │ │ - this.writeNode("wps:LiteralData", data.literalData, node); │ │ │ │ │ - } else if (data.complexData) { │ │ │ │ │ - this.writeNode("wps:ComplexData", data.complexData, node); │ │ │ │ │ - } else if (data.boundingBoxData) { │ │ │ │ │ - this.writeNode("ows:BoundingBox", data.boundingBoxData, node); │ │ │ │ │ - } │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "LiteralData": function(literalData) { │ │ │ │ │ - var node = this.createElementNSPlus("wps:LiteralData", { │ │ │ │ │ - attributes: { │ │ │ │ │ - uom: literalData.uom │ │ │ │ │ - }, │ │ │ │ │ - value: literalData.value │ │ │ │ │ - }); │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "ComplexData": function(complexData) { │ │ │ │ │ - var node = this.createElementNSPlus("wps:ComplexData", { │ │ │ │ │ - attributes: { │ │ │ │ │ - mimeType: complexData.mimeType, │ │ │ │ │ - encoding: complexData.encoding, │ │ │ │ │ - schema: complexData.schema │ │ │ │ │ - } │ │ │ │ │ - }); │ │ │ │ │ - var data = complexData.value; │ │ │ │ │ - if (typeof data === "string") { │ │ │ │ │ - node.appendChild( │ │ │ │ │ - this.getXMLDoc().createCDATASection(complexData.value) │ │ │ │ │ - ); │ │ │ │ │ - } else { │ │ │ │ │ - node.appendChild(data); │ │ │ │ │ - } │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "Reference": function(reference) { │ │ │ │ │ - var node = this.createElementNSPlus("wps:Reference", { │ │ │ │ │ - attributes: { │ │ │ │ │ - mimeType: reference.mimeType, │ │ │ │ │ - "xlink:href": reference.href, │ │ │ │ │ - method: reference.method, │ │ │ │ │ - encoding: reference.encoding, │ │ │ │ │ - schema: reference.schema │ │ │ │ │ - } │ │ │ │ │ - }); │ │ │ │ │ - if (reference.body) { │ │ │ │ │ - this.writeNode("wps:Body", reference.body, node); │ │ │ │ │ - } │ │ │ │ │ - return node; │ │ │ │ │ - }, │ │ │ │ │ - "BoundingBoxData": function(node, obj) { │ │ │ │ │ - this.writers['ows']['BoundingBox'].apply(this, [node, obj, "wps:BoundingBoxData"]); │ │ │ │ │ - }, │ │ │ │ │ - "Body": function(body) { │ │ │ │ │ - var node = this.createElementNSPlus("wps:Body", {}); │ │ │ │ │ - if (body.wcs) { │ │ │ │ │ - this.writeNode("wcs:GetCoverage", body.wcs, node); │ │ │ │ │ - } else if (body.wfs) { │ │ │ │ │ - // OpenLayers.Format.WFST expects these to be on the │ │ │ │ │ - // instance and not in the options │ │ │ │ │ - this.featureType = body.wfs.featureType; │ │ │ │ │ - this.version = body.wfs.version; │ │ │ │ │ - this.writeNode("wfs:GetFeature", body.wfs, node); │ │ │ │ │ - } else { │ │ │ │ │ - this.writeNode("wps:Execute", body, node); │ │ │ │ │ - } │ │ │ │ │ - return node; │ │ │ │ │ - } │ │ │ │ │ - }, │ │ │ │ │ - "wcs": OpenLayers.Format.WCSGetCoverage.prototype.writers.wcs, │ │ │ │ │ - "wfs": OpenLayers.Format.WFST.v1_1_0.prototype.writers.wfs, │ │ │ │ │ - "ogc": OpenLayers.Format.Filter.v1_1_0.prototype.writers.ogc, │ │ │ │ │ - "ows": OpenLayers.Format.OWSCommon.v1_1_0.prototype.writers.ows │ │ │ │ │ - }, │ │ │ │ │ + /** │ │ │ │ │ + * Property: 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", │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: readers │ │ │ │ │ - * Contains public functions, grouped by namespace prefix, that will │ │ │ │ │ - * be applied when a namespaced node is found matching the function │ │ │ │ │ - * name. The function will be applied in the scope of this parser │ │ │ │ │ - * with two arguments: the node being read and a context object passed │ │ │ │ │ - * from the parent. │ │ │ │ │ - */ │ │ │ │ │ - readers: { │ │ │ │ │ - "wps": { │ │ │ │ │ - "ExecuteResponse": function(node, obj) { │ │ │ │ │ - obj.executeResponse = { │ │ │ │ │ - lang: node.getAttribute("lang"), │ │ │ │ │ - statusLocation: node.getAttribute("statusLocation"), │ │ │ │ │ - serviceInstance: node.getAttribute("serviceInstance"), │ │ │ │ │ - service: node.getAttribute("service") │ │ │ │ │ - }; │ │ │ │ │ - this.readChildNodes(node, obj.executeResponse); │ │ │ │ │ - }, │ │ │ │ │ - "Process": function(node, obj) { │ │ │ │ │ - obj.process = {}; │ │ │ │ │ - this.readChildNodes(node, obj.process); │ │ │ │ │ - }, │ │ │ │ │ - "Status": function(node, obj) { │ │ │ │ │ - obj.status = { │ │ │ │ │ - creationTime: node.getAttribute("creationTime") │ │ │ │ │ - }; │ │ │ │ │ - this.readChildNodes(node, obj.status); │ │ │ │ │ - }, │ │ │ │ │ - "ProcessSucceeded": function(node, obj) { │ │ │ │ │ - obj.processSucceeded = true; │ │ │ │ │ - }, │ │ │ │ │ - "ProcessOutputs": function(node, processDescription) { │ │ │ │ │ - processDescription.processOutputs = []; │ │ │ │ │ - this.readChildNodes(node, processDescription.processOutputs); │ │ │ │ │ - }, │ │ │ │ │ - "Output": function(node, processOutputs) { │ │ │ │ │ - var output = {}; │ │ │ │ │ - this.readChildNodes(node, output); │ │ │ │ │ - processOutputs.push(output); │ │ │ │ │ - }, │ │ │ │ │ - "Reference": function(node, output) { │ │ │ │ │ - output.reference = { │ │ │ │ │ - href: node.getAttribute("href"), │ │ │ │ │ - mimeType: node.getAttribute("mimeType"), │ │ │ │ │ - encoding: node.getAttribute("encoding"), │ │ │ │ │ - schema: node.getAttribute("schema") │ │ │ │ │ - }; │ │ │ │ │ - }, │ │ │ │ │ - "Data": function(node, output) { │ │ │ │ │ - output.data = {}; │ │ │ │ │ - this.readChildNodes(node, output); │ │ │ │ │ - }, │ │ │ │ │ - "LiteralData": function(node, output) { │ │ │ │ │ - output.literalData = { │ │ │ │ │ - dataType: node.getAttribute("dataType"), │ │ │ │ │ - uom: node.getAttribute("uom"), │ │ │ │ │ - value: this.getChildValue(node) │ │ │ │ │ - }; │ │ │ │ │ - }, │ │ │ │ │ - "ComplexData": function(node, output) { │ │ │ │ │ - output.complexData = { │ │ │ │ │ - mimeType: node.getAttribute("mimeType"), │ │ │ │ │ - schema: node.getAttribute("schema"), │ │ │ │ │ - encoding: node.getAttribute("encoding"), │ │ │ │ │ - value: "" │ │ │ │ │ - }; │ │ │ │ │ + /** │ │ │ │ │ + * 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, │ │ │ │ │ │ │ │ │ │ - // try to get *some* value, ignore the empty text values │ │ │ │ │ - if (this.isSimpleContent(node)) { │ │ │ │ │ - var child; │ │ │ │ │ - for (child = node.firstChild; child; child = child.nextSibling) { │ │ │ │ │ - switch (child.nodeType) { │ │ │ │ │ - case 3: // text node │ │ │ │ │ - case 4: // cdata section │ │ │ │ │ - output.complexData.value += child.nodeValue; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - } else { │ │ │ │ │ - for (child = node.firstChild; child; child = child.nextSibling) { │ │ │ │ │ - if (child.nodeType == 1) { │ │ │ │ │ - output.complexData.value = child; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ + /** APIProperty: tileOptions │ │ │ │ │ + * {Object} optional configuration options for instances │ │ │ │ │ + * created by this Layer, if supported by the tile class. │ │ │ │ │ + */ │ │ │ │ │ + tileOptions: null, │ │ │ │ │ │ │ │ │ │ - }, │ │ │ │ │ - "BoundingBox": function(node, output) { │ │ │ │ │ - output.boundingBoxData = { │ │ │ │ │ - dimensions: node.getAttribute("dimensions"), │ │ │ │ │ - crs: node.getAttribute("crs") │ │ │ │ │ - }; │ │ │ │ │ - this.readChildNodes(node, output.boundingBoxData); │ │ │ │ │ - } │ │ │ │ │ - }, │ │ │ │ │ + /** │ │ │ │ │ + * APIProperty: tileClass │ │ │ │ │ + * {} The tile class to use for this layer. │ │ │ │ │ + * Defaults is OpenLayers.Tile.Image. │ │ │ │ │ + */ │ │ │ │ │ + tileClass: OpenLayers.Tile.Image, │ │ │ │ │ │ │ │ │ │ - // TODO: we should add Exception parsing here │ │ │ │ │ - "ows": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers["ows"] │ │ │ │ │ - }, │ │ │ │ │ + /** │ │ │ │ │ + * Property: grid │ │ │ │ │ + * {Array(Array())} This is an array of rows, each row is │ │ │ │ │ + * an array of tiles. │ │ │ │ │ + */ │ │ │ │ │ + grid: null, │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.WPSExecute" │ │ │ │ │ + /** │ │ │ │ │ + * 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, │ │ │ │ │ │ │ │ │ │ - }); │ │ │ │ │ -/* ====================================================================== │ │ │ │ │ - OpenLayers/Events.js │ │ │ │ │ - ====================================================================== */ │ │ │ │ │ + /** 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, │ │ │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ - * full text of the license. */ │ │ │ │ │ + /** │ │ │ │ │ + * 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", │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * @requires OpenLayers/Util.js │ │ │ │ │ - */ │ │ │ │ │ + /** │ │ │ │ │ + * APIProperty: numLoadingTiles │ │ │ │ │ + * {Integer} How many tiles are still loading? │ │ │ │ │ + */ │ │ │ │ │ + numLoadingTiles: 0, │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * Namespace: OpenLayers.Event │ │ │ │ │ - * Utility functions for event handling. │ │ │ │ │ - */ │ │ │ │ │ -OpenLayers.Event = { │ │ │ │ │ + /** │ │ │ │ │ + * Property: serverResolutions │ │ │ │ │ + * {Array(Number}} This property is documented in subclasses as │ │ │ │ │ + * an API property. │ │ │ │ │ + */ │ │ │ │ │ + serverResolutions: null, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: observers │ │ │ │ │ - * {Object} A hashtable cache of the event observers. Keyed by │ │ │ │ │ - * element._eventCacheID │ │ │ │ │ + /** │ │ │ │ │ + * Property: loading │ │ │ │ │ + * {Boolean} Indicates if tiles are being loaded. │ │ │ │ │ */ │ │ │ │ │ - observers: false, │ │ │ │ │ + loading: false, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Constant: KEY_SPACE │ │ │ │ │ - * {int} │ │ │ │ │ + * Property: backBuffer │ │ │ │ │ + * {DOMElement} The back buffer. │ │ │ │ │ */ │ │ │ │ │ - KEY_SPACE: 32, │ │ │ │ │ + backBuffer: null, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Constant: KEY_BACKSPACE │ │ │ │ │ - * {int} │ │ │ │ │ + /** │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - KEY_BACKSPACE: 8, │ │ │ │ │ + gridResolution: null, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Constant: KEY_TAB │ │ │ │ │ - * {int} │ │ │ │ │ + /** │ │ │ │ │ + * Property: backBufferResolution │ │ │ │ │ + * {Number} The resolution of the current back buffer. This property is │ │ │ │ │ + * updated each time a back buffer is created. │ │ │ │ │ */ │ │ │ │ │ - KEY_TAB: 9, │ │ │ │ │ + backBufferResolution: null, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Constant: KEY_RETURN │ │ │ │ │ - * {int} │ │ │ │ │ + /** │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - KEY_RETURN: 13, │ │ │ │ │ + backBufferLonLat: null, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Constant: KEY_ESC │ │ │ │ │ - * {int} │ │ │ │ │ + /** │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - KEY_ESC: 27, │ │ │ │ │ + backBufferTimerId: null, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Constant: KEY_LEFT │ │ │ │ │ - * {int} │ │ │ │ │ + /** │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - KEY_LEFT: 37, │ │ │ │ │ + removeBackBufferDelay: null, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Constant: KEY_UP │ │ │ │ │ - * {int} │ │ │ │ │ + /** │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - KEY_UP: 38, │ │ │ │ │ + className: null, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Constant: KEY_RIGHT │ │ │ │ │ - * {int} │ │ │ │ │ + /** │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - KEY_RIGHT: 39, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Constant: KEY_DOWN │ │ │ │ │ - * {int} │ │ │ │ │ + /** │ │ │ │ │ + * Property: gridLayout │ │ │ │ │ + * {Object} Object containing properties tilelon, tilelat, startcol, │ │ │ │ │ + * startrow │ │ │ │ │ */ │ │ │ │ │ - KEY_DOWN: 40, │ │ │ │ │ + gridLayout: null, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Constant: KEY_DELETE │ │ │ │ │ - * {int} │ │ │ │ │ + /** │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - KEY_DELETE: 46, │ │ │ │ │ + rowSign: null, │ │ │ │ │ │ │ │ │ │ + /** │ │ │ │ │ + * Property: transitionendEvents │ │ │ │ │ + * {Array} Event names for transitionend │ │ │ │ │ + */ │ │ │ │ │ + transitionendEvents: [ │ │ │ │ │ + 'transitionend', 'webkitTransitionEnd', 'otransitionend', │ │ │ │ │ + 'oTransitionEnd' │ │ │ │ │ + ], │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: element │ │ │ │ │ - * Cross browser event element detection. │ │ │ │ │ - * │ │ │ │ │ + * Constructor: OpenLayers.Layer.Grid │ │ │ │ │ + * Create a new grid layer │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * event - {Event} │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {DOMElement} The element that caused the event │ │ │ │ │ + * name - {String} │ │ │ │ │ + * url - {String} │ │ │ │ │ + * params - {Object} │ │ │ │ │ + * options - {Object} Hashtable of extra options to tag onto the layer │ │ │ │ │ */ │ │ │ │ │ - element: function(event) { │ │ │ │ │ - return event.target || event.srcElement; │ │ │ │ │ + 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: isSingleTouch │ │ │ │ │ - * Determine whether event was caused by a single touch │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * event - {Event} │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Boolean} │ │ │ │ │ + * Method: initProperties │ │ │ │ │ + * Set any properties that depend on the value of singleTile. │ │ │ │ │ + * Currently sets removeBackBufferDelay and className │ │ │ │ │ */ │ │ │ │ │ - isSingleTouch: function(event) { │ │ │ │ │ - return event.touches && event.touches.length == 1; │ │ │ │ │ + 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: isMultiTouch │ │ │ │ │ - * Determine whether event was caused by a multi touch │ │ │ │ │ + * Method: setMap │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * event - {Event} │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Boolean} │ │ │ │ │ + * map - {} The map. │ │ │ │ │ */ │ │ │ │ │ - isMultiTouch: function(event) { │ │ │ │ │ - return event.touches && event.touches.length > 1; │ │ │ │ │ + setMap: function(map) { │ │ │ │ │ + OpenLayers.Layer.HTTPRequest.prototype.setMap.call(this, map); │ │ │ │ │ + OpenLayers.Element.addClass(this.div, this.className); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: isLeftClick │ │ │ │ │ - * Determine whether event was caused by a left click. │ │ │ │ │ + * Method: removeMap │ │ │ │ │ + * Called when the layer is removed from the map. │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * event - {Event} │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Boolean} │ │ │ │ │ + * map - {} The map. │ │ │ │ │ */ │ │ │ │ │ - isLeftClick: function(event) { │ │ │ │ │ - return (((event.which) && (event.which == 1)) || │ │ │ │ │ - ((event.button) && (event.button == 1))); │ │ │ │ │ + removeMap: function(map) { │ │ │ │ │ + this.removeBackBuffer(); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: isRightClick │ │ │ │ │ - * Determine whether event was caused by a right mouse click. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * event - {Event} │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Boolean} │ │ │ │ │ + * APIMethod: destroy │ │ │ │ │ + * Deconstruct the layer and clear the grid. │ │ │ │ │ */ │ │ │ │ │ - isRightClick: function(event) { │ │ │ │ │ - return (((event.which) && (event.which == 3)) || │ │ │ │ │ - ((event.button) && (event.button == 2))); │ │ │ │ │ + destroy: function() { │ │ │ │ │ + this.removeBackBuffer(); │ │ │ │ │ + this.clearGrid(); │ │ │ │ │ + │ │ │ │ │ + this.grid = null; │ │ │ │ │ + this.tileSize = null; │ │ │ │ │ + OpenLayers.Layer.HTTPRequest.prototype.destroy.apply(this, arguments); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: stop │ │ │ │ │ - * Stops an event from propagating. │ │ │ │ │ + * 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} │ │ │ │ │ * │ │ │ │ │ - * 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. │ │ │ │ │ + * Returns: │ │ │ │ │ + * redrawn: {Boolean} whether the layer was actually redrawn. │ │ │ │ │ */ │ │ │ │ │ - stop: function(event, allowDefault) { │ │ │ │ │ - │ │ │ │ │ - if (!allowDefault) { │ │ │ │ │ - OpenLayers.Event.preventDefault(event); │ │ │ │ │ - } │ │ │ │ │ │ │ │ │ │ - if (event.stopPropagation) { │ │ │ │ │ - event.stopPropagation(); │ │ │ │ │ - } else { │ │ │ │ │ - event.cancelBubble = true; │ │ │ │ │ + /** │ │ │ │ │ + * Method: clearGrid │ │ │ │ │ + * Go through and remove all tiles from the grid, calling │ │ │ │ │ + * destroy() on each of them to kill circular references │ │ │ │ │ + */ │ │ │ │ │ + clearGrid: function() { │ │ │ │ │ + if (this.grid) { │ │ │ │ │ + for (var iRow = 0, len = this.grid.length; iRow < len; iRow++) { │ │ │ │ │ + var row = this.grid[iRow]; │ │ │ │ │ + for (var iCol = 0, clen = row.length; iCol < clen; iCol++) { │ │ │ │ │ + var tile = row[iCol]; │ │ │ │ │ + this.destroyTile(tile); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + this.grid = []; │ │ │ │ │ + this.gridResolution = null; │ │ │ │ │ + this.gridLayout = null; │ │ │ │ │ } │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: preventDefault │ │ │ │ │ - * Cancels the event if it is cancelable, without stopping further │ │ │ │ │ - * propagation of the event. │ │ │ │ │ - * │ │ │ │ │ + * APIMethod: addOptions │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * event - {Event} │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - preventDefault: function(event) { │ │ │ │ │ - if (event.preventDefault) { │ │ │ │ │ - event.preventDefault(); │ │ │ │ │ - } else { │ │ │ │ │ - event.returnValue = false; │ │ │ │ │ + addOptions: function(newOptions, reinitialize) { │ │ │ │ │ + var singleTileChanged = newOptions.singleTile !== undefined && │ │ │ │ │ + newOptions.singleTile !== this.singleTile; │ │ │ │ │ + OpenLayers.Layer.HTTPRequest.prototype.addOptions.apply(this, arguments); │ │ │ │ │ + if (this.map && singleTileChanged) { │ │ │ │ │ + this.initProperties(); │ │ │ │ │ + this.clearGrid(); │ │ │ │ │ + this.tileSize = this.options.tileSize; │ │ │ │ │ + this.setTileSize(); │ │ │ │ │ + this.moveTo(null, true); │ │ │ │ │ } │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: findElement │ │ │ │ │ - * │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: clone │ │ │ │ │ + * Create a clone of this layer │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * event - {Event} │ │ │ │ │ - * tagName - {String} │ │ │ │ │ + * obj - {Object} Is this ever used? │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {DOMElement} The first node with the given tagName, starting from the │ │ │ │ │ - * node the event was triggered on and traversing the DOM upwards │ │ │ │ │ + * {} An exact clone of this OpenLayers.Layer.Grid │ │ │ │ │ */ │ │ │ │ │ - findElement: function(event, tagName) { │ │ │ │ │ - var element = OpenLayers.Event.element(event); │ │ │ │ │ - while (element.parentNode && (!element.tagName || │ │ │ │ │ - (element.tagName.toUpperCase() != tagName.toUpperCase()))) { │ │ │ │ │ - element = element.parentNode; │ │ │ │ │ + clone: function(obj) { │ │ │ │ │ + │ │ │ │ │ + if (obj == null) { │ │ │ │ │ + obj = new OpenLayers.Layer.Grid(this.name, │ │ │ │ │ + this.url, │ │ │ │ │ + this.params, │ │ │ │ │ + this.getOptions()); │ │ │ │ │ } │ │ │ │ │ - return element; │ │ │ │ │ + │ │ │ │ │ + //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: observe │ │ │ │ │ - * │ │ │ │ │ + /** │ │ │ │ │ + * 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: │ │ │ │ │ - * elementParam - {DOMElement || String} │ │ │ │ │ - * name - {String} │ │ │ │ │ - * observer - {function} │ │ │ │ │ - * useCapture - {Boolean} │ │ │ │ │ + * bounds - {} │ │ │ │ │ + * zoomChanged - {Boolean} │ │ │ │ │ + * dragging - {Boolean} │ │ │ │ │ */ │ │ │ │ │ - observe: function(elementParam, name, observer, useCapture) { │ │ │ │ │ - var element = OpenLayers.Util.getElement(elementParam); │ │ │ │ │ - useCapture = useCapture || false; │ │ │ │ │ + moveTo: function(bounds, zoomChanged, dragging) { │ │ │ │ │ │ │ │ │ │ - if (name == 'keypress' && │ │ │ │ │ - (navigator.appVersion.match(/Konqueror|Safari|KHTML/) || │ │ │ │ │ - element.attachEvent)) { │ │ │ │ │ - name = 'keydown'; │ │ │ │ │ - } │ │ │ │ │ + OpenLayers.Layer.HTTPRequest.prototype.moveTo.apply(this, arguments); │ │ │ │ │ │ │ │ │ │ - //if observers cache has not yet been created, create it │ │ │ │ │ - if (!this.observers) { │ │ │ │ │ - this.observers = {}; │ │ │ │ │ - } │ │ │ │ │ + bounds = bounds || this.map.getExtent(); │ │ │ │ │ │ │ │ │ │ - //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); │ │ │ │ │ - } │ │ │ │ │ + if (bounds != null) { │ │ │ │ │ │ │ │ │ │ - var cacheID = element._eventCacheID; │ │ │ │ │ + // if grid is empty or zoom has changed, we *must* re-tile │ │ │ │ │ + var forceReTile = !this.grid.length || zoomChanged; │ │ │ │ │ │ │ │ │ │ - //if there is not yet a hash entry for this element, add one │ │ │ │ │ - if (!this.observers[cacheID]) { │ │ │ │ │ - this.observers[cacheID] = []; │ │ │ │ │ - } │ │ │ │ │ + // total bounds of the tiles │ │ │ │ │ + var tilesBounds = this.getTilesBounds(); │ │ │ │ │ │ │ │ │ │ - //add a new observer to this element's list │ │ │ │ │ - this.observers[cacheID].push({ │ │ │ │ │ - 'element': element, │ │ │ │ │ - 'name': name, │ │ │ │ │ - 'observer': observer, │ │ │ │ │ - 'useCapture': useCapture │ │ │ │ │ - }); │ │ │ │ │ + // the new map resolution │ │ │ │ │ + var resolution = this.map.getResolution(); │ │ │ │ │ │ │ │ │ │ - //add the actual browser event listener │ │ │ │ │ - if (element.addEventListener) { │ │ │ │ │ - element.addEventListener(name, observer, useCapture); │ │ │ │ │ - } else if (element.attachEvent) { │ │ │ │ │ - element.attachEvent('on' + name, observer); │ │ │ │ │ + // 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: 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} │ │ │ │ │ + /** │ │ │ │ │ + * 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). │ │ │ │ │ */ │ │ │ │ │ - stopObservingElement: function(elementParam) { │ │ │ │ │ - var element = OpenLayers.Util.getElement(elementParam); │ │ │ │ │ - var cacheID = element._eventCacheID; │ │ │ │ │ + getTileData: function(loc) { │ │ │ │ │ + var data = null, │ │ │ │ │ + x = loc.lon, │ │ │ │ │ + y = loc.lat, │ │ │ │ │ + numRows = this.grid.length; │ │ │ │ │ │ │ │ │ │ - this._removeElementObservers(OpenLayers.Event.observers[cacheID]); │ │ │ │ │ + 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: _removeElementObservers │ │ │ │ │ + * Method: destroyTile │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * elementObservers - {Array(Object)} Array of (element, name, │ │ │ │ │ - * observer, usecapture) objects, │ │ │ │ │ - * taken directly from hashtable │ │ │ │ │ + * tile - {} │ │ │ │ │ */ │ │ │ │ │ - _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 │ │ │ │ │ - ]); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ + destroyTile: function(tile) { │ │ │ │ │ + this.removeTileMonitoringHooks(tile); │ │ │ │ │ + tile.destroy(); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: stopObserving │ │ │ │ │ - * │ │ │ │ │ + * Method: getServerResolution │ │ │ │ │ + * Return the closest server-supported resolution. │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * elementParam - {DOMElement || String} │ │ │ │ │ - * name - {String} │ │ │ │ │ - * observer - {function} │ │ │ │ │ - * useCapture - {Boolean} │ │ │ │ │ - * │ │ │ │ │ + * resolution - {Number} The base resolution. If undefined the │ │ │ │ │ + * map resolution is used. │ │ │ │ │ + * │ │ │ │ │ * Returns: │ │ │ │ │ - * {Boolean} Whether or not the event observer was removed │ │ │ │ │ + * {Number} The closest server resolution value. │ │ │ │ │ */ │ │ │ │ │ - stopObserving: function(elementParam, name, observer, useCapture) { │ │ │ │ │ - useCapture = useCapture || false; │ │ │ │ │ + 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; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - var element = OpenLayers.Util.getElement(elementParam); │ │ │ │ │ - var cacheID = element._eventCacheID; │ │ │ │ │ + /** │ │ │ │ │ + * 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); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - if (name == 'keypress') { │ │ │ │ │ - if (navigator.appVersion.match(/Konqueror|Safari|KHTML/) || │ │ │ │ │ - element.detachEvent) { │ │ │ │ │ - name = 'keydown'; │ │ │ │ │ + /** │ │ │ │ │ + * 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; │ │ │ │ │ } │ │ │ │ │ │ │ │ │ │ - // find element's entry in this.observers cache and remove it │ │ │ │ │ - var foundEntry = false; │ │ │ │ │ - var elementObservers = OpenLayers.Event.observers[cacheID]; │ │ │ │ │ - if (elementObservers) { │ │ │ │ │ + var ratio = this.backBufferResolution / resolution; │ │ │ │ │ │ │ │ │ │ - // find the specific event type in the element's list │ │ │ │ │ - var i = 0; │ │ │ │ │ - while (!foundEntry && i < elementObservers.length) { │ │ │ │ │ - var cacheEntry = elementObservers[i]; │ │ │ │ │ + // 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'; │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ - if ((cacheEntry.name == name) && │ │ │ │ │ - (cacheEntry.observer == observer) && │ │ │ │ │ - (cacheEntry.useCapture == useCapture)) { │ │ │ │ │ + // 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'; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - elementObservers.splice(i, 1); │ │ │ │ │ - if (elementObservers.length == 0) { │ │ │ │ │ - delete OpenLayers.Event.observers[cacheID]; │ │ │ │ │ + /** │ │ │ │ │ + * Method: createBackBuffer │ │ │ │ │ + * Create a back buffer. │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {DOMElement} The DOM element for the back buffer, undefined if the │ │ │ │ │ + * grid isn't initialized yet. │ │ │ │ │ + */ │ │ │ │ │ + createBackBuffer: function() { │ │ │ │ │ + var backBuffer; │ │ │ │ │ + if (this.grid.length > 0) { │ │ │ │ │ + backBuffer = document.createElement('div'); │ │ │ │ │ + backBuffer.id = this.div.id + '_bb'; │ │ │ │ │ + backBuffer.className = 'olBackBuffer'; │ │ │ │ │ + backBuffer.style.position = 'absolute'; │ │ │ │ │ + var map = this.map; │ │ │ │ │ + backBuffer.style.zIndex = this.transitionEffect === 'resize' ? │ │ │ │ │ + this.getZIndex() - 1 : │ │ │ │ │ + // 'map-resize': │ │ │ │ │ + map.Z_INDEX_BASE.BaseLayer - │ │ │ │ │ + (map.getNumLayers() - map.getLayerIndex(this)); │ │ │ │ │ + for (var i = 0, lenI = this.grid.length; i < lenI; i++) { │ │ │ │ │ + for (var j = 0, lenJ = this.grid[i].length; j < lenJ; j++) { │ │ │ │ │ + var tile = this.grid[i][j], │ │ │ │ │ + markup = this.grid[i][j].createBackBuffer(); │ │ │ │ │ + if (markup) { │ │ │ │ │ + markup._i = i; │ │ │ │ │ + markup._j = j; │ │ │ │ │ + markup._w = tile.size.w; │ │ │ │ │ + markup._h = tile.size.h; │ │ │ │ │ + markup.id = tile.id + '_bb'; │ │ │ │ │ + backBuffer.appendChild(markup); │ │ │ │ │ } │ │ │ │ │ - foundEntry = true; │ │ │ │ │ - break; │ │ │ │ │ } │ │ │ │ │ - i++; │ │ │ │ │ } │ │ │ │ │ } │ │ │ │ │ + return backBuffer; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - //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); │ │ │ │ │ + /** │ │ │ │ │ + * Method: removeBackBuffer │ │ │ │ │ + * Remove back buffer from DOM. │ │ │ │ │ + */ │ │ │ │ │ + removeBackBuffer: function() { │ │ │ │ │ + if (this._transitionElement) { │ │ │ │ │ + for (var i = this.transitionendEvents.length - 1; i >= 0; --i) { │ │ │ │ │ + OpenLayers.Event.stopObserving(this._transitionElement, │ │ │ │ │ + this.transitionendEvents[i], this._removeBackBuffer); │ │ │ │ │ + } │ │ │ │ │ + delete this._transitionElement; │ │ │ │ │ + } │ │ │ │ │ + if (this.backBuffer) { │ │ │ │ │ + if (this.backBuffer.parentNode) { │ │ │ │ │ + this.backBuffer.parentNode.removeChild(this.backBuffer); │ │ │ │ │ + } │ │ │ │ │ + this.backBuffer = null; │ │ │ │ │ + this.backBufferResolution = null; │ │ │ │ │ + if (this.backBufferTimerId !== null) { │ │ │ │ │ + window.clearTimeout(this.backBufferTimerId); │ │ │ │ │ + this.backBufferTimerId = null; │ │ │ │ │ } │ │ │ │ │ } │ │ │ │ │ - return foundEntry; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: unloadCache │ │ │ │ │ - * Cycle through all the element entries in the events cache and call │ │ │ │ │ - * stopObservingElement on each. │ │ │ │ │ + /** │ │ │ │ │ + * Method: moveByPx │ │ │ │ │ + * Move the layer based on pixel vector. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * dx - {Number} │ │ │ │ │ + * dy - {Number} │ │ │ │ │ */ │ │ │ │ │ - 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; │ │ │ │ │ + moveByPx: function(dx, dy) { │ │ │ │ │ + if (!this.singleTile) { │ │ │ │ │ + this.moveGriddedTiles(); │ │ │ │ │ } │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Event" │ │ │ │ │ -}; │ │ │ │ │ + /** │ │ │ │ │ + * 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]); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ -/* prevent memory leaks in IE */ │ │ │ │ │ -OpenLayers.Event.observe(window, 'unload', OpenLayers.Event.unloadCache, false); │ │ │ │ │ + /** │ │ │ │ │ + * 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; │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * Class: OpenLayers.Events │ │ │ │ │ - */ │ │ │ │ │ -OpenLayers.Events = OpenLayers.Class({ │ │ │ │ │ + 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(); │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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" │ │ │ │ │ - ], │ │ │ │ │ + bounds = new OpenLayers.Bounds(bottomLeftTileBounds.left, │ │ │ │ │ + bottomLeftTileBounds.bottom, │ │ │ │ │ + bottomLeftTileBounds.left + width, │ │ │ │ │ + bottomLeftTileBounds.bottom + height); │ │ │ │ │ + } │ │ │ │ │ + return bounds; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: listeners │ │ │ │ │ - * {Object} Hashtable of Array(Function): events listener functions │ │ │ │ │ + /** │ │ │ │ │ + * Method: initSingleTile │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * bounds - {} │ │ │ │ │ */ │ │ │ │ │ - listeners: null, │ │ │ │ │ + initSingleTile: function(bounds) { │ │ │ │ │ + this.events.triggerEvent("retile"); │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: object │ │ │ │ │ - * {Object} the code object issuing application events │ │ │ │ │ - */ │ │ │ │ │ - object: null, │ │ │ │ │ + //determine new tile bounds │ │ │ │ │ + var center = bounds.getCenterLonLat(); │ │ │ │ │ + var tileWidth = bounds.getWidth() * this.ratio; │ │ │ │ │ + var tileHeight = bounds.getHeight() * this.ratio; │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: element │ │ │ │ │ - * {DOMElement} the DOM element receiving browser events │ │ │ │ │ - */ │ │ │ │ │ - element: null, │ │ │ │ │ + var tileBounds = │ │ │ │ │ + new OpenLayers.Bounds(center.lon - (tileWidth / 2), │ │ │ │ │ + center.lat - (tileHeight / 2), │ │ │ │ │ + center.lon + (tileWidth / 2), │ │ │ │ │ + center.lat + (tileHeight / 2)); │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: eventHandler │ │ │ │ │ - * {Function} bound event handler attached to elements │ │ │ │ │ - */ │ │ │ │ │ - eventHandler: null, │ │ │ │ │ + var px = this.map.getLayerPxFromLonLat({ │ │ │ │ │ + lon: tileBounds.left, │ │ │ │ │ + lat: tileBounds.top │ │ │ │ │ + }); │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIProperty: fallThrough │ │ │ │ │ - * {Boolean} │ │ │ │ │ - */ │ │ │ │ │ - fallThrough: null, │ │ │ │ │ + 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(); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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) │ │ │ │ │ - * } │ │ │ │ │ + * Method: calculateGridLayout │ │ │ │ │ + * Generate parameters for the grid layout. │ │ │ │ │ * │ │ │ │ │ - * 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. │ │ │ │ │ + * 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} │ │ │ │ │ * │ │ │ │ │ - * 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 │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Object} Object containing properties tilelon, tilelat, startcol, │ │ │ │ │ + * startrow │ │ │ │ │ */ │ │ │ │ │ - includeXY: false, │ │ │ │ │ + calculateGridLayout: function(bounds, origin, resolution) { │ │ │ │ │ + var tilelon = resolution * this.tileSize.w; │ │ │ │ │ + var tilelat = resolution * this.tileSize.h; │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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, │ │ │ │ │ + var offsetlon = bounds.left - origin.lon; │ │ │ │ │ + var tilecol = Math.floor(offsetlon / tilelon) - this.buffer; │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: extensionCount │ │ │ │ │ - * {Object} Keys are event types (like in ), values are the │ │ │ │ │ - * number of extension listeners for each event type. │ │ │ │ │ - */ │ │ │ │ │ - extensionCount: null, │ │ │ │ │ + var rowSign = this.rowSign; │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: clearMouseListener │ │ │ │ │ - * A version of that is bound to this instance so that │ │ │ │ │ - * it can be used with and │ │ │ │ │ - * . │ │ │ │ │ - */ │ │ │ │ │ - clearMouseListener: null, │ │ │ │ │ + var offsetlat = rowSign * (origin.lat - bounds.top + tilelat); │ │ │ │ │ + var tilerow = Math[~rowSign ? 'floor' : 'ceil'](offsetlat / tilelat) - this.buffer * rowSign; │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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 = []; │ │ │ │ │ + return { │ │ │ │ │ + tilelon: tilelon, │ │ │ │ │ + tilelat: tilelat, │ │ │ │ │ + startcol: tilecol, │ │ │ │ │ + startrow: tilerow │ │ │ │ │ + }; │ │ │ │ │ │ │ │ │ │ - // 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 │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - 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 │ │ │ │ │ - ); │ │ │ │ │ - } │ │ │ │ │ + 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]]); │ │ │ │ │ } │ │ │ │ │ - this.element = null; │ │ │ │ │ - │ │ │ │ │ - this.listeners = null; │ │ │ │ │ - this.object = null; │ │ │ │ │ - this.fallThrough = null; │ │ │ │ │ - this.eventHandler = null; │ │ │ │ │ + return origin; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: addEventType │ │ │ │ │ - * Deprecated. Any event can be triggered without adding it first. │ │ │ │ │ - * │ │ │ │ │ + * Method: getTileBoundsForGridIndex │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * eventName - {String} │ │ │ │ │ + * row - {Number} The row of the grid │ │ │ │ │ + * col - {Number} The column of the grid │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {} The bounds for the tile at (row, col) │ │ │ │ │ */ │ │ │ │ │ - addEventType: function(eventName) {}, │ │ │ │ │ + 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: attachToElement │ │ │ │ │ - * │ │ │ │ │ + * Method: initGriddedTiles │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * element - {HTMLDOMElement} a DOM element to attach browser events to │ │ │ │ │ + * bounds - {} │ │ │ │ │ */ │ │ │ │ │ - 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 │ │ │ │ │ - ); │ │ │ │ │ + initGriddedTiles: function(bounds) { │ │ │ │ │ + this.events.triggerEvent("retile"); │ │ │ │ │ │ │ │ │ │ - // 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); │ │ │ │ │ + // work out mininum number of rows and columns; this is the number of │ │ │ │ │ + // tiles required to cover the viewport plus at least one for panning │ │ │ │ │ + │ │ │ │ │ + var viewSize = this.map.getSize(); │ │ │ │ │ + │ │ │ │ │ + var origin = this.getTileOrigin(); │ │ │ │ │ + var resolution = this.map.getResolution(), │ │ │ │ │ + serverResolution = this.getServerResolution(), │ │ │ │ │ + ratio = resolution / serverResolution, │ │ │ │ │ + tileSize = { │ │ │ │ │ + w: this.tileSize.w / ratio, │ │ │ │ │ + h: this.tileSize.h / ratio │ │ │ │ │ + }; │ │ │ │ │ + │ │ │ │ │ + var minRows = Math.ceil(viewSize.h / tileSize.h) + │ │ │ │ │ + 2 * this.buffer + 1; │ │ │ │ │ + var minCols = Math.ceil(viewSize.w / tileSize.w) + │ │ │ │ │ + 2 * this.buffer + 1; │ │ │ │ │ + │ │ │ │ │ + var tileLayout = this.calculateGridLayout(bounds, origin, serverResolution); │ │ │ │ │ + this.gridLayout = tileLayout; │ │ │ │ │ + │ │ │ │ │ + var tilelon = tileLayout.tilelon; │ │ │ │ │ + var tilelat = tileLayout.tilelat; │ │ │ │ │ + │ │ │ │ │ + var layerContainerDivLeft = this.map.layerContainerOriginPx.x; │ │ │ │ │ + var layerContainerDivTop = this.map.layerContainerOriginPx.y; │ │ │ │ │ + │ │ │ │ │ + var tileBounds = this.getTileBoundsForGridIndex(0, 0); │ │ │ │ │ + var startPx = this.map.getViewPortPxFromLonLat( │ │ │ │ │ + new OpenLayers.LonLat(tileBounds.left, tileBounds.top) │ │ │ │ │ + ); │ │ │ │ │ + startPx.x = Math.round(startPx.x) - layerContainerDivLeft; │ │ │ │ │ + startPx.y = Math.round(startPx.y) - layerContainerDivTop; │ │ │ │ │ + │ │ │ │ │ + var tileData = [], │ │ │ │ │ + center = this.map.getCenter(); │ │ │ │ │ + │ │ │ │ │ + var rowidx = 0; │ │ │ │ │ + do { │ │ │ │ │ + var row = this.grid[rowidx]; │ │ │ │ │ + if (!row) { │ │ │ │ │ + row = []; │ │ │ │ │ + this.grid.push(row); │ │ │ │ │ } │ │ │ │ │ + │ │ │ │ │ + var colidx = 0; │ │ │ │ │ + do { │ │ │ │ │ + tileBounds = this.getTileBoundsForGridIndex(rowidx, colidx); │ │ │ │ │ + var px = startPx.clone(); │ │ │ │ │ + px.x = px.x + colidx * Math.round(tileSize.w); │ │ │ │ │ + px.y = px.y + rowidx * Math.round(tileSize.h); │ │ │ │ │ + var tile = row[colidx]; │ │ │ │ │ + if (!tile) { │ │ │ │ │ + tile = this.addTile(tileBounds, px); │ │ │ │ │ + this.addTileMonitoringHooks(tile); │ │ │ │ │ + row.push(tile); │ │ │ │ │ + } else { │ │ │ │ │ + tile.moveTo(tileBounds, px, false); │ │ │ │ │ + } │ │ │ │ │ + var tileCenter = tileBounds.getCenterLonLat(); │ │ │ │ │ + tileData.push({ │ │ │ │ │ + tile: tile, │ │ │ │ │ + distance: Math.pow(tileCenter.lon - center.lon, 2) + │ │ │ │ │ + Math.pow(tileCenter.lat - center.lat, 2) │ │ │ │ │ + }); │ │ │ │ │ + │ │ │ │ │ + colidx += 1; │ │ │ │ │ + } while ((tileBounds.right <= bounds.right + tilelon * this.buffer) || │ │ │ │ │ + colidx < minCols); │ │ │ │ │ + │ │ │ │ │ + rowidx += 1; │ │ │ │ │ + } while ((tileBounds.bottom >= bounds.bottom - tilelat * this.buffer) || │ │ │ │ │ + rowidx < minRows); │ │ │ │ │ + │ │ │ │ │ + //shave off exceess rows and colums │ │ │ │ │ + this.removeExcessTiles(rowidx, colidx); │ │ │ │ │ + │ │ │ │ │ + var resolution = this.getServerResolution(); │ │ │ │ │ + // store the resolution of the grid │ │ │ │ │ + this.gridResolution = resolution; │ │ │ │ │ + │ │ │ │ │ + //now actually draw the tiles │ │ │ │ │ + tileData.sort(function(a, b) { │ │ │ │ │ + return a.distance - b.distance; │ │ │ │ │ + }); │ │ │ │ │ + for (var i = 0, ii = tileData.length; i < ii; ++i) { │ │ │ │ │ + tileData[i].tile.draw(); │ │ │ │ │ } │ │ │ │ │ - // 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) │ │ │ │ │ + * Method: getMaxExtent │ │ │ │ │ + * Get this layer's maximum extent. (Implemented as a getter for │ │ │ │ │ + * potential specific implementations in sub-classes.) │ │ │ │ │ * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * object - {Object} │ │ │ │ │ + * Returns: │ │ │ │ │ + * {} │ │ │ │ │ */ │ │ │ │ │ - on: function(object) { │ │ │ │ │ - for (var type in object) { │ │ │ │ │ - if (type != "scope" && object.hasOwnProperty(type)) { │ │ │ │ │ - this.register(type, object.scope, object[type]); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ + getMaxExtent: function() { │ │ │ │ │ + return this.maxExtent; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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(); │ │ │ │ │ + * APIMethod: addTile │ │ │ │ │ + * Create a tile, initialize it, and add it to the layer div. │ │ │ │ │ * │ │ │ │ │ - * 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. │ │ │ │ │ + * Parameters │ │ │ │ │ + * bounds - {} │ │ │ │ │ + * position - {} │ │ │ │ │ * │ │ │ │ │ - * 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. │ │ │ │ │ + * Returns: │ │ │ │ │ + * {} The added OpenLayers.Tile │ │ │ │ │ */ │ │ │ │ │ - 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; │ │ │ │ │ + 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"); │ │ │ │ │ } │ │ │ │ │ - var listeners = this.listeners[type]; │ │ │ │ │ - if (!listeners) { │ │ │ │ │ - listeners = []; │ │ │ │ │ - this.listeners[type] = listeners; │ │ │ │ │ - this.extensionCount[type] = 0; │ │ │ │ │ + this.events.triggerEvent("tileloadstart", { │ │ │ │ │ + tile: tile │ │ │ │ │ + }); │ │ │ │ │ + this.numLoadingTiles++; │ │ │ │ │ + if (!this.singleTile && this.backBuffer && this.gridResolution === this.backBufferResolution) { │ │ │ │ │ + OpenLayers.Element.addClass(tile.getTile(), replacingCls); │ │ │ │ │ } │ │ │ │ │ - var listener = { │ │ │ │ │ - obj: obj, │ │ │ │ │ - func: func │ │ │ │ │ - }; │ │ │ │ │ - if (priority) { │ │ │ │ │ - listeners.splice(this.extensionCount[type], 0, listener); │ │ │ │ │ - if (typeof priority === "object" && priority.extension) { │ │ │ │ │ - this.extensionCount[type]++; │ │ │ │ │ + }; │ │ │ │ │ + │ │ │ │ │ + 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 { │ │ │ │ │ - listeners.push(listener); │ │ │ │ │ + break; │ │ │ │ │ } │ │ │ │ │ } │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ - * │ │ │ │ │ + * Method: shiftRow │ │ │ │ │ + * Shifty grid work │ │ │ │ │ * │ │ │ │ │ * 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. │ │ │ │ │ + * prepend - {Boolean} if true, prepend to beginning. │ │ │ │ │ + * if false, then append to end │ │ │ │ │ + * tileSize - {Object} rendered tile size; object with w and h properties │ │ │ │ │ */ │ │ │ │ │ - registerPriority: function(type, obj, func) { │ │ │ │ │ - this.register(type, obj, func, true); │ │ │ │ │ + shiftRow: function(prepend, tileSize) { │ │ │ │ │ + var grid = this.grid; │ │ │ │ │ + var rowIndex = prepend ? 0 : (grid.length - 1); │ │ │ │ │ + var sign = prepend ? -1 : 1; │ │ │ │ │ + var rowSign = this.rowSign; │ │ │ │ │ + var tileLayout = this.gridLayout; │ │ │ │ │ + tileLayout.startrow += sign * rowSign; │ │ │ │ │ + │ │ │ │ │ + var modelRow = grid[rowIndex]; │ │ │ │ │ + var row = grid[prepend ? 'pop' : 'shift'](); │ │ │ │ │ + for (var i = 0, len = row.length; i < len; i++) { │ │ │ │ │ + var tile = row[i]; │ │ │ │ │ + var position = modelRow[i].position.clone(); │ │ │ │ │ + position.y += tileSize.h * sign; │ │ │ │ │ + tile.moveTo(this.getTileBoundsForGridIndex(rowIndex, i), position); │ │ │ │ │ + } │ │ │ │ │ + grid[prepend ? 'unshift' : 'push'](row); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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 │ │ │ │ │ - * }); │ │ │ │ │ + * Method: shiftColumn │ │ │ │ │ + * Shift grid work in the other dimension │ │ │ │ │ * │ │ │ │ │ - * // this is equivalent to the following │ │ │ │ │ - * events.unregister("loadstart", object, loadStartListener); │ │ │ │ │ - * events.unregister("loadend", object, loadEndListener); │ │ │ │ │ - * (end) │ │ │ │ │ + * 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 │ │ │ │ │ */ │ │ │ │ │ - un: function(object) { │ │ │ │ │ - for (var type in object) { │ │ │ │ │ - if (type != "scope" && object.hasOwnProperty(type)) { │ │ │ │ │ - this.unregister(type, object.scope, object[type]); │ │ │ │ │ - } │ │ │ │ │ + shiftColumn: function(prepend, tileSize) { │ │ │ │ │ + var grid = this.grid; │ │ │ │ │ + var colIndex = prepend ? 0 : (grid[0].length - 1); │ │ │ │ │ + var sign = prepend ? -1 : 1; │ │ │ │ │ + var tileLayout = this.gridLayout; │ │ │ │ │ + tileLayout.startcol += sign; │ │ │ │ │ + │ │ │ │ │ + for (var i = 0, len = grid.length; i < len; i++) { │ │ │ │ │ + var row = grid[i]; │ │ │ │ │ + var position = row[colIndex].position.clone(); │ │ │ │ │ + var tile = row[prepend ? 'pop' : 'shift'](); │ │ │ │ │ + position.x += tileSize.w * sign; │ │ │ │ │ + tile.moveTo(this.getTileBoundsForGridIndex(i, colIndex), position); │ │ │ │ │ + row[prepend ? 'unshift' : 'push'](tile); │ │ │ │ │ } │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: unregister │ │ │ │ │ - * │ │ │ │ │ + * Method: removeExcessTiles │ │ │ │ │ + * When the size of the map or the buffer changes, we may need to │ │ │ │ │ + * remove some excess rows and columns. │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * type - {String} │ │ │ │ │ - * obj - {Object} If none specified, defaults to this.object │ │ │ │ │ - * func - {Function} │ │ │ │ │ + * rows - {Integer} Maximum number of rows we want our grid to have. │ │ │ │ │ + * columns - {Integer} Maximum number of columns we want our grid to have. │ │ │ │ │ */ │ │ │ │ │ - unregister: function(type, obj, func) { │ │ │ │ │ - if (obj == null) { │ │ │ │ │ - obj = this.object; │ │ │ │ │ + removeExcessTiles: function(rows, columns) { │ │ │ │ │ + var i, l; │ │ │ │ │ + │ │ │ │ │ + // remove extra rows │ │ │ │ │ + while (this.grid.length > rows) { │ │ │ │ │ + var row = this.grid.pop(); │ │ │ │ │ + for (i = 0, l = row.length; i < l; i++) { │ │ │ │ │ + var tile = row[i]; │ │ │ │ │ + this.destroyTile(tile); │ │ │ │ │ + } │ │ │ │ │ } │ │ │ │ │ - var listeners = this.listeners[type]; │ │ │ │ │ - if (listeners != null) { │ │ │ │ │ - for (var i = 0, len = listeners.length; i < len; i++) { │ │ │ │ │ - if (listeners[i].obj == obj && listeners[i].func == func) { │ │ │ │ │ - listeners.splice(i, 1); │ │ │ │ │ - break; │ │ │ │ │ - } │ │ │ │ │ + │ │ │ │ │ + // remove extra columns │ │ │ │ │ + for (i = 0, l = this.grid.length; i < l; i++) { │ │ │ │ │ + while (this.grid[i].length > columns) { │ │ │ │ │ + var row = this.grid[i]; │ │ │ │ │ + var tile = row.pop(); │ │ │ │ │ + this.destroyTile(tile); │ │ │ │ │ } │ │ │ │ │ } │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: remove │ │ │ │ │ - * Remove all listeners for a given event type. If type is not registered, │ │ │ │ │ - * does nothing. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * type - {String} │ │ │ │ │ + /** │ │ │ │ │ + * Method: onMapResize │ │ │ │ │ + * For singleTile layers, this will set a new tile size according to the │ │ │ │ │ + * dimensions of the map pane. │ │ │ │ │ */ │ │ │ │ │ - remove: function(type) { │ │ │ │ │ - if (this.listeners[type] != null) { │ │ │ │ │ - this.listeners[type] = []; │ │ │ │ │ + onMapResize: function() { │ │ │ │ │ + if (this.singleTile) { │ │ │ │ │ + this.clearGrid(); │ │ │ │ │ + this.setTileSize(); │ │ │ │ │ } │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: triggerEvent │ │ │ │ │ - * Trigger a specified registered event. │ │ │ │ │ - * │ │ │ │ │ + * APIMethod: getTileBounds │ │ │ │ │ + * Returns The tile bounds for a layer given a pixel location. │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * type - {String} │ │ │ │ │ - * evt - {Event || Object} will be passed to the listeners. │ │ │ │ │ + * viewPortPx - {} The location in the viewport. │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {Boolean} The last listener return. If a listener returns false, the │ │ │ │ │ - * chain of listeners will stop getting called. │ │ │ │ │ + * {} Bounds of the tile at the given pixel location. │ │ │ │ │ */ │ │ │ │ │ - triggerEvent: function(type, evt) { │ │ │ │ │ - var listeners = this.listeners[type]; │ │ │ │ │ + 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); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - // fast path │ │ │ │ │ - if (!listeners || listeners.length == 0) { │ │ │ │ │ - return undefined; │ │ │ │ │ - } │ │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.Grid" │ │ │ │ │ +}); │ │ │ │ │ +/* ====================================================================== │ │ │ │ │ + OpenLayers/TileManager.js │ │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ │ │ - // prep evt object with object & div references │ │ │ │ │ - if (evt == null) { │ │ │ │ │ - evt = {}; │ │ │ │ │ - } │ │ │ │ │ - evt.object = this.object; │ │ │ │ │ - evt.element = this.element; │ │ │ │ │ - if (!evt.type) { │ │ │ │ │ - evt.type = type; │ │ │ │ │ - } │ │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ + * full text of the license. */ │ │ │ │ │ │ │ │ │ │ - // execute all callbacks registered for specified type │ │ │ │ │ - // get a clone of the listeners array to │ │ │ │ │ - // allow for splicing during callbacks │ │ │ │ │ - listeners = listeners.slice(); │ │ │ │ │ - var continueChain; │ │ │ │ │ - for (var i = 0, len = listeners.length; i < len; i++) { │ │ │ │ │ - var callback = listeners[i]; │ │ │ │ │ - // bind the context to callback.obj │ │ │ │ │ - continueChain = callback.func.apply(callback.obj, [evt]); │ │ │ │ │ │ │ │ │ │ - if ((continueChain != undefined) && (continueChain == false)) { │ │ │ │ │ - // if callback returns false, execute no more callbacks. │ │ │ │ │ - break; │ │ │ │ │ - } │ │ │ │ │ +/** │ │ │ │ │ + * @requires OpenLayers/Util.js │ │ │ │ │ + * @requires OpenLayers/BaseTypes.js │ │ │ │ │ + * @requires OpenLayers/BaseTypes/Element.js │ │ │ │ │ + * @requires OpenLayers/Layer/Grid.js │ │ │ │ │ + * @requires OpenLayers/Tile/Image.js │ │ │ │ │ + */ │ │ │ │ │ + │ │ │ │ │ +/** │ │ │ │ │ + * Class: OpenLayers.TileManager │ │ │ │ │ + * Provides queueing of image requests and caching of image elements. │ │ │ │ │ + * │ │ │ │ │ + * Queueing avoids unnecessary image requests while changing zoom levels │ │ │ │ │ + * quickly, and helps improve dragging performance on mobile devices that show │ │ │ │ │ + * a lag in dragging when loading of new images starts. and │ │ │ │ │ + * are the configuration options to control this behavior. │ │ │ │ │ + * │ │ │ │ │ + * Caching avoids setting the src on image elements for images that have already │ │ │ │ │ + * been used. Several maps can share a TileManager instance, in which case each │ │ │ │ │ + * map gets its own tile queue, but all maps share the same tile cache. │ │ │ │ │ + */ │ │ │ │ │ +OpenLayers.TileManager = OpenLayers.Class({ │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * APIProperty: cacheSize │ │ │ │ │ + * {Number} Number of image elements to keep referenced in this instance's │ │ │ │ │ + * cache for fast reuse. Default is 256. │ │ │ │ │ + */ │ │ │ │ │ + cacheSize: 256, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * APIProperty: tilesPerFrame │ │ │ │ │ + * {Number} Number of queued tiles to load per frame (see ). │ │ │ │ │ + * Default is 2. │ │ │ │ │ + */ │ │ │ │ │ + tilesPerFrame: 2, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * APIProperty: frameDelay │ │ │ │ │ + * {Number} Delay between tile loading frames (see ) in │ │ │ │ │ + * milliseconds. Default is 16. │ │ │ │ │ + */ │ │ │ │ │ + frameDelay: 16, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * APIProperty: moveDelay │ │ │ │ │ + * {Number} Delay in milliseconds after a map's move event before loading │ │ │ │ │ + * tiles. Default is 100. │ │ │ │ │ + */ │ │ │ │ │ + moveDelay: 100, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * APIProperty: zoomDelay │ │ │ │ │ + * {Number} Delay in milliseconds after a map's zoomend event before loading │ │ │ │ │ + * tiles. Default is 200. │ │ │ │ │ + */ │ │ │ │ │ + zoomDelay: 200, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Property: maps │ │ │ │ │ + * {Array()} The maps to manage tiles on. │ │ │ │ │ + */ │ │ │ │ │ + maps: null, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Property: tileQueueId │ │ │ │ │ + * {Object} The ids of the loop, keyed by map id. │ │ │ │ │ + */ │ │ │ │ │ + tileQueueId: null, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Property: tileQueue │ │ │ │ │ + * {Object(Array())} Tiles queued for drawing, keyed by │ │ │ │ │ + * map id. │ │ │ │ │ + */ │ │ │ │ │ + tileQueue: null, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Property: tileCache │ │ │ │ │ + * {Object} Cached image elements, keyed by URL. │ │ │ │ │ + */ │ │ │ │ │ + tileCache: null, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Property: tileCacheIndex │ │ │ │ │ + * {Array(String)} URLs of cached tiles. First entry is the least recently │ │ │ │ │ + * used. │ │ │ │ │ + */ │ │ │ │ │ + tileCacheIndex: null, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Constructor: OpenLayers.TileManager │ │ │ │ │ + * Constructor for a new instance. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * options - {Object} Configuration for this instance. │ │ │ │ │ + */ │ │ │ │ │ + initialize: function(options) { │ │ │ │ │ + OpenLayers.Util.extend(this, options); │ │ │ │ │ + this.maps = []; │ │ │ │ │ + this.tileQueueId = {}; │ │ │ │ │ + this.tileQueue = {}; │ │ │ │ │ + this.tileCache = {}; │ │ │ │ │ + this.tileCacheIndex = []; │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Method: addMap │ │ │ │ │ + * Binds this instance to a map │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * map - {} │ │ │ │ │ + */ │ │ │ │ │ + addMap: function(map) { │ │ │ │ │ + if (this._destroyed || !OpenLayers.Layer.Grid) { │ │ │ │ │ + return; │ │ │ │ │ } │ │ │ │ │ - // don't fall through to other DOM elements │ │ │ │ │ - if (!this.fallThrough) { │ │ │ │ │ - OpenLayers.Event.stop(evt, true); │ │ │ │ │ + this.maps.push(map); │ │ │ │ │ + this.tileQueue[map.id] = []; │ │ │ │ │ + for (var i = 0, ii = map.layers.length; i < ii; ++i) { │ │ │ │ │ + this.addLayer({ │ │ │ │ │ + layer: map.layers[i] │ │ │ │ │ + }); │ │ │ │ │ } │ │ │ │ │ - return continueChain; │ │ │ │ │ + map.events.on({ │ │ │ │ │ + move: this.move, │ │ │ │ │ + zoomend: this.zoomEnd, │ │ │ │ │ + changelayer: this.changeLayer, │ │ │ │ │ + addlayer: this.addLayer, │ │ │ │ │ + preremovelayer: this.removeLayer, │ │ │ │ │ + scope: this │ │ │ │ │ + }); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: handleBrowserEvent │ │ │ │ │ - * Basically just a wrapper to the triggerEvent() function, but takes │ │ │ │ │ - * care to set a property 'xy' on the event with the current mouse │ │ │ │ │ - * position. │ │ │ │ │ + * Method: removeMap │ │ │ │ │ + * Unbinds this instance from a map │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * evt - {Event} │ │ │ │ │ + * map - {} │ │ │ │ │ */ │ │ │ │ │ - handleBrowserEvent: function(evt) { │ │ │ │ │ - var type = evt.type, │ │ │ │ │ - listeners = this.listeners[type]; │ │ │ │ │ - if (!listeners || listeners.length == 0) { │ │ │ │ │ - // noone's listening, bail out │ │ │ │ │ + removeMap: function(map) { │ │ │ │ │ + if (this._destroyed || !OpenLayers.Layer.Grid) { │ │ │ │ │ return; │ │ │ │ │ } │ │ │ │ │ - // add clientX & clientY to all events - corresponds to average x, y │ │ │ │ │ - var touches = evt.touches; │ │ │ │ │ - if (touches && touches[0]) { │ │ │ │ │ - var x = 0; │ │ │ │ │ - var y = 0; │ │ │ │ │ - var num = touches.length; │ │ │ │ │ - var touch; │ │ │ │ │ - for (var i = 0; i < num; ++i) { │ │ │ │ │ - touch = this.getTouchClientXY(touches[i]); │ │ │ │ │ - x += touch.clientX; │ │ │ │ │ - y += touch.clientY; │ │ │ │ │ + window.clearTimeout(this.tileQueueId[map.id]); │ │ │ │ │ + if (map.layers) { │ │ │ │ │ + for (var i = 0, ii = map.layers.length; i < ii; ++i) { │ │ │ │ │ + this.removeLayer({ │ │ │ │ │ + layer: map.layers[i] │ │ │ │ │ + }); │ │ │ │ │ } │ │ │ │ │ - evt.clientX = x / num; │ │ │ │ │ - evt.clientY = y / num; │ │ │ │ │ } │ │ │ │ │ - if (this.includeXY) { │ │ │ │ │ - evt.xy = this.getMousePosition(evt); │ │ │ │ │ + if (map.events) { │ │ │ │ │ + map.events.un({ │ │ │ │ │ + move: this.move, │ │ │ │ │ + zoomend: this.zoomEnd, │ │ │ │ │ + changelayer: this.changeLayer, │ │ │ │ │ + addlayer: this.addLayer, │ │ │ │ │ + preremovelayer: this.removeLayer, │ │ │ │ │ + scope: this │ │ │ │ │ + }); │ │ │ │ │ } │ │ │ │ │ - this.triggerEvent(type, evt); │ │ │ │ │ + delete this.tileQueue[map.id]; │ │ │ │ │ + delete this.tileQueueId[map.id]; │ │ │ │ │ + OpenLayers.Util.removeItem(this.maps, map); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: getTouchClientXY │ │ │ │ │ - * WebKit has a few bugs for clientX/clientY. This method detects them │ │ │ │ │ - * and calculate the correct values. │ │ │ │ │ + * Method: move │ │ │ │ │ + * Handles the map's move event │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * evt - {Touch} a Touch object from a TouchEvent │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Object} An object with only clientX and clientY properties with the │ │ │ │ │ - * calculated values. │ │ │ │ │ + * evt - {Object} Listener argument │ │ │ │ │ */ │ │ │ │ │ - getTouchClientXY: function(evt) { │ │ │ │ │ - // olMochWin is to override window, used for testing │ │ │ │ │ - var win = window.olMockWin || window, │ │ │ │ │ - winPageX = win.pageXOffset, │ │ │ │ │ - winPageY = win.pageYOffset, │ │ │ │ │ - x = evt.clientX, │ │ │ │ │ - y = evt.clientY; │ │ │ │ │ - │ │ │ │ │ - if (evt.pageY === 0 && Math.floor(y) > Math.floor(evt.pageY) || │ │ │ │ │ - evt.pageX === 0 && Math.floor(x) > Math.floor(evt.pageX)) { │ │ │ │ │ - // iOS4 include scroll offset in clientX/Y │ │ │ │ │ - x = x - winPageX; │ │ │ │ │ - y = y - winPageY; │ │ │ │ │ - } else if (y < (evt.pageY - winPageY) || x < (evt.pageX - winPageX)) { │ │ │ │ │ - // Some Android browsers have totally bogus values for clientX/Y │ │ │ │ │ - // when scrolling/zooming a page │ │ │ │ │ - x = evt.pageX - winPageX; │ │ │ │ │ - y = evt.pageY - winPageY; │ │ │ │ │ - } │ │ │ │ │ - │ │ │ │ │ - evt.olClientX = x; │ │ │ │ │ - evt.olClientY = y; │ │ │ │ │ - │ │ │ │ │ - return { │ │ │ │ │ - clientX: x, │ │ │ │ │ - clientY: y │ │ │ │ │ - }; │ │ │ │ │ + move: function(evt) { │ │ │ │ │ + this.updateTimeout(evt.object, this.moveDelay, true); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * Method: zoomEnd │ │ │ │ │ + * Handles the map's zoomEnd event │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * evt - {Object} Listener argument │ │ │ │ │ */ │ │ │ │ │ - clearMouseCache: function() { │ │ │ │ │ - this.element.scrolls = null; │ │ │ │ │ - this.element.lefttop = null; │ │ │ │ │ - this.element.offsets = null; │ │ │ │ │ + zoomEnd: function(evt) { │ │ │ │ │ + this.updateTimeout(evt.object, this.zoomDelay); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: getMousePosition │ │ │ │ │ - * │ │ │ │ │ + * Method: changeLayer │ │ │ │ │ + * Handles the map's changeLayer event │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * evt - {Event} │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {} The current xy coordinate of the mouse, adjusted │ │ │ │ │ - * for offsets │ │ │ │ │ + * evt - {Object} Listener argument │ │ │ │ │ */ │ │ │ │ │ - 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 │ │ │ │ │ - ]; │ │ │ │ │ + changeLayer: function(evt) { │ │ │ │ │ + if (evt.property === 'visibility' || evt.property === 'params') { │ │ │ │ │ + this.updateTimeout(evt.object, 0); │ │ │ │ │ } │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - if (!this.element.lefttop) { │ │ │ │ │ - this.element.lefttop = [ │ │ │ │ │ - (document.documentElement.clientLeft || 0), │ │ │ │ │ - (document.documentElement.clientTop || 0) │ │ │ │ │ - ]; │ │ │ │ │ + /** │ │ │ │ │ + * Method: addLayer │ │ │ │ │ + * Handles the map's addlayer event │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * evt - {Object} The listener argument │ │ │ │ │ + */ │ │ │ │ │ + addLayer: function(evt) { │ │ │ │ │ + var layer = evt.layer; │ │ │ │ │ + if (layer instanceof OpenLayers.Layer.Grid) { │ │ │ │ │ + layer.events.on({ │ │ │ │ │ + addtile: this.addTile, │ │ │ │ │ + retile: this.clearTileQueue, │ │ │ │ │ + scope: this │ │ │ │ │ + }); │ │ │ │ │ + var i, j, tile; │ │ │ │ │ + for (i = layer.grid.length - 1; i >= 0; --i) { │ │ │ │ │ + for (j = layer.grid[i].length - 1; j >= 0; --j) { │ │ │ │ │ + tile = layer.grid[i][j]; │ │ │ │ │ + this.addTile({ │ │ │ │ │ + tile: tile │ │ │ │ │ + }); │ │ │ │ │ + if (tile.url && !tile.imgDiv) { │ │ │ │ │ + this.manageTileCache({ │ │ │ │ │ + object: tile │ │ │ │ │ + }); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ } │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - if (!this.element.offsets) { │ │ │ │ │ - this.element.offsets = OpenLayers.Util.pagePosition(this.element); │ │ │ │ │ + /** │ │ │ │ │ + * Method: removeLayer │ │ │ │ │ + * Handles the map's preremovelayer event │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * evt - {Object} The listener argument │ │ │ │ │ + */ │ │ │ │ │ + removeLayer: function(evt) { │ │ │ │ │ + var layer = evt.layer; │ │ │ │ │ + if (layer instanceof OpenLayers.Layer.Grid) { │ │ │ │ │ + this.clearTileQueue({ │ │ │ │ │ + object: layer │ │ │ │ │ + }); │ │ │ │ │ + if (layer.events) { │ │ │ │ │ + layer.events.un({ │ │ │ │ │ + addtile: this.addTile, │ │ │ │ │ + retile: this.clearTileQueue, │ │ │ │ │ + scope: this │ │ │ │ │ + }); │ │ │ │ │ + } │ │ │ │ │ + if (layer.grid) { │ │ │ │ │ + var i, j, tile; │ │ │ │ │ + for (i = layer.grid.length - 1; i >= 0; --i) { │ │ │ │ │ + for (j = layer.grid[i].length - 1; j >= 0; --j) { │ │ │ │ │ + tile = layer.grid[i][j]; │ │ │ │ │ + this.unloadTile({ │ │ │ │ │ + object: tile │ │ │ │ │ + }); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ } │ │ │ │ │ - │ │ │ │ │ - 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 │ │ │ │ │ + * Method: updateTimeout │ │ │ │ │ + * Applies the or to the loop, │ │ │ │ │ + * and schedules more queue processing after if there are still │ │ │ │ │ + * tiles in the queue. │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * element - {DOMElement} The DOM element to register the listener on │ │ │ │ │ - * type - {String} The event type │ │ │ │ │ - * handler - {Function} the handler │ │ │ │ │ + * map - {} The map to update the timeout for │ │ │ │ │ + * delay - {Number} The delay to apply │ │ │ │ │ + * nice - {Boolean} If true, the timeout function will only be created if │ │ │ │ │ + * the tilequeue is not empty. This is used by the move handler to │ │ │ │ │ + * avoid impacts on dragging performance. For other events, the tile │ │ │ │ │ + * queue may not be populated yet, so we need to set the timer │ │ │ │ │ + * regardless of the queue size. │ │ │ │ │ */ │ │ │ │ │ - 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(); │ │ │ │ │ + updateTimeout: function(map, delay, nice) { │ │ │ │ │ + window.clearTimeout(this.tileQueueId[map.id]); │ │ │ │ │ + var tileQueue = this.tileQueue[map.id]; │ │ │ │ │ + if (!nice || tileQueue.length) { │ │ │ │ │ + this.tileQueueId[map.id] = window.setTimeout( │ │ │ │ │ + OpenLayers.Function.bind(function() { │ │ │ │ │ + this.drawTilesFromQueue(map); │ │ │ │ │ + if (tileQueue.length) { │ │ │ │ │ + this.updateTimeout(map, this.frameDelay); │ │ │ │ │ } │ │ │ │ │ - }, │ │ │ │ │ - type: type │ │ │ │ │ - }, evt)); │ │ │ │ │ + }, this), delay │ │ │ │ │ + ); │ │ │ │ │ } │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - 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: addTile │ │ │ │ │ + * Listener for the layer's addtile event │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * evt - {Object} The listener argument │ │ │ │ │ + */ │ │ │ │ │ + addTile: function(evt) { │ │ │ │ │ + if (evt.tile instanceof OpenLayers.Tile.Image) { │ │ │ │ │ + evt.tile.events.on({ │ │ │ │ │ + beforedraw: this.queueTileDraw, │ │ │ │ │ + beforeload: this.manageTileCache, │ │ │ │ │ + loadend: this.addToCache, │ │ │ │ │ + unload: this.unloadTile, │ │ │ │ │ + scope: this │ │ │ │ │ + }); │ │ │ │ │ + } else { │ │ │ │ │ + // Layer has the wrong tile type, so don't handle it any longer │ │ │ │ │ + this.removeLayer({ │ │ │ │ │ + layer: evt.tile.layer │ │ │ │ │ + }); │ │ │ │ │ } │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: addMsTouchListenerStart │ │ │ │ │ + * Method: unloadTile │ │ │ │ │ + * Listener for the tile's unload event │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * element - {DOMElement} The DOM element to register the listener on │ │ │ │ │ - * type - {String} The event type │ │ │ │ │ - * handler - {Function} the handler │ │ │ │ │ + * evt - {Object} The listener argument │ │ │ │ │ */ │ │ │ │ │ - addMsTouchListenerStart: function(element, type, handler) { │ │ │ │ │ - var touches = this._msTouches; │ │ │ │ │ - │ │ │ │ │ - var cb = function(e) { │ │ │ │ │ + unloadTile: function(evt) { │ │ │ │ │ + var tile = evt.object; │ │ │ │ │ + tile.events.un({ │ │ │ │ │ + beforedraw: this.queueTileDraw, │ │ │ │ │ + beforeload: this.manageTileCache, │ │ │ │ │ + loadend: this.addToCache, │ │ │ │ │ + unload: this.unloadTile, │ │ │ │ │ + scope: this │ │ │ │ │ + }); │ │ │ │ │ + OpenLayers.Util.removeItem(this.tileQueue[tile.layer.map.id], tile); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - var alreadyInArray = false; │ │ │ │ │ - for (var i = 0, ii = touches.length; i < ii; ++i) { │ │ │ │ │ - if (touches[i].pointerId == e.pointerId) { │ │ │ │ │ - alreadyInArray = true; │ │ │ │ │ - break; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - if (!alreadyInArray) { │ │ │ │ │ - touches.push(e); │ │ │ │ │ + /** │ │ │ │ │ + * Method: queueTileDraw │ │ │ │ │ + * Adds a tile to the queue that will draw it. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * evt - {Object} Listener argument of the tile's beforedraw event │ │ │ │ │ + */ │ │ │ │ │ + queueTileDraw: function(evt) { │ │ │ │ │ + var tile = evt.object; │ │ │ │ │ + var queued = false; │ │ │ │ │ + var layer = tile.layer; │ │ │ │ │ + var url = layer.getURL(tile.bounds); │ │ │ │ │ + var img = this.tileCache[url]; │ │ │ │ │ + if (img && img.className !== 'olTileImage') { │ │ │ │ │ + // cached image no longer valid, e.g. because we're olTileReplacing │ │ │ │ │ + delete this.tileCache[url]; │ │ │ │ │ + OpenLayers.Util.removeItem(this.tileCacheIndex, url); │ │ │ │ │ + img = null; │ │ │ │ │ + } │ │ │ │ │ + // queue only if image with same url not cached already │ │ │ │ │ + if (layer.url && (layer.async || !img)) { │ │ │ │ │ + // add to queue only if not in queue already │ │ │ │ │ + var tileQueue = this.tileQueue[layer.map.id]; │ │ │ │ │ + if (!~OpenLayers.Util.indexOf(tileQueue, tile)) { │ │ │ │ │ + tileQueue.push(tile); │ │ │ │ │ } │ │ │ │ │ + queued = true; │ │ │ │ │ + } │ │ │ │ │ + return !queued; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - e.touches = touches.slice(); │ │ │ │ │ - handler(e); │ │ │ │ │ - }; │ │ │ │ │ - │ │ │ │ │ - OpenLayers.Event.observe(element, 'MSPointerDown', cb); │ │ │ │ │ - │ │ │ │ │ - // Need to also listen for end events to keep the _msTouches list │ │ │ │ │ - // accurate │ │ │ │ │ - var internalCb = function(e) { │ │ │ │ │ - for (var i = 0, ii = touches.length; i < ii; ++i) { │ │ │ │ │ - if (touches[i].pointerId == e.pointerId) { │ │ │ │ │ - touches.splice(i, 1); │ │ │ │ │ - break; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - }; │ │ │ │ │ - OpenLayers.Event.observe(element, 'MSPointerUp', internalCb); │ │ │ │ │ + /** │ │ │ │ │ + * Method: drawTilesFromQueue │ │ │ │ │ + * Draws tiles from the tileQueue, and unqueues the tiles │ │ │ │ │ + */ │ │ │ │ │ + drawTilesFromQueue: function(map) { │ │ │ │ │ + var tileQueue = this.tileQueue[map.id]; │ │ │ │ │ + var limit = this.tilesPerFrame; │ │ │ │ │ + var animating = map.zoomTween && map.zoomTween.playing; │ │ │ │ │ + while (!animating && tileQueue.length && limit) { │ │ │ │ │ + tileQueue.shift().draw(true); │ │ │ │ │ + --limit; │ │ │ │ │ + } │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: addMsTouchListenerMove │ │ │ │ │ + * Method: manageTileCache │ │ │ │ │ + * Adds, updates, removes and fetches cache entries. │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * element - {DOMElement} The DOM element to register the listener on │ │ │ │ │ - * type - {String} The event type │ │ │ │ │ - * handler - {Function} the handler │ │ │ │ │ + * evt - {Object} Listener argument of the tile's beforeload event │ │ │ │ │ */ │ │ │ │ │ - addMsTouchListenerMove: function(element, type, handler) { │ │ │ │ │ - var touches = this._msTouches; │ │ │ │ │ - var cb = function(e) { │ │ │ │ │ - │ │ │ │ │ - //Don't fire touch moves when mouse isn't down │ │ │ │ │ - if (e.pointerType == e.MSPOINTER_TYPE_MOUSE && e.buttons == 0) { │ │ │ │ │ - return; │ │ │ │ │ - } │ │ │ │ │ - │ │ │ │ │ - if (touches.length == 1 && touches[0].pageX == e.pageX && │ │ │ │ │ - touches[0].pageY == e.pageY) { │ │ │ │ │ - // don't trigger event when pointer has not moved │ │ │ │ │ - return; │ │ │ │ │ + manageTileCache: function(evt) { │ │ │ │ │ + var tile = evt.object; │ │ │ │ │ + var img = this.tileCache[tile.url]; │ │ │ │ │ + if (img) { │ │ │ │ │ + // if image is on its layer's backbuffer, remove it from backbuffer │ │ │ │ │ + if (img.parentNode && │ │ │ │ │ + OpenLayers.Element.hasClass(img.parentNode, 'olBackBuffer')) { │ │ │ │ │ + img.parentNode.removeChild(img); │ │ │ │ │ + img.id = null; │ │ │ │ │ } │ │ │ │ │ - for (var i = 0, ii = touches.length; i < ii; ++i) { │ │ │ │ │ - if (touches[i].pointerId == e.pointerId) { │ │ │ │ │ - touches[i] = e; │ │ │ │ │ - break; │ │ │ │ │ - } │ │ │ │ │ + // only use image from cache if it is not on a layer already │ │ │ │ │ + if (!img.parentNode) { │ │ │ │ │ + img.style.visibility = 'hidden'; │ │ │ │ │ + img.style.opacity = 0; │ │ │ │ │ + tile.setImage(img); │ │ │ │ │ + // LRU - move tile to the end of the array to mark it as the most │ │ │ │ │ + // recently used │ │ │ │ │ + OpenLayers.Util.removeItem(this.tileCacheIndex, tile.url); │ │ │ │ │ + this.tileCacheIndex.push(tile.url); │ │ │ │ │ } │ │ │ │ │ - │ │ │ │ │ - e.touches = touches.slice(); │ │ │ │ │ - handler(e); │ │ │ │ │ - }; │ │ │ │ │ - │ │ │ │ │ - OpenLayers.Event.observe(element, 'MSPointerMove', cb); │ │ │ │ │ + } │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: addMsTouchListenerEnd │ │ │ │ │ + * Method: addToCache │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * element - {DOMElement} The DOM element to register the listener on │ │ │ │ │ - * type - {String} The event type │ │ │ │ │ - * handler - {Function} the handler │ │ │ │ │ + * evt - {Object} Listener argument for the tile's loadend event │ │ │ │ │ */ │ │ │ │ │ - addMsTouchListenerEnd: function(element, type, handler) { │ │ │ │ │ - var touches = this._msTouches; │ │ │ │ │ - │ │ │ │ │ - var cb = function(e) { │ │ │ │ │ - │ │ │ │ │ - for (var i = 0, ii = touches.length; i < ii; ++i) { │ │ │ │ │ - if (touches[i].pointerId == e.pointerId) { │ │ │ │ │ - touches.splice(i, 1); │ │ │ │ │ - break; │ │ │ │ │ + addToCache: function(evt) { │ │ │ │ │ + var tile = evt.object; │ │ │ │ │ + if (!this.tileCache[tile.url]) { │ │ │ │ │ + if (!OpenLayers.Element.hasClass(tile.imgDiv, 'olImageLoadError')) { │ │ │ │ │ + if (this.tileCacheIndex.length >= this.cacheSize) { │ │ │ │ │ + delete this.tileCache[this.tileCacheIndex[0]]; │ │ │ │ │ + this.tileCacheIndex.shift(); │ │ │ │ │ } │ │ │ │ │ + this.tileCache[tile.url] = tile.imgDiv; │ │ │ │ │ + this.tileCacheIndex.push(tile.url); │ │ │ │ │ } │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - e.touches = touches.slice(); │ │ │ │ │ - handler(e); │ │ │ │ │ - }; │ │ │ │ │ - │ │ │ │ │ - OpenLayers.Event.observe(element, 'MSPointerUp', cb); │ │ │ │ │ + /** │ │ │ │ │ + * Method: clearTileQueue │ │ │ │ │ + * Clears the tile queue from tiles of a specific layer │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * evt - {Object} Listener argument of the layer's retile event │ │ │ │ │ + */ │ │ │ │ │ + clearTileQueue: function(evt) { │ │ │ │ │ + var layer = evt.object; │ │ │ │ │ + var tileQueue = this.tileQueue[layer.map.id]; │ │ │ │ │ + for (var i = tileQueue.length - 1; i >= 0; --i) { │ │ │ │ │ + if (tileQueue[i].layer === layer) { │ │ │ │ │ + tileQueue.splice(i, 1); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Events" │ │ │ │ │ + /** │ │ │ │ │ + * Method: destroy │ │ │ │ │ + */ │ │ │ │ │ + destroy: function() { │ │ │ │ │ + for (var i = this.maps.length - 1; i >= 0; --i) { │ │ │ │ │ + this.removeMap(this.maps[i]); │ │ │ │ │ + } │ │ │ │ │ + this.maps = null; │ │ │ │ │ + this.tileQueue = null; │ │ │ │ │ + this.tileQueueId = null; │ │ │ │ │ + this.tileCache = null; │ │ │ │ │ + this.tileCacheIndex = null; │ │ │ │ │ + this._destroyed = true; │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ }); │ │ │ │ │ /* ====================================================================== │ │ │ │ │ OpenLayers/Request/XMLHttpRequest.js │ │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ │ │ // XMLHttpRequest.js Copyright (C) 2010 Sergey Ilinsky (http://www.ilinsky.com) │ │ │ │ │ // │ │ │ │ │ @@ -19738,9893 +17145,14761 @@ │ │ │ │ │ method: "OPTIONS" │ │ │ │ │ }); │ │ │ │ │ return OpenLayers.Request.issue(config); │ │ │ │ │ } │ │ │ │ │ │ │ │ │ │ }); │ │ │ │ │ /* ====================================================================== │ │ │ │ │ - OpenLayers/WPSProcess.js │ │ │ │ │ + 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/SingleFile.js │ │ │ │ │ + * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ │ */ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * @requires OpenLayers/Geometry.js │ │ │ │ │ - * @requires OpenLayers/Feature/Vector.js │ │ │ │ │ - * @requires OpenLayers/Format/WKT.js │ │ │ │ │ - * @requires OpenLayers/Format/GeoJSON.js │ │ │ │ │ - * @requires OpenLayers/Format/WPSExecute.js │ │ │ │ │ - * @requires OpenLayers/Request.js │ │ │ │ │ + * Class: OpenLayers.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 < len; ++i) { │ │ │ │ │ + components[i] = result[i].geometry; │ │ │ │ │ + } │ │ │ │ │ + geom = new OpenLayers.Geometry.Collection(components); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + return geom; │ │ │ │ │ +}; │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Class: OpenLayers.WPSProcess │ │ │ │ │ - * Representation of a WPS process. Usually instances of │ │ │ │ │ - * are created by calling 'getProcess' on an │ │ │ │ │ - * instance. │ │ │ │ │ + * Method: OpenLayers.Geometry.segmentsIntersect │ │ │ │ │ + * Determine whether two line segments intersect. Optionally calculates │ │ │ │ │ + * and returns the intersection point. This function is optimized for │ │ │ │ │ + * cases where seg1.x2 >= seg2.x1 || seg2.x2 >= seg1.x1. In those │ │ │ │ │ + * obvious cases where there is no intersection, the function should │ │ │ │ │ + * not be called. │ │ │ │ │ * │ │ │ │ │ - * Currently supports processes that have geometries │ │ │ │ │ - * or features as output, using WKT or GeoJSON as output format. It also │ │ │ │ │ - * supports chaining of processes by using the method to create a │ │ │ │ │ - * handle that is used as process input instead of a static value. │ │ │ │ │ + * 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.WPSProcess = OpenLayers.Class({ │ │ │ │ │ +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/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: client │ │ │ │ │ - * {} The client that manages this process. │ │ │ │ │ + * Property: options │ │ │ │ │ + * {Object} A reference to options passed to the constructor. │ │ │ │ │ */ │ │ │ │ │ - client: null, │ │ │ │ │ + options: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: server │ │ │ │ │ - * {String} Local client identifier for this process's server. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - server: null, │ │ │ │ │ + externalProjection: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: identifier │ │ │ │ │ - * {String} Process identifier known to the server. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - identifier: null, │ │ │ │ │ + internalProjection: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: description │ │ │ │ │ - * {Object} DescribeProcess response for this process. │ │ │ │ │ + * APIProperty: data │ │ │ │ │ + * {Object} When is true, this is the parsed string sent to │ │ │ │ │ + * . │ │ │ │ │ */ │ │ │ │ │ - description: null, │ │ │ │ │ + data: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: localWPS │ │ │ │ │ - * {String} Service endpoint for locally chained WPS processes. Default is │ │ │ │ │ - * 'http://geoserver/wps'. │ │ │ │ │ + * APIProperty: keepData │ │ │ │ │ + * {Object} Maintain a reference () to the most recently read data. │ │ │ │ │ + * Default is false. │ │ │ │ │ */ │ │ │ │ │ - localWPS: 'http://geoserver/wps', │ │ │ │ │ + keepData: false, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: formats │ │ │ │ │ - * {Object} OpenLayers.Format instances keyed by mimetype. │ │ │ │ │ + * 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 │ │ │ │ │ */ │ │ │ │ │ - formats: null, │ │ │ │ │ + initialize: function(options) { │ │ │ │ │ + OpenLayers.Util.extend(this, options); │ │ │ │ │ + this.options = options; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: chained │ │ │ │ │ - * {Integer} Number of chained processes for pending execute requests that │ │ │ │ │ - * don't have a full configuration yet. │ │ │ │ │ + * APIMethod: destroy │ │ │ │ │ + * Clean up. │ │ │ │ │ */ │ │ │ │ │ - chained: 0, │ │ │ │ │ + destroy: function() {}, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: executeCallbacks │ │ │ │ │ - * {Array} Callbacks waiting to be executed until all chained processes │ │ │ │ │ - * are configured; │ │ │ │ │ + * 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 │ │ │ │ │ */ │ │ │ │ │ - executeCallbacks: null, │ │ │ │ │ + read: function(data) { │ │ │ │ │ + throw new Error('Read not implemented.'); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Constructor: OpenLayers.WPSProcess │ │ │ │ │ + * Method: write │ │ │ │ │ + * Accept an object, and return a string. │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * options - {Object} Object whose properties will be set on the instance. │ │ │ │ │ + * object - {Object} Object to be serialized │ │ │ │ │ * │ │ │ │ │ - * Avaliable options: │ │ │ │ │ - * client - {} Mandatory. Client that manages this │ │ │ │ │ - * process. │ │ │ │ │ - * server - {String} Mandatory. Local client identifier of this process's │ │ │ │ │ - * server. │ │ │ │ │ - * identifier - {String} Mandatory. Process identifier known to the server. │ │ │ │ │ + * Returns: │ │ │ │ │ + * {String} A string representation of the object. │ │ │ │ │ */ │ │ │ │ │ - initialize: function(options) { │ │ │ │ │ - OpenLayers.Util.extend(this, options); │ │ │ │ │ - this.executeCallbacks = []; │ │ │ │ │ - this.formats = { │ │ │ │ │ - 'application/wkt': new OpenLayers.Format.WKT(), │ │ │ │ │ - 'application/json': new OpenLayers.Format.GeoJSON() │ │ │ │ │ - }; │ │ │ │ │ + write: function(object) { │ │ │ │ │ + throw new Error('Write not implemented.'); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ + CLASS_NAME: "OpenLayers.Format" │ │ │ │ │ +}); │ │ │ │ │ +/* ====================================================================== │ │ │ │ │ + OpenLayers/Geometry/Point.js │ │ │ │ │ + ====================================================================== */ │ │ │ │ │ + │ │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ + * full text of the license. */ │ │ │ │ │ + │ │ │ │ │ +/** │ │ │ │ │ + * @requires OpenLayers/Geometry.js │ │ │ │ │ + */ │ │ │ │ │ + │ │ │ │ │ +/** │ │ │ │ │ + * Class: OpenLayers.Geometry.Point │ │ │ │ │ + * Point geometry class. │ │ │ │ │ + * │ │ │ │ │ + * Inherits from: │ │ │ │ │ + * - │ │ │ │ │ + */ │ │ │ │ │ +OpenLayers.Geometry.Point = OpenLayers.Class(OpenLayers.Geometry, { │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * APIProperty: x │ │ │ │ │ + * {float} │ │ │ │ │ + */ │ │ │ │ │ + x: null, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * APIProperty: y │ │ │ │ │ + * {float} │ │ │ │ │ + */ │ │ │ │ │ + y: null, │ │ │ │ │ + │ │ │ │ │ /** │ │ │ │ │ - * Method: describe │ │ │ │ │ - * Makes the client issue a DescribeProcess request asynchronously. │ │ │ │ │ + * Constructor: OpenLayers.Geometry.Point │ │ │ │ │ + * Construct a point geometry. │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * options - {Object} Configuration for the method call │ │ │ │ │ + * 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). │ │ │ │ │ * │ │ │ │ │ - * Available options: │ │ │ │ │ - * callback - {Function} Callback to execute when the description is │ │ │ │ │ - * available. Will be called with the parsed description as argument. │ │ │ │ │ - * Optional. │ │ │ │ │ - * scope - {Object} The scope in which the callback will be executed. │ │ │ │ │ - * Default is the global object. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - describe: function(options) { │ │ │ │ │ - options = options || {}; │ │ │ │ │ - if (!this.description) { │ │ │ │ │ - this.client.describeProcess(this.server, this.identifier, function(description) { │ │ │ │ │ - if (!this.description) { │ │ │ │ │ - this.parseDescription(description); │ │ │ │ │ - } │ │ │ │ │ - if (options.callback) { │ │ │ │ │ - options.callback.call(options.scope, this.description); │ │ │ │ │ + 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 < len; i++) { │ │ │ │ │ + geometry.addComponent(this.components[i].clone()); │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + // catch any randomly tagged-on properties │ │ │ │ │ + OpenLayers.Util.applyDefaults(geometry, this); │ │ │ │ │ + │ │ │ │ │ + return geometry; │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Method: getComponentsString │ │ │ │ │ + * Get a string representing the components for this collection │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {String} A string representation of the components of this geometry │ │ │ │ │ + */ │ │ │ │ │ + getComponentsString: function() { │ │ │ │ │ + var strings = []; │ │ │ │ │ + for (var i = 0, len = this.components.length; i < len; i++) { │ │ │ │ │ + strings.push(this.components[i].toShortString()); │ │ │ │ │ + } │ │ │ │ │ + return strings.join(","); │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: calculateBounds │ │ │ │ │ + * Recalculate the bounds by iterating through the components and │ │ │ │ │ + * calling calling extendBounds() on each item. │ │ │ │ │ + */ │ │ │ │ │ + calculateBounds: function() { │ │ │ │ │ + this.bounds = null; │ │ │ │ │ + var bounds = new OpenLayers.Bounds(); │ │ │ │ │ + var components = this.components; │ │ │ │ │ + if (components) { │ │ │ │ │ + for (var i = 0, len = components.length; i < len; i++) { │ │ │ │ │ + bounds.extend(components[i].getBounds()); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + // to preserve old behavior, we only set bounds if non-null │ │ │ │ │ + // in the future, we could add bounds.isEmpty() │ │ │ │ │ + if (bounds.left != null && bounds.bottom != null && │ │ │ │ │ + bounds.right != null && bounds.top != null) { │ │ │ │ │ + this.setBounds(bounds); │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: addComponents │ │ │ │ │ + * Add components to this geometry. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * components - {Array()} An array of geometries to add │ │ │ │ │ + */ │ │ │ │ │ + addComponents: function(components) { │ │ │ │ │ + if (!(OpenLayers.Util.isArray(components))) { │ │ │ │ │ + components = [components]; │ │ │ │ │ + } │ │ │ │ │ + for (var i = 0, len = components.length; i < len; i++) { │ │ │ │ │ + this.addComponent(components[i]); │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Method: addComponent │ │ │ │ │ + * Add a new component (geometry) to the collection. If this.componentTypes │ │ │ │ │ + * is set, then the component class name must be in the componentTypes array. │ │ │ │ │ + * │ │ │ │ │ + * The bounds cache is reset. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * component - {} 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); │ │ │ │ │ } │ │ │ │ │ - }, this); │ │ │ │ │ - } else if (options.callback) { │ │ │ │ │ - var description = this.description; │ │ │ │ │ - window.setTimeout(function() { │ │ │ │ │ - options.callback.call(options.scope, description); │ │ │ │ │ - }, 0); │ │ │ │ │ + component.parent = this; │ │ │ │ │ + this.clearBounds(); │ │ │ │ │ + added = true; │ │ │ │ │ + } │ │ │ │ │ } │ │ │ │ │ + return added; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: configure │ │ │ │ │ - * Configure the process, but do not execute it. Use this for processes │ │ │ │ │ - * that are chained as input of a different process by means of the │ │ │ │ │ - * method. │ │ │ │ │ + * APIMethod: removeComponents │ │ │ │ │ + * Remove components from this geometry. │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * options - {Object} │ │ │ │ │ + * 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: │ │ │ │ │ - * {} this process. │ │ │ │ │ + * {Float} The length of the geometry │ │ │ │ │ + */ │ │ │ │ │ + getLength: function() { │ │ │ │ │ + var length = 0.0; │ │ │ │ │ + for (var i = 0, len = this.components.length; i < len; i++) { │ │ │ │ │ + length += this.components[i].getLength(); │ │ │ │ │ + } │ │ │ │ │ + return length; │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: getArea │ │ │ │ │ + * Calculate the area of this geometry. Note how this function is overridden │ │ │ │ │ + * in . │ │ │ │ │ * │ │ │ │ │ - * Available options: │ │ │ │ │ - * inputs - {Object} The inputs for the process, keyed by input identifier. │ │ │ │ │ - * For spatial data inputs, the value of an input is usually an │ │ │ │ │ - * , an or an array of │ │ │ │ │ - * geometries or features. │ │ │ │ │ - * callback - {Function} Callback to call when the configuration is │ │ │ │ │ - * complete. Optional. │ │ │ │ │ - * scope - {Object} Optional scope for the callback. │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Float} The area of the collection by summing its parts │ │ │ │ │ */ │ │ │ │ │ - configure: function(options) { │ │ │ │ │ - this.describe({ │ │ │ │ │ - callback: function() { │ │ │ │ │ - var description = this.description, │ │ │ │ │ - inputs = options.inputs, │ │ │ │ │ - input, i, ii; │ │ │ │ │ - for (i = 0, ii = description.dataInputs.length; i < ii; ++i) { │ │ │ │ │ - input = description.dataInputs[i]; │ │ │ │ │ - this.setInputData(input, inputs[input.identifier]); │ │ │ │ │ + getArea: function() { │ │ │ │ │ + var area = 0.0; │ │ │ │ │ + for (var i = 0, len = this.components.length; i < len; i++) { │ │ │ │ │ + area += this.components[i].getArea(); │ │ │ │ │ + } │ │ │ │ │ + return area; │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: getGeodesicArea │ │ │ │ │ + * Calculate the approximate area of the polygon were it projected onto │ │ │ │ │ + * the earth. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * projection - {} The spatial reference system │ │ │ │ │ + * for the geometry coordinates. If not provided, Geographic/WGS84 is │ │ │ │ │ + * assumed. │ │ │ │ │ + * │ │ │ │ │ + * Reference: │ │ │ │ │ + * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for │ │ │ │ │ + * Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion │ │ │ │ │ + * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409 │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {float} The approximate geodesic area of the geometry in square meters. │ │ │ │ │ + */ │ │ │ │ │ + getGeodesicArea: function(projection) { │ │ │ │ │ + var area = 0.0; │ │ │ │ │ + for (var i = 0, len = this.components.length; i < len; i++) { │ │ │ │ │ + area += this.components[i].getGeodesicArea(projection); │ │ │ │ │ + } │ │ │ │ │ + return area; │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: getCentroid │ │ │ │ │ + * │ │ │ │ │ + * Compute the centroid for this geometry collection. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * weighted - {Boolean} Perform the getCentroid computation recursively, │ │ │ │ │ + * returning an area weighted average of all geometries in this collection. │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {} The centroid of the collection │ │ │ │ │ + */ │ │ │ │ │ + getCentroid: function(weighted) { │ │ │ │ │ + if (!weighted) { │ │ │ │ │ + return this.components.length && this.components[0].getCentroid(); │ │ │ │ │ + } │ │ │ │ │ + var len = this.components.length; │ │ │ │ │ + if (!len) { │ │ │ │ │ + return false; │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + var areas = []; │ │ │ │ │ + var centroids = []; │ │ │ │ │ + var areaSum = 0; │ │ │ │ │ + var minArea = Number.MAX_VALUE; │ │ │ │ │ + var component; │ │ │ │ │ + for (var i = 0; i < len; ++i) { │ │ │ │ │ + component = this.components[i]; │ │ │ │ │ + var area = component.getArea(); │ │ │ │ │ + var centroid = component.getCentroid(true); │ │ │ │ │ + if (isNaN(area) || isNaN(centroid.x) || isNaN(centroid.y)) { │ │ │ │ │ + continue; │ │ │ │ │ + } │ │ │ │ │ + areas.push(area); │ │ │ │ │ + areaSum += area; │ │ │ │ │ + minArea = (area < minArea && area > 0) ? area : minArea; │ │ │ │ │ + centroids.push(centroid); │ │ │ │ │ + } │ │ │ │ │ + len = areas.length; │ │ │ │ │ + if (areaSum === 0) { │ │ │ │ │ + // all the components in this collection have 0 area │ │ │ │ │ + // probably a collection of points -- weight all the points the same │ │ │ │ │ + for (var i = 0; i < len; ++i) { │ │ │ │ │ + areas[i] = 1; │ │ │ │ │ + } │ │ │ │ │ + areaSum = areas.length; │ │ │ │ │ + } else { │ │ │ │ │ + // normalize all the areas where the smallest area will get │ │ │ │ │ + // a value of 1 │ │ │ │ │ + for (var i = 0; i < len; ++i) { │ │ │ │ │ + areas[i] /= minArea; │ │ │ │ │ + } │ │ │ │ │ + areaSum /= minArea; │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + var xSum = 0, │ │ │ │ │ + ySum = 0, │ │ │ │ │ + centroid, area; │ │ │ │ │ + for (var i = 0; i < len; ++i) { │ │ │ │ │ + centroid = centroids[i]; │ │ │ │ │ + area = areas[i]; │ │ │ │ │ + xSum += centroid.x * area; │ │ │ │ │ + ySum += centroid.y * area; │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + return new OpenLayers.Geometry.Point(xSum / areaSum, ySum / areaSum); │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: getGeodesicLength │ │ │ │ │ + * Calculate the approximate length of the geometry were it projected onto │ │ │ │ │ + * the earth. │ │ │ │ │ + * │ │ │ │ │ + * projection - {} The spatial reference system │ │ │ │ │ + * for the geometry coordinates. If not provided, Geographic/WGS84 is │ │ │ │ │ + * assumed. │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Float} The appoximate geodesic length of the geometry in meters. │ │ │ │ │ + */ │ │ │ │ │ + getGeodesicLength: function(projection) { │ │ │ │ │ + var length = 0.0; │ │ │ │ │ + for (var i = 0, len = this.components.length; i < len; i++) { │ │ │ │ │ + length += this.components[i].getGeodesicLength(projection); │ │ │ │ │ + } │ │ │ │ │ + return length; │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: move │ │ │ │ │ + * Moves a geometry by the given displacement along positive x and y axes. │ │ │ │ │ + * This modifies the position of the geometry and clears the cached │ │ │ │ │ + * bounds. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * x - {Float} Distance to move geometry in positive x direction. │ │ │ │ │ + * y - {Float} Distance to move geometry in positive y direction. │ │ │ │ │ + */ │ │ │ │ │ + move: function(x, y) { │ │ │ │ │ + for (var i = 0, len = this.components.length; i < len; i++) { │ │ │ │ │ + this.components[i].move(x, y); │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: rotate │ │ │ │ │ + * Rotate a geometry around some origin │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * angle - {Float} Rotation angle in degrees (measured counterclockwise │ │ │ │ │ + * from the positive x-axis) │ │ │ │ │ + * origin - {} Center point for the rotation │ │ │ │ │ + */ │ │ │ │ │ + rotate: function(angle, origin) { │ │ │ │ │ + for (var i = 0, len = this.components.length; i < len; ++i) { │ │ │ │ │ + this.components[i].rotate(angle, origin); │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: resize │ │ │ │ │ + * Resize a geometry relative to some origin. Use this method to apply │ │ │ │ │ + * a uniform scaling to a geometry. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * scale - {Float} Factor by which to scale the geometry. A scale of 2 │ │ │ │ │ + * doubles the size of the geometry in each dimension │ │ │ │ │ + * (lines, for example, will be twice as long, and polygons │ │ │ │ │ + * will have four times the area). │ │ │ │ │ + * origin - {} 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 < this.components.length; ++i) { │ │ │ │ │ + this.components[i].resize(scale, origin, ratio); │ │ │ │ │ + } │ │ │ │ │ + return this; │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: distanceTo │ │ │ │ │ + * Calculate the closest distance between two geometries (on the x-y plane). │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * geometry - {} The target geometry. │ │ │ │ │ + * options - {Object} Optional properties for configuring the distance │ │ │ │ │ + * calculation. │ │ │ │ │ + * │ │ │ │ │ + * Valid options: │ │ │ │ │ + * details - {Boolean} Return details from the distance calculation. │ │ │ │ │ + * Default is false. │ │ │ │ │ + * edge - {Boolean} Calculate the distance from this geometry to the │ │ │ │ │ + * nearest edge of the target geometry. Default is true. If true, │ │ │ │ │ + * calling distanceTo from a geometry that is wholly contained within │ │ │ │ │ + * the target will result in a non-zero distance. If false, whenever │ │ │ │ │ + * geometries intersect, calling distanceTo will return 0. If false, │ │ │ │ │ + * details cannot be returned. │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Number | Object} The distance between this geometry and the target. │ │ │ │ │ + * If details is true, the return will be an object with distance, │ │ │ │ │ + * x0, y0, x1, and y1 properties. The x0 and y0 properties represent │ │ │ │ │ + * the coordinates of the closest point on this geometry. The x1 and y1 │ │ │ │ │ + * properties represent the coordinates of the closest point on the │ │ │ │ │ + * target geometry. │ │ │ │ │ + */ │ │ │ │ │ + distanceTo: function(geometry, options) { │ │ │ │ │ + var edge = !(options && options.edge === false); │ │ │ │ │ + var details = edge && options && options.details; │ │ │ │ │ + var result, best, distance; │ │ │ │ │ + var min = Number.POSITIVE_INFINITY; │ │ │ │ │ + for (var i = 0, len = this.components.length; i < len; ++i) { │ │ │ │ │ + result = this.components[i].distanceTo(geometry, options); │ │ │ │ │ + distance = details ? result.distance : result; │ │ │ │ │ + if (distance < min) { │ │ │ │ │ + min = distance; │ │ │ │ │ + best = result; │ │ │ │ │ + if (min == 0) { │ │ │ │ │ + break; │ │ │ │ │ } │ │ │ │ │ - if (options.callback) { │ │ │ │ │ - options.callback.call(options.scope); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + return best; │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: equals │ │ │ │ │ + * Determine whether another geometry is equivalent to this one. Geometries │ │ │ │ │ + * are considered equivalent if all components have the same coordinates. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * geometry - {} The geometry to test. │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Boolean} The supplied geometry is equivalent to this geometry. │ │ │ │ │ + */ │ │ │ │ │ + equals: function(geometry) { │ │ │ │ │ + var equivalent = true; │ │ │ │ │ + if (!geometry || !geometry.CLASS_NAME || │ │ │ │ │ + (this.CLASS_NAME != geometry.CLASS_NAME)) { │ │ │ │ │ + equivalent = false; │ │ │ │ │ + } else if (!(OpenLayers.Util.isArray(geometry.components)) || │ │ │ │ │ + (geometry.components.length != this.components.length)) { │ │ │ │ │ + equivalent = false; │ │ │ │ │ + } else { │ │ │ │ │ + for (var i = 0, len = this.components.length; i < len; ++i) { │ │ │ │ │ + if (!this.components[i].equals(geometry.components[i])) { │ │ │ │ │ + equivalent = false; │ │ │ │ │ + break; │ │ │ │ │ } │ │ │ │ │ - }, │ │ │ │ │ - scope: this │ │ │ │ │ - }); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + return equivalent; │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: transform │ │ │ │ │ + * Reproject the components geometry from source to dest. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * source - {} │ │ │ │ │ + * dest - {} │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {} │ │ │ │ │ + */ │ │ │ │ │ + transform: function(source, dest) { │ │ │ │ │ + if (source && dest) { │ │ │ │ │ + for (var i = 0, len = this.components.length; i < len; i++) { │ │ │ │ │ + var component = this.components[i]; │ │ │ │ │ + component.transform(source, dest); │ │ │ │ │ + } │ │ │ │ │ + this.bounds = null; │ │ │ │ │ + } │ │ │ │ │ return this; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: execute │ │ │ │ │ - * Configures and executes the process │ │ │ │ │ + * APIMethod: intersects │ │ │ │ │ + * Determine if the input geometry intersects this one. │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * options - {Object} │ │ │ │ │ + * geometry - {} Any type of geometry. │ │ │ │ │ * │ │ │ │ │ - * Available options: │ │ │ │ │ - * inputs - {Object} The inputs for the process, keyed by input identifier. │ │ │ │ │ - * For spatial data inputs, the value of an input is usually an │ │ │ │ │ - * , an or an array of │ │ │ │ │ - * geometries or features. │ │ │ │ │ - * output - {String} The identifier of the output to request and parse. │ │ │ │ │ - * Optional. If not provided, the first output will be requested. │ │ │ │ │ - * success - {Function} Callback to call when the process is complete. │ │ │ │ │ - * This function is called with an outputs object as argument, which │ │ │ │ │ - * will have a property with the identifier of the requested output │ │ │ │ │ - * (or 'result' if output was not configured). For processes that │ │ │ │ │ - * generate spatial output, the value will be an array of │ │ │ │ │ - * instances. │ │ │ │ │ - * scope - {Object} Optional scope for the success callback. │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Boolean} The input geometry intersects this one. │ │ │ │ │ */ │ │ │ │ │ - execute: function(options) { │ │ │ │ │ - this.configure({ │ │ │ │ │ - inputs: options.inputs, │ │ │ │ │ - callback: function() { │ │ │ │ │ - var me = this; │ │ │ │ │ - //TODO For now we only deal with a single output │ │ │ │ │ - var outputIndex = this.getOutputIndex( │ │ │ │ │ - me.description.processOutputs, options.output │ │ │ │ │ - ); │ │ │ │ │ - me.setResponseForm({ │ │ │ │ │ - outputIndex: outputIndex │ │ │ │ │ + intersects: function(geometry) { │ │ │ │ │ + var intersect = false; │ │ │ │ │ + for (var i = 0, len = this.components.length; i < len; ++i) { │ │ │ │ │ + intersect = geometry.intersects(this.components[i]); │ │ │ │ │ + if (intersect) { │ │ │ │ │ + break; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + return intersect; │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: getVertices │ │ │ │ │ + * Return a list of all points in this geometry. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * nodes - {Boolean} For lines, only return vertices that are │ │ │ │ │ + * endpoints. If false, for lines, only vertices that are not │ │ │ │ │ + * endpoints will be returned. If not provided, all vertices will │ │ │ │ │ + * be returned. │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Array} A list of all vertices in the geometry. │ │ │ │ │ + */ │ │ │ │ │ + getVertices: function(nodes) { │ │ │ │ │ + var vertices = []; │ │ │ │ │ + for (var i = 0, len = this.components.length; i < len; ++i) { │ │ │ │ │ + Array.prototype.push.apply( │ │ │ │ │ + vertices, this.components[i].getVertices(nodes) │ │ │ │ │ + ); │ │ │ │ │ + } │ │ │ │ │ + return vertices; │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + │ │ │ │ │ + CLASS_NAME: "OpenLayers.Geometry.Collection" │ │ │ │ │ +}); │ │ │ │ │ +/* ====================================================================== │ │ │ │ │ + OpenLayers/Geometry/MultiPoint.js │ │ │ │ │ + ====================================================================== */ │ │ │ │ │ + │ │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ + * full text of the license. */ │ │ │ │ │ + │ │ │ │ │ +/** │ │ │ │ │ + * @requires OpenLayers/Geometry/Collection.js │ │ │ │ │ + * @requires OpenLayers/Geometry/Point.js │ │ │ │ │ + */ │ │ │ │ │ + │ │ │ │ │ +/** │ │ │ │ │ + * Class: OpenLayers.Geometry.MultiPoint │ │ │ │ │ + * MultiPoint is a collection of Points. Create a new instance with the │ │ │ │ │ + * 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 < len; i++) { │ │ │ │ │ + length += this.components[i - 1].distanceTo(this.components[i]); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + return length; │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: getGeodesicLength │ │ │ │ │ + * Calculate the approximate length of the geometry were it projected onto │ │ │ │ │ + * the earth. │ │ │ │ │ + * │ │ │ │ │ + * projection - {} The spatial reference system │ │ │ │ │ + * for the geometry coordinates. If not provided, Geographic/WGS84 is │ │ │ │ │ + * assumed. │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Float} The appoximate geodesic length of the geometry in meters. │ │ │ │ │ + */ │ │ │ │ │ + getGeodesicLength: function(projection) { │ │ │ │ │ + var geom = this; // so we can work with a clone if needed │ │ │ │ │ + if (projection) { │ │ │ │ │ + var gg = new OpenLayers.Projection("EPSG:4326"); │ │ │ │ │ + if (!gg.equals(projection)) { │ │ │ │ │ + geom = this.clone().transform(projection, gg); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + var length = 0.0; │ │ │ │ │ + if (geom.components && (geom.components.length > 1)) { │ │ │ │ │ + var p1, p2; │ │ │ │ │ + for (var i = 1, len = geom.components.length; i < len; i++) { │ │ │ │ │ + p1 = geom.components[i - 1]; │ │ │ │ │ + p2 = geom.components[i]; │ │ │ │ │ + // this returns km and requires lon/lat properties │ │ │ │ │ + length += OpenLayers.Util.distVincenty({ │ │ │ │ │ + lon: p1.x, │ │ │ │ │ + lat: p1.y │ │ │ │ │ + }, { │ │ │ │ │ + lon: p2.x, │ │ │ │ │ + lat: p2.y │ │ │ │ │ }); │ │ │ │ │ - (function callback() { │ │ │ │ │ - OpenLayers.Util.removeItem(me.executeCallbacks, callback); │ │ │ │ │ - if (me.chained !== 0) { │ │ │ │ │ - // need to wait until chained processes have a │ │ │ │ │ - // description and configuration - see chainProcess │ │ │ │ │ - me.executeCallbacks.push(callback); │ │ │ │ │ - return; │ │ │ │ │ - } │ │ │ │ │ - // all chained processes are added as references now, so │ │ │ │ │ - // let's proceed. │ │ │ │ │ - OpenLayers.Request.POST({ │ │ │ │ │ - url: me.client.servers[me.server].url, │ │ │ │ │ - data: new OpenLayers.Format.WPSExecute().write(me.description), │ │ │ │ │ - success: function(response) { │ │ │ │ │ - var output = me.description.processOutputs[outputIndex]; │ │ │ │ │ - var mimeType = me.findMimeType( │ │ │ │ │ - output.complexOutput.supported.formats │ │ │ │ │ - ); │ │ │ │ │ - //TODO For now we assume a spatial output │ │ │ │ │ - var features = me.formats[mimeType].read(response.responseText); │ │ │ │ │ - if (features instanceof OpenLayers.Feature.Vector) { │ │ │ │ │ - features = [features]; │ │ │ │ │ - } │ │ │ │ │ - if (options.success) { │ │ │ │ │ - var outputs = {}; │ │ │ │ │ - outputs[options.output || 'result'] = features; │ │ │ │ │ - options.success.call(options.scope, outputs); │ │ │ │ │ - } │ │ │ │ │ - }, │ │ │ │ │ - scope: me │ │ │ │ │ - }); │ │ │ │ │ - })(); │ │ │ │ │ - }, │ │ │ │ │ - scope: this │ │ │ │ │ - }); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + // convert to m │ │ │ │ │ + return length * 1000; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ + CLASS_NAME: "OpenLayers.Geometry.Curve" │ │ │ │ │ +}); │ │ │ │ │ +/* ====================================================================== │ │ │ │ │ + OpenLayers/Geometry/LineString.js │ │ │ │ │ + ====================================================================== */ │ │ │ │ │ + │ │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ + * full text of the license. */ │ │ │ │ │ + │ │ │ │ │ +/** │ │ │ │ │ + * @requires OpenLayers/Geometry/Curve.js │ │ │ │ │ + */ │ │ │ │ │ + │ │ │ │ │ +/** │ │ │ │ │ + * Class: OpenLayers.Geometry.LineString │ │ │ │ │ + * A LineString is a Curve which, once two points have been added to it, can │ │ │ │ │ + * never be less than two points long. │ │ │ │ │ + * │ │ │ │ │ + * Inherits from: │ │ │ │ │ + * - │ │ │ │ │ + */ │ │ │ │ │ +OpenLayers.Geometry.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: output │ │ │ │ │ - * Chain an output of a configured process (see ) as input to │ │ │ │ │ - * another process. │ │ │ │ │ + * 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) │ │ │ │ │ * │ │ │ │ │ - * (code) │ │ │ │ │ - * intersect = client.getProcess('opengeo', 'JTS:intersection'); │ │ │ │ │ - * intersect.configure({ │ │ │ │ │ - * // ... │ │ │ │ │ - * }); │ │ │ │ │ - * buffer = client.getProcess('opengeo', 'JTS:buffer'); │ │ │ │ │ - * buffer.execute({ │ │ │ │ │ - * inputs: { │ │ │ │ │ - * geom: intersect.output('result'), // <-- here we're chaining │ │ │ │ │ - * distance: 1 │ │ │ │ │ - * }, │ │ │ │ │ - * // ... │ │ │ │ │ - * }); │ │ │ │ │ - * (end) │ │ │ │ │ + * Parameters: │ │ │ │ │ + * point - {} The point to be removed │ │ │ │ │ * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * identifier - {String} Identifier of the output that we're chaining. If │ │ │ │ │ - * not provided, the first output will be used. │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Boolean} The component was removed. │ │ │ │ │ */ │ │ │ │ │ - output: function(identifier) { │ │ │ │ │ - return new OpenLayers.WPSProcess.ChainLink({ │ │ │ │ │ - process: this, │ │ │ │ │ - output: identifier │ │ │ │ │ - }); │ │ │ │ │ + removeComponent: function(point) { │ │ │ │ │ + var removed = this.components && (this.components.length > 2); │ │ │ │ │ + if (removed) { │ │ │ │ │ + OpenLayers.Geometry.Collection.prototype.removeComponent.apply(this, │ │ │ │ │ + arguments); │ │ │ │ │ + } │ │ │ │ │ + return removed; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: parseDescription │ │ │ │ │ - * Parses the DescribeProcess response │ │ │ │ │ + * 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: │ │ │ │ │ - * description - {Object} │ │ │ │ │ + * geometry - {} │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Boolean} The input geometry intersects this geometry. │ │ │ │ │ */ │ │ │ │ │ - parseDescription: function(description) { │ │ │ │ │ - var server = this.client.servers[this.server]; │ │ │ │ │ - this.description = new OpenLayers.Format.WPSDescribeProcess() │ │ │ │ │ - .read(server.processDescription[this.identifier]) │ │ │ │ │ - .processDescriptions[this.identifier]; │ │ │ │ │ + intersects: function(geometry) { │ │ │ │ │ + var intersect = false; │ │ │ │ │ + var type = geometry.CLASS_NAME; │ │ │ │ │ + if (type == "OpenLayers.Geometry.LineString" || │ │ │ │ │ + type == "OpenLayers.Geometry.LinearRing" || │ │ │ │ │ + type == "OpenLayers.Geometry.Point") { │ │ │ │ │ + var segs1 = this.getSortedSegments(); │ │ │ │ │ + var segs2; │ │ │ │ │ + if (type == "OpenLayers.Geometry.Point") { │ │ │ │ │ + segs2 = [{ │ │ │ │ │ + x1: geometry.x, │ │ │ │ │ + y1: geometry.y, │ │ │ │ │ + x2: geometry.x, │ │ │ │ │ + y2: geometry.y │ │ │ │ │ + }]; │ │ │ │ │ + } else { │ │ │ │ │ + segs2 = geometry.getSortedSegments(); │ │ │ │ │ + } │ │ │ │ │ + var seg1, seg1x1, seg1x2, seg1y1, seg1y2, │ │ │ │ │ + seg2, seg2y1, seg2y2; │ │ │ │ │ + // sweep right │ │ │ │ │ + outer: for (var i = 0, len = segs1.length; i < len; ++i) { │ │ │ │ │ + seg1 = segs1[i]; │ │ │ │ │ + seg1x1 = seg1.x1; │ │ │ │ │ + seg1x2 = seg1.x2; │ │ │ │ │ + seg1y1 = seg1.y1; │ │ │ │ │ + seg1y2 = seg1.y2; │ │ │ │ │ + inner: for (var j = 0, jlen = segs2.length; j < jlen; ++j) { │ │ │ │ │ + seg2 = segs2[j]; │ │ │ │ │ + if (seg2.x1 > seg1x2) { │ │ │ │ │ + // seg1 still left of seg2 │ │ │ │ │ + break; │ │ │ │ │ + } │ │ │ │ │ + if (seg2.x2 < seg1x1) { │ │ │ │ │ + // seg2 still left of seg1 │ │ │ │ │ + continue; │ │ │ │ │ + } │ │ │ │ │ + seg2y1 = seg2.y1; │ │ │ │ │ + seg2y2 = seg2.y2; │ │ │ │ │ + if (Math.min(seg2y1, seg2y2) > Math.max(seg1y1, seg1y2)) { │ │ │ │ │ + // seg2 above seg1 │ │ │ │ │ + continue; │ │ │ │ │ + } │ │ │ │ │ + if (Math.max(seg2y1, seg2y2) < Math.min(seg1y1, seg1y2)) { │ │ │ │ │ + // seg2 below seg1 │ │ │ │ │ + continue; │ │ │ │ │ + } │ │ │ │ │ + if (OpenLayers.Geometry.segmentsIntersect(seg1, seg2)) { │ │ │ │ │ + intersect = true; │ │ │ │ │ + break outer; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } else { │ │ │ │ │ + intersect = geometry.intersects(this); │ │ │ │ │ + } │ │ │ │ │ + return intersect; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: setInputData │ │ │ │ │ - * Sets the data for a single input │ │ │ │ │ + * Method: getSortedSegments │ │ │ │ │ * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * input - {Object} An entry from the dataInputs array of the process │ │ │ │ │ - * description. │ │ │ │ │ - * data - {Mixed} For spatial data inputs, this is usually an │ │ │ │ │ - * , an or an array of │ │ │ │ │ - * geometries or features. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - setInputData: function(input, data) { │ │ │ │ │ - // clear any previous data │ │ │ │ │ - delete input.data; │ │ │ │ │ - delete input.reference; │ │ │ │ │ - if (data instanceof OpenLayers.WPSProcess.ChainLink) { │ │ │ │ │ - ++this.chained; │ │ │ │ │ - input.reference = { │ │ │ │ │ - method: 'POST', │ │ │ │ │ - href: data.process.server === this.server ? │ │ │ │ │ - this.localWPS : this.client.servers[data.process.server].url │ │ │ │ │ - }; │ │ │ │ │ - data.process.describe({ │ │ │ │ │ - callback: function() { │ │ │ │ │ - --this.chained; │ │ │ │ │ - this.chainProcess(input, data); │ │ │ │ │ - }, │ │ │ │ │ - scope: this │ │ │ │ │ - }); │ │ │ │ │ - } else { │ │ │ │ │ - input.data = {}; │ │ │ │ │ - var complexData = input.complexData; │ │ │ │ │ - if (complexData) { │ │ │ │ │ - var format = this.findMimeType(complexData.supported.formats); │ │ │ │ │ - input.data.complexData = { │ │ │ │ │ - mimeType: format, │ │ │ │ │ - value: this.formats[format].write(this.toFeatures(data)) │ │ │ │ │ + getSortedSegments: function() { │ │ │ │ │ + var numSeg = this.components.length - 1; │ │ │ │ │ + var segments = new Array(numSeg), │ │ │ │ │ + point1, point2; │ │ │ │ │ + for (var i = 0; i < numSeg; ++i) { │ │ │ │ │ + point1 = this.components[i]; │ │ │ │ │ + point2 = this.components[i + 1]; │ │ │ │ │ + if (point1.x < point2.x) { │ │ │ │ │ + segments[i] = { │ │ │ │ │ + x1: point1.x, │ │ │ │ │ + y1: point1.y, │ │ │ │ │ + x2: point2.x, │ │ │ │ │ + y2: point2.y │ │ │ │ │ }; │ │ │ │ │ } else { │ │ │ │ │ - input.data.literalData = { │ │ │ │ │ - value: data │ │ │ │ │ + segments[i] = { │ │ │ │ │ + x1: point2.x, │ │ │ │ │ + y1: point2.y, │ │ │ │ │ + x2: point1.x, │ │ │ │ │ + y2: point1.y │ │ │ │ │ }; │ │ │ │ │ } │ │ │ │ │ } │ │ │ │ │ + // more efficient to define this somewhere static │ │ │ │ │ + function byX1(seg1, seg2) { │ │ │ │ │ + return seg1.x1 - seg2.x1; │ │ │ │ │ + } │ │ │ │ │ + return segments.sort(byX1); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: setResponseForm │ │ │ │ │ - * Sets the responseForm property of the payload. │ │ │ │ │ + * Method: splitWithSegment │ │ │ │ │ + * Split this geometry with the given segment. │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * options - {Object} See below. │ │ │ │ │ + * seg - {Object} An object with x1, y1, x2, and y2 properties referencing │ │ │ │ │ + * segment endpoint coordinates. │ │ │ │ │ + * options - {Object} Properties of this object will be used to determine │ │ │ │ │ + * how the split is conducted. │ │ │ │ │ * │ │ │ │ │ - * Available options: │ │ │ │ │ - * outputIndex - {Integer} The index of the output to use. Optional. │ │ │ │ │ - * supportedFormats - {Object} Object with supported mime types as key, │ │ │ │ │ - * and true as value for supported types. Optional. │ │ │ │ │ + * Valid options: │ │ │ │ │ + * edge - {Boolean} Allow splitting when only edges intersect. Default is │ │ │ │ │ + * true. If false, a vertex on the source segment must be within the │ │ │ │ │ + * tolerance distance of the intersection to be considered a split. │ │ │ │ │ + * tolerance - {Number} If a non-null value is provided, intersections │ │ │ │ │ + * within the tolerance distance of one of the source segment's │ │ │ │ │ + * endpoints will be assumed to occur at the endpoint. │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Object} An object with *lines* and *points* properties. If the given │ │ │ │ │ + * segment intersects this linestring, the lines array will reference │ │ │ │ │ + * geometries that result from the split. The points array will contain │ │ │ │ │ + * all intersection points. Intersection points are sorted along the │ │ │ │ │ + * segment (in order from x1,y1 to x2,y2). │ │ │ │ │ */ │ │ │ │ │ - setResponseForm: function(options) { │ │ │ │ │ - options = options || {}; │ │ │ │ │ - var output = this.description.processOutputs[options.outputIndex || 0]; │ │ │ │ │ - this.description.responseForm = { │ │ │ │ │ - rawDataOutput: { │ │ │ │ │ - identifier: output.identifier, │ │ │ │ │ - mimeType: this.findMimeType(output.complexOutput.supported.formats, options.supportedFormats) │ │ │ │ │ - } │ │ │ │ │ + splitWithSegment: function(seg, options) { │ │ │ │ │ + var edge = !(options && options.edge === false); │ │ │ │ │ + var tolerance = options && options.tolerance; │ │ │ │ │ + var lines = []; │ │ │ │ │ + var verts = this.getVertices(); │ │ │ │ │ + var points = []; │ │ │ │ │ + var intersections = []; │ │ │ │ │ + var split = false; │ │ │ │ │ + var vert1, vert2, point; │ │ │ │ │ + var node, vertex, target; │ │ │ │ │ + var interOptions = { │ │ │ │ │ + point: true, │ │ │ │ │ + tolerance: tolerance │ │ │ │ │ }; │ │ │ │ │ + var result = null; │ │ │ │ │ + for (var i = 0, stop = verts.length - 2; i <= stop; ++i) { │ │ │ │ │ + vert1 = verts[i]; │ │ │ │ │ + points.push(vert1.clone()); │ │ │ │ │ + vert2 = verts[i + 1]; │ │ │ │ │ + target = { │ │ │ │ │ + x1: vert1.x, │ │ │ │ │ + y1: vert1.y, │ │ │ │ │ + x2: vert2.x, │ │ │ │ │ + y2: vert2.y │ │ │ │ │ + }; │ │ │ │ │ + point = OpenLayers.Geometry.segmentsIntersect( │ │ │ │ │ + seg, target, interOptions │ │ │ │ │ + ); │ │ │ │ │ + if (point instanceof OpenLayers.Geometry.Point) { │ │ │ │ │ + if ((point.x === seg.x1 && point.y === seg.y1) || │ │ │ │ │ + (point.x === seg.x2 && point.y === seg.y2) || │ │ │ │ │ + point.equals(vert1) || point.equals(vert2)) { │ │ │ │ │ + vertex = true; │ │ │ │ │ + } else { │ │ │ │ │ + vertex = false; │ │ │ │ │ + } │ │ │ │ │ + if (vertex || edge) { │ │ │ │ │ + // push intersections different than the previous │ │ │ │ │ + if (!point.equals(intersections[intersections.length - 1])) { │ │ │ │ │ + intersections.push(point.clone()); │ │ │ │ │ + } │ │ │ │ │ + if (i === 0) { │ │ │ │ │ + if (point.equals(vert1)) { │ │ │ │ │ + continue; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + if (point.equals(vert2)) { │ │ │ │ │ + continue; │ │ │ │ │ + } │ │ │ │ │ + split = true; │ │ │ │ │ + if (!point.equals(vert1)) { │ │ │ │ │ + points.push(point); │ │ │ │ │ + } │ │ │ │ │ + lines.push(new OpenLayers.Geometry.LineString(points)); │ │ │ │ │ + points = [point.clone()]; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + if (split) { │ │ │ │ │ + points.push(vert2.clone()); │ │ │ │ │ + lines.push(new OpenLayers.Geometry.LineString(points)); │ │ │ │ │ + } │ │ │ │ │ + if (intersections.length > 0) { │ │ │ │ │ + // sort intersections along segment │ │ │ │ │ + var xDir = seg.x1 < seg.x2 ? 1 : -1; │ │ │ │ │ + var yDir = seg.y1 < seg.y2 ? 1 : -1; │ │ │ │ │ + result = { │ │ │ │ │ + lines: lines, │ │ │ │ │ + points: intersections.sort(function(p1, p2) { │ │ │ │ │ + return (xDir * p1.x - xDir * p2.x) || (yDir * p1.y - yDir * p2.y); │ │ │ │ │ + }) │ │ │ │ │ + }; │ │ │ │ │ + } │ │ │ │ │ + return result; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: getOutputIndex │ │ │ │ │ - * Gets the index of a processOutput by its identifier │ │ │ │ │ - * │ │ │ │ │ + * Method: split │ │ │ │ │ + * Use this geometry (the source) to attempt to split a target geometry. │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * outputs - {Array} The processOutputs array to look at │ │ │ │ │ - * identifier - {String} The identifier of the output │ │ │ │ │ + * target - {} The target geometry. │ │ │ │ │ + * options - {Object} Properties of this object will be used to determine │ │ │ │ │ + * how the split is conducted. │ │ │ │ │ * │ │ │ │ │ - * Returns │ │ │ │ │ - * {Integer} The index of the processOutput with the provided identifier │ │ │ │ │ - * in the outputs array. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - getOutputIndex: function(outputs, identifier) { │ │ │ │ │ - var output; │ │ │ │ │ - if (identifier) { │ │ │ │ │ - for (var i = outputs.length - 1; i >= 0; --i) { │ │ │ │ │ - if (outputs[i].identifier === identifier) { │ │ │ │ │ - output = i; │ │ │ │ │ - break; │ │ │ │ │ + split: function(target, options) { │ │ │ │ │ + var results = null; │ │ │ │ │ + var mutual = options && options.mutual; │ │ │ │ │ + var sourceSplit, targetSplit, sourceParts, targetParts; │ │ │ │ │ + if (target instanceof OpenLayers.Geometry.LineString) { │ │ │ │ │ + var verts = this.getVertices(); │ │ │ │ │ + var vert1, vert2, seg, splits, lines, point; │ │ │ │ │ + var points = []; │ │ │ │ │ + sourceParts = []; │ │ │ │ │ + for (var i = 0, stop = verts.length - 2; i <= stop; ++i) { │ │ │ │ │ + vert1 = verts[i]; │ │ │ │ │ + vert2 = verts[i + 1]; │ │ │ │ │ + seg = { │ │ │ │ │ + x1: vert1.x, │ │ │ │ │ + y1: vert1.y, │ │ │ │ │ + x2: vert2.x, │ │ │ │ │ + y2: vert2.y │ │ │ │ │ + }; │ │ │ │ │ + targetParts = targetParts || [target]; │ │ │ │ │ + if (mutual) { │ │ │ │ │ + points.push(vert1.clone()); │ │ │ │ │ + } │ │ │ │ │ + for (var j = 0; j < targetParts.length; ++j) { │ │ │ │ │ + splits = targetParts[j].splitWithSegment(seg, options); │ │ │ │ │ + if (splits) { │ │ │ │ │ + // splice in new features │ │ │ │ │ + lines = splits.lines; │ │ │ │ │ + if (lines.length > 0) { │ │ │ │ │ + lines.unshift(j, 1); │ │ │ │ │ + Array.prototype.splice.apply(targetParts, lines); │ │ │ │ │ + j += lines.length - 2; │ │ │ │ │ + } │ │ │ │ │ + if (mutual) { │ │ │ │ │ + for (var k = 0, len = splits.points.length; k < len; ++k) { │ │ │ │ │ + point = splits.points[k]; │ │ │ │ │ + if (!point.equals(vert1)) { │ │ │ │ │ + points.push(point); │ │ │ │ │ + sourceParts.push(new OpenLayers.Geometry.LineString(points)); │ │ │ │ │ + if (point.equals(vert2)) { │ │ │ │ │ + points = []; │ │ │ │ │ + } else { │ │ │ │ │ + points = [point.clone()]; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ } │ │ │ │ │ } │ │ │ │ │ + if (mutual && sourceParts.length > 0 && points.length > 0) { │ │ │ │ │ + points.push(vert2.clone()); │ │ │ │ │ + sourceParts.push(new OpenLayers.Geometry.LineString(points)); │ │ │ │ │ + } │ │ │ │ │ } else { │ │ │ │ │ - output = 0; │ │ │ │ │ + results = target.splitWith(this, options); │ │ │ │ │ } │ │ │ │ │ - return output; │ │ │ │ │ + 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: chainProcess │ │ │ │ │ - * Sets a fully configured chained process as input for this process. │ │ │ │ │ + * Method: splitWith │ │ │ │ │ + * Split this geometry (the target) with the given geometry (the source). │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * input - {Object} The dataInput that the chained process provides. │ │ │ │ │ - * chainLink - {} The process to chain. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - chainProcess: function(input, chainLink) { │ │ │ │ │ - var output = this.getOutputIndex( │ │ │ │ │ - chainLink.process.description.processOutputs, chainLink.output │ │ │ │ │ - ); │ │ │ │ │ - input.reference.mimeType = this.findMimeType( │ │ │ │ │ - input.complexData.supported.formats, │ │ │ │ │ - chainLink.process.description.processOutputs[output].complexOutput.supported.formats │ │ │ │ │ - ); │ │ │ │ │ - var formats = {}; │ │ │ │ │ - formats[input.reference.mimeType] = true; │ │ │ │ │ - chainLink.process.setResponseForm({ │ │ │ │ │ - outputIndex: output, │ │ │ │ │ - supportedFormats: formats │ │ │ │ │ - }); │ │ │ │ │ - input.reference.body = chainLink.process.description; │ │ │ │ │ - while (this.executeCallbacks.length > 0) { │ │ │ │ │ - this.executeCallbacks[0](); │ │ │ │ │ - } │ │ │ │ │ + splitWith: function(geometry, options) { │ │ │ │ │ + return geometry.split(this, options); │ │ │ │ │ + │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: toFeatures │ │ │ │ │ - * Converts spatial input into features so it can be processed by │ │ │ │ │ - * instances. │ │ │ │ │ + * APIMethod: getVertices │ │ │ │ │ + * Return a list of all points in this geometry. │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * source - {Mixed} An , an │ │ │ │ │ - * , or an array of geometries or features │ │ │ │ │ + * 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()} │ │ │ │ │ + * {Array} A list of all vertices in the geometry. │ │ │ │ │ */ │ │ │ │ │ - toFeatures: function(source) { │ │ │ │ │ - var isArray = OpenLayers.Util.isArray(source); │ │ │ │ │ - if (!isArray) { │ │ │ │ │ - source = [source]; │ │ │ │ │ - } │ │ │ │ │ - var target = new Array(source.length), │ │ │ │ │ - current; │ │ │ │ │ - for (var i = 0, ii = source.length; i < ii; ++i) { │ │ │ │ │ - current = source[i]; │ │ │ │ │ - target[i] = current instanceof OpenLayers.Feature.Vector ? │ │ │ │ │ - current : new OpenLayers.Feature.Vector(current); │ │ │ │ │ + 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 isArray ? target : target[0]; │ │ │ │ │ + return vertices; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: findMimeType │ │ │ │ │ - * Finds a supported mime type. │ │ │ │ │ + * APIMethod: distanceTo │ │ │ │ │ + * Calculate the closest distance between two geometries (on the x-y plane). │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * sourceFormats - {Object} An object literal with mime types as key and │ │ │ │ │ - * true as value for supported formats. │ │ │ │ │ - * targetFormats - {Object} Like , but optional to check for │ │ │ │ │ - * supported mime types on a different target than this process. │ │ │ │ │ - * Default is to check against this process's supported formats. │ │ │ │ │ + * 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: │ │ │ │ │ - * {String} A supported mime type. │ │ │ │ │ + * {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. │ │ │ │ │ */ │ │ │ │ │ - findMimeType: function(sourceFormats, targetFormats) { │ │ │ │ │ - targetFormats = targetFormats || this.formats; │ │ │ │ │ - for (var f in sourceFormats) { │ │ │ │ │ - if (f in targetFormats) { │ │ │ │ │ - return f; │ │ │ │ │ + distanceTo: function(geometry, options) { │ │ │ │ │ + var edge = !(options && options.edge === false); │ │ │ │ │ + var details = edge && options && options.details; │ │ │ │ │ + var result, best = {}; │ │ │ │ │ + var min = Number.POSITIVE_INFINITY; │ │ │ │ │ + if (geometry instanceof OpenLayers.Geometry.Point) { │ │ │ │ │ + var segs = this.getSortedSegments(); │ │ │ │ │ + var x = geometry.x; │ │ │ │ │ + var y = geometry.y; │ │ │ │ │ + var seg; │ │ │ │ │ + for (var i = 0, len = segs.length; i < len; ++i) { │ │ │ │ │ + seg = segs[i]; │ │ │ │ │ + result = OpenLayers.Geometry.distanceToSegment(geometry, seg); │ │ │ │ │ + if (result.distance < min) { │ │ │ │ │ + min = result.distance; │ │ │ │ │ + best = result; │ │ │ │ │ + if (min === 0) { │ │ │ │ │ + break; │ │ │ │ │ + } │ │ │ │ │ + } else { │ │ │ │ │ + // if distance increases and we cross y0 to the right of x0, no need to keep looking. │ │ │ │ │ + if (seg.x2 > x && ((y > seg.y1 && y < seg.y2) || (y < seg.y1 && y > seg.y2))) { │ │ │ │ │ + break; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + if (details) { │ │ │ │ │ + best = { │ │ │ │ │ + distance: best.distance, │ │ │ │ │ + x0: best.x, │ │ │ │ │ + y0: best.y, │ │ │ │ │ + x1: x, │ │ │ │ │ + y1: y │ │ │ │ │ + }; │ │ │ │ │ + } else { │ │ │ │ │ + best = best.distance; │ │ │ │ │ + } │ │ │ │ │ + } else if (geometry instanceof OpenLayers.Geometry.LineString) { │ │ │ │ │ + var segs0 = this.getSortedSegments(); │ │ │ │ │ + var segs1 = geometry.getSortedSegments(); │ │ │ │ │ + var seg0, seg1, intersection, x0, y0; │ │ │ │ │ + var len1 = segs1.length; │ │ │ │ │ + var interOptions = { │ │ │ │ │ + point: true │ │ │ │ │ + }; │ │ │ │ │ + outer: for (var i = 0, len = segs0.length; i < len; ++i) { │ │ │ │ │ + seg0 = segs0[i]; │ │ │ │ │ + x0 = seg0.x1; │ │ │ │ │ + y0 = seg0.y1; │ │ │ │ │ + for (var j = 0; j < len1; ++j) { │ │ │ │ │ + seg1 = segs1[j]; │ │ │ │ │ + intersection = OpenLayers.Geometry.segmentsIntersect(seg0, seg1, interOptions); │ │ │ │ │ + if (intersection) { │ │ │ │ │ + min = 0; │ │ │ │ │ + best = { │ │ │ │ │ + distance: 0, │ │ │ │ │ + x0: intersection.x, │ │ │ │ │ + y0: intersection.y, │ │ │ │ │ + x1: intersection.x, │ │ │ │ │ + y1: intersection.y │ │ │ │ │ + }; │ │ │ │ │ + break outer; │ │ │ │ │ + } else { │ │ │ │ │ + result = OpenLayers.Geometry.distanceToSegment({ │ │ │ │ │ + x: x0, │ │ │ │ │ + y: y0 │ │ │ │ │ + }, seg1); │ │ │ │ │ + if (result.distance < min) { │ │ │ │ │ + min = result.distance; │ │ │ │ │ + best = { │ │ │ │ │ + distance: min, │ │ │ │ │ + x0: x0, │ │ │ │ │ + y0: y0, │ │ │ │ │ + x1: result.x, │ │ │ │ │ + y1: result.y │ │ │ │ │ + }; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + if (!details) { │ │ │ │ │ + best = best.distance; │ │ │ │ │ + } │ │ │ │ │ + if (min !== 0) { │ │ │ │ │ + // check the final vertex in this line's sorted segments │ │ │ │ │ + if (seg0) { │ │ │ │ │ + result = geometry.distanceTo( │ │ │ │ │ + new OpenLayers.Geometry.Point(seg0.x2, seg0.y2), │ │ │ │ │ + options │ │ │ │ │ + ); │ │ │ │ │ + var dist = details ? result.distance : result; │ │ │ │ │ + if (dist < min) { │ │ │ │ │ + if (details) { │ │ │ │ │ + best = { │ │ │ │ │ + distance: min, │ │ │ │ │ + x0: result.x1, │ │ │ │ │ + y0: result.y1, │ │ │ │ │ + x1: result.x0, │ │ │ │ │ + y1: result.y0 │ │ │ │ │ + }; │ │ │ │ │ + } else { │ │ │ │ │ + best = dist; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } else { │ │ │ │ │ + best = geometry.distanceTo(this, options); │ │ │ │ │ + // swap since target comes from this line │ │ │ │ │ + if (details) { │ │ │ │ │ + best = { │ │ │ │ │ + distance: best.distance, │ │ │ │ │ + x0: best.x1, │ │ │ │ │ + y0: best.y1, │ │ │ │ │ + x1: best.x0, │ │ │ │ │ + y1: best.y0 │ │ │ │ │ + }; │ │ │ │ │ } │ │ │ │ │ } │ │ │ │ │ + return best; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.WPSProcess" │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: simplify │ │ │ │ │ + * This function will return a simplified LineString. │ │ │ │ │ + * Simplification is based on the Douglas-Peucker algorithm. │ │ │ │ │ + * │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * tolerance - {number} threshhold for simplification in map units │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {OpenLayers.Geometry.LineString} the simplified LineString │ │ │ │ │ + */ │ │ │ │ │ + simplify: function(tolerance) { │ │ │ │ │ + if (this && this !== null) { │ │ │ │ │ + var points = this.getVertices(); │ │ │ │ │ + if (points.length < 3) { │ │ │ │ │ + return this; │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ -}); │ │ │ │ │ + var compareNumbers = function(a, b) { │ │ │ │ │ + return (a - b); │ │ │ │ │ + }; │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * Class: OpenLayers.WPSProcess.ChainLink │ │ │ │ │ - * Type for chaining processes. │ │ │ │ │ - */ │ │ │ │ │ -OpenLayers.WPSProcess.ChainLink = OpenLayers.Class({ │ │ │ │ │ + /** │ │ │ │ │ + * Private function doing the Douglas-Peucker reduction │ │ │ │ │ + */ │ │ │ │ │ + var douglasPeuckerReduction = function(points, firstPoint, lastPoint, tolerance) { │ │ │ │ │ + var maxDistance = 0; │ │ │ │ │ + var indexFarthest = 0; │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: process │ │ │ │ │ - * {} The process to chain │ │ │ │ │ - */ │ │ │ │ │ - process: null, │ │ │ │ │ + for (var index = firstPoint, distance; index < lastPoint; index++) { │ │ │ │ │ + distance = perpendicularDistance(points[firstPoint], points[lastPoint], points[index]); │ │ │ │ │ + if (distance > maxDistance) { │ │ │ │ │ + maxDistance = distance; │ │ │ │ │ + indexFarthest = index; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: output │ │ │ │ │ - * {String} The output identifier of the output we are going to use as │ │ │ │ │ - * input for another process. │ │ │ │ │ - */ │ │ │ │ │ - output: null, │ │ │ │ │ + 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); │ │ │ │ │ + } │ │ │ │ │ + }; │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Constructor: OpenLayers.WPSProcess.ChainLink │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * options - {Object} Properties to set on the instance. │ │ │ │ │ - */ │ │ │ │ │ - initialize: function(options) { │ │ │ │ │ - OpenLayers.Util.extend(this, options); │ │ │ │ │ - }, │ │ │ │ │ + /** │ │ │ │ │ + * 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 │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.WPSProcess.ChainLink" │ │ │ │ │ + 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/Rule.js │ │ │ │ │ + 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 │ │ │ │ │ + */ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ │ - * @requires OpenLayers/Util.js │ │ │ │ │ - * @requires OpenLayers/Style.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 < len; ++i) { │ │ │ │ │ + sourceLine = this.components[i]; │ │ │ │ │ + sourceSplit = false; │ │ │ │ │ + for (var j = 0; j < targetParts.length; ++j) { │ │ │ │ │ + splits = sourceLine.split(targetParts[j], options); │ │ │ │ │ + if (splits) { │ │ │ │ │ + if (mutual) { │ │ │ │ │ + sourceLines = splits[0]; │ │ │ │ │ + for (var k = 0, klen = sourceLines.length; k < klen; ++k) { │ │ │ │ │ + if (k === 0 && sourceParts.length) { │ │ │ │ │ + sourceParts[sourceParts.length - 1].addComponent( │ │ │ │ │ + sourceLines[k] │ │ │ │ │ + ); │ │ │ │ │ + } else { │ │ │ │ │ + sourceParts.push( │ │ │ │ │ + new OpenLayers.Geometry.MultiLineString([ │ │ │ │ │ + sourceLines[k] │ │ │ │ │ + ]) │ │ │ │ │ + ); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + sourceSplit = true; │ │ │ │ │ + splits = splits[1]; │ │ │ │ │ + } │ │ │ │ │ + if (splits.length) { │ │ │ │ │ + // splice in new target parts │ │ │ │ │ + splits.unshift(j, 1); │ │ │ │ │ + Array.prototype.splice.apply(targetParts, splits); │ │ │ │ │ + break; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + if (!sourceSplit) { │ │ │ │ │ + // source line was not hit │ │ │ │ │ + if (sourceParts.length) { │ │ │ │ │ + // add line to existing multi │ │ │ │ │ + sourceParts[sourceParts.length - 1].addComponent( │ │ │ │ │ + sourceLine.clone() │ │ │ │ │ + ); │ │ │ │ │ + } else { │ │ │ │ │ + // create a fresh multi │ │ │ │ │ + sourceParts = [ │ │ │ │ │ + new OpenLayers.Geometry.MultiLineString( │ │ │ │ │ + sourceLine.clone() │ │ │ │ │ + ) │ │ │ │ │ + ]; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + if (sourceParts && sourceParts.length > 1) { │ │ │ │ │ + sourceSplit = true; │ │ │ │ │ + } else { │ │ │ │ │ + sourceParts = []; │ │ │ │ │ + } │ │ │ │ │ + if (targetParts && targetParts.length > 1) { │ │ │ │ │ + targetSplit = true; │ │ │ │ │ + } else { │ │ │ │ │ + targetParts = []; │ │ │ │ │ + } │ │ │ │ │ + if (sourceSplit || targetSplit) { │ │ │ │ │ + if (mutual) { │ │ │ │ │ + results = [sourceParts, targetParts]; │ │ │ │ │ + } else { │ │ │ │ │ + results = targetParts; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + return results; │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Method: splitWith │ │ │ │ │ + * Split this geometry (the target) with the given geometry (the source). │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * geometry - {} A geometry used to split this │ │ │ │ │ + * geometry (the source). │ │ │ │ │ + * options - {Object} Properties of this object will be used to determine │ │ │ │ │ + * how the split is conducted. │ │ │ │ │ + * │ │ │ │ │ + * Valid options: │ │ │ │ │ + * mutual - {Boolean} Split the source geometry in addition to the target │ │ │ │ │ + * geometry. Default is false. │ │ │ │ │ + * edge - {Boolean} Allow splitting when only edges intersect. Default is │ │ │ │ │ + * true. If false, a vertex on the source must be within the tolerance │ │ │ │ │ + * distance of the intersection to be considered a split. │ │ │ │ │ + * tolerance - {Number} If a non-null value is provided, intersections │ │ │ │ │ + * within the tolerance distance of an existing vertex on the source │ │ │ │ │ + * will be assumed to occur at the vertex. │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Array} A list of geometries (of this same type as the target) that │ │ │ │ │ + * result from splitting the target with the source geometry. The │ │ │ │ │ + * source and target geometry will remain unmodified. If no split │ │ │ │ │ + * results, null will be returned. If mutual is true and a split │ │ │ │ │ + * results, return will be an array of two arrays - the first will be │ │ │ │ │ + * all geometries that result from splitting the source geometry and │ │ │ │ │ + * the second will be all geometries that result from splitting the │ │ │ │ │ + * target geometry. │ │ │ │ │ + */ │ │ │ │ │ + splitWith: function(geometry, options) { │ │ │ │ │ + var results = null; │ │ │ │ │ + var mutual = options && options.mutual; │ │ │ │ │ + var splits, targetLine, sourceLines, sourceSplit, targetSplit, sourceParts, targetParts; │ │ │ │ │ + if (geometry instanceof OpenLayers.Geometry.LineString) { │ │ │ │ │ + targetParts = []; │ │ │ │ │ + sourceParts = [geometry]; │ │ │ │ │ + for (var i = 0, len = this.components.length; i < len; ++i) { │ │ │ │ │ + targetSplit = false; │ │ │ │ │ + targetLine = this.components[i]; │ │ │ │ │ + for (var j = 0; j < sourceParts.length; ++j) { │ │ │ │ │ + splits = sourceParts[j].split(targetLine, options); │ │ │ │ │ + if (splits) { │ │ │ │ │ + if (mutual) { │ │ │ │ │ + sourceLines = splits[0]; │ │ │ │ │ + if (sourceLines.length) { │ │ │ │ │ + // splice in new source parts │ │ │ │ │ + sourceLines.unshift(j, 1); │ │ │ │ │ + Array.prototype.splice.apply(sourceParts, sourceLines); │ │ │ │ │ + j += sourceLines.length - 2; │ │ │ │ │ + } │ │ │ │ │ + splits = splits[1]; │ │ │ │ │ + if (splits.length === 0) { │ │ │ │ │ + splits = [targetLine.clone()]; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + for (var k = 0, klen = splits.length; k < klen; ++k) { │ │ │ │ │ + if (k === 0 && targetParts.length) { │ │ │ │ │ + targetParts[targetParts.length - 1].addComponent( │ │ │ │ │ + splits[k] │ │ │ │ │ + ); │ │ │ │ │ + } else { │ │ │ │ │ + targetParts.push( │ │ │ │ │ + new OpenLayers.Geometry.MultiLineString([ │ │ │ │ │ + splits[k] │ │ │ │ │ + ]) │ │ │ │ │ + ); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + targetSplit = true; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + if (!targetSplit) { │ │ │ │ │ + // target component was not hit │ │ │ │ │ + if (targetParts.length) { │ │ │ │ │ + // add it to any existing multi-line │ │ │ │ │ + targetParts[targetParts.length - 1].addComponent( │ │ │ │ │ + targetLine.clone() │ │ │ │ │ + ); │ │ │ │ │ + } else { │ │ │ │ │ + // or start with a fresh multi-line │ │ │ │ │ + targetParts = [ │ │ │ │ │ + new OpenLayers.Geometry.MultiLineString([ │ │ │ │ │ + targetLine.clone() │ │ │ │ │ + ]) │ │ │ │ │ + ]; │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } else { │ │ │ │ │ + results = geometry.split(this); │ │ │ │ │ + } │ │ │ │ │ + if (sourceParts && sourceParts.length > 1) { │ │ │ │ │ + sourceSplit = true; │ │ │ │ │ + } else { │ │ │ │ │ + sourceParts = []; │ │ │ │ │ + } │ │ │ │ │ + if (targetParts && targetParts.length > 1) { │ │ │ │ │ + targetSplit = true; │ │ │ │ │ + } else { │ │ │ │ │ + targetParts = []; │ │ │ │ │ + } │ │ │ │ │ + if (sourceSplit || targetSplit) { │ │ │ │ │ + if (mutual) { │ │ │ │ │ + results = [sourceParts, targetParts]; │ │ │ │ │ + } else { │ │ │ │ │ + results = targetParts; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + return results; │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + CLASS_NAME: "OpenLayers.Geometry.MultiLineString" │ │ │ │ │ + }); │ │ │ │ │ +/* ====================================================================== │ │ │ │ │ + OpenLayers/Geometry/LinearRing.js │ │ │ │ │ + ====================================================================== */ │ │ │ │ │ + │ │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ + * full text of the license. */ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Class: OpenLayers.Rule │ │ │ │ │ - * This class represents an SLD Rule, as being used for rule-based SLD styling. │ │ │ │ │ + * @requires OpenLayers/Geometry/LineString.js │ │ │ │ │ */ │ │ │ │ │ -OpenLayers.Rule = OpenLayers.Class({ │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: id │ │ │ │ │ - * {String} A unique id for this session. │ │ │ │ │ - */ │ │ │ │ │ - id: null, │ │ │ │ │ +/** │ │ │ │ │ + * 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, { │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIProperty: name │ │ │ │ │ - * {String} name of this rule │ │ │ │ │ - */ │ │ │ │ │ - name: 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: ["OpenLayers.Geometry.Point"], │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: title │ │ │ │ │ - * {String} Title of this rule (set if included in SLD) │ │ │ │ │ - */ │ │ │ │ │ - title: null, │ │ │ │ │ + /** │ │ │ │ │ + * 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 │ │ │ │ │ + */ │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: description │ │ │ │ │ - * {String} Description of this rule (set if abstract is included in SLD) │ │ │ │ │ - */ │ │ │ │ │ - description: null, │ │ │ │ │ + /** │ │ │ │ │ + * 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; │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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, │ │ │ │ │ + //remove last point │ │ │ │ │ + var lastPoint = this.components.pop(); │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: filter │ │ │ │ │ - * {} Optional filter for the rule. │ │ │ │ │ - */ │ │ │ │ │ - filter: null, │ │ │ │ │ + // 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); │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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, │ │ │ │ │ + //append copy of first point │ │ │ │ │ + var firstPoint = this.components[0]; │ │ │ │ │ + OpenLayers.Geometry.Collection.prototype.addComponent.apply(this, │ │ │ │ │ + [firstPoint]); │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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, │ │ │ │ │ + return added; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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, │ │ │ │ │ + /** │ │ │ │ │ + * 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(); │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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, │ │ │ │ │ + //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; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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, │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: move │ │ │ │ │ + * Moves a geometry by the given displacement along positive x and y axes. │ │ │ │ │ + * This modifies the position of the geometry and clears the cached │ │ │ │ │ + * bounds. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * x - {Float} Distance to move geometry in positive x direction. │ │ │ │ │ + * y - {Float} Distance to move geometry in positive y direction. │ │ │ │ │ + */ │ │ │ │ │ + move: function(x, y) { │ │ │ │ │ + for (var i = 0, len = this.components.length; i < len - 1; i++) { │ │ │ │ │ + this.components[i].move(x, y); │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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: rotate │ │ │ │ │ + * Rotate a geometry around some origin │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * angle - {Float} Rotation angle in degrees (measured counterclockwise │ │ │ │ │ + * from the positive x-axis) │ │ │ │ │ + * origin - {} Center point for the rotation │ │ │ │ │ + */ │ │ │ │ │ + rotate: function(angle, origin) { │ │ │ │ │ + for (var i = 0, len = this.components.length; i < len - 1; ++i) { │ │ │ │ │ + this.components[i].rotate(angle, origin); │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIMethod: 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: resize │ │ │ │ │ + * Resize a geometry relative to some origin. Use this method to apply │ │ │ │ │ + * a uniform scaling to a geometry. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * scale - {Float} Factor by which to scale the geometry. A scale of 2 │ │ │ │ │ + * doubles the size of the geometry in each dimension │ │ │ │ │ + * (lines, for example, will be twice as long, and polygons │ │ │ │ │ + * will have four times the area). │ │ │ │ │ + * origin - {} 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 < len - 1; ++i) { │ │ │ │ │ + this.components[i].resize(scale, origin, ratio); │ │ │ │ │ + } │ │ │ │ │ + return this; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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; │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: transform │ │ │ │ │ + * Reproject the components geometry from source to dest. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * source - {} │ │ │ │ │ + * dest - {} │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {} │ │ │ │ │ + */ │ │ │ │ │ + transform: function(source, dest) { │ │ │ │ │ + if (source && dest) { │ │ │ │ │ + for (var i = 0, len = this.components.length; i < len - 1; i++) { │ │ │ │ │ + var component = this.components[i]; │ │ │ │ │ + component.transform(source, dest); │ │ │ │ │ + } │ │ │ │ │ + this.bounds = null; │ │ │ │ │ + } │ │ │ │ │ + return this; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - if (this.minScaleDenominator || this.maxScaleDenominator) { │ │ │ │ │ - var scale = feature.layer.map.getScale(); │ │ │ │ │ - } │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: getCentroid │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {} 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; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - // 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); │ │ │ │ │ - } │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: getArea │ │ │ │ │ + * Note - The area is positive if the ring is oriented CW, otherwise │ │ │ │ │ + * it will be negative. │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Float} The signed area for a ring. │ │ │ │ │ + */ │ │ │ │ │ + getArea: function() { │ │ │ │ │ + var area = 0.0; │ │ │ │ │ + if (this.components && (this.components.length > 2)) { │ │ │ │ │ + var sum = 0.0; │ │ │ │ │ + for (var i = 0, len = this.components.length; i < len - 1; i++) { │ │ │ │ │ + var b = this.components[i]; │ │ │ │ │ + var c = this.components[i + 1]; │ │ │ │ │ + sum += (b.x + c.x) * (c.y - b.y); │ │ │ │ │ + } │ │ │ │ │ + area = -sum / 2.0; │ │ │ │ │ + } │ │ │ │ │ + return area; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - // 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); │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: getGeodesicArea │ │ │ │ │ + * Calculate the approximate area of the polygon were it projected onto │ │ │ │ │ + * the earth. Note that this area will be positive if ring is oriented │ │ │ │ │ + * clockwise, otherwise it will be negative. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * projection - {} The spatial reference system │ │ │ │ │ + * for the geometry coordinates. If not provided, Geographic/WGS84 is │ │ │ │ │ + * assumed. │ │ │ │ │ + * │ │ │ │ │ + * Reference: │ │ │ │ │ + * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for │ │ │ │ │ + * Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion │ │ │ │ │ + * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409 │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {float} The approximate signed geodesic area of the polygon in square │ │ │ │ │ + * meters. │ │ │ │ │ + */ │ │ │ │ │ + getGeodesicArea: function(projection) { │ │ │ │ │ + var ring = this; // so we can work with a clone if needed │ │ │ │ │ + if (projection) { │ │ │ │ │ + var gg = new OpenLayers.Projection("EPSG:4326"); │ │ │ │ │ + if (!gg.equals(projection)) { │ │ │ │ │ + ring = this.clone().transform(projection, gg); │ │ │ │ │ + } │ │ │ │ │ } │ │ │ │ │ - } │ │ │ │ │ + var area = 0.0; │ │ │ │ │ + var len = ring.components && ring.components.length; │ │ │ │ │ + if (len > 2) { │ │ │ │ │ + var p1, p2; │ │ │ │ │ + for (var i = 0; i < len - 1; i++) { │ │ │ │ │ + p1 = ring.components[i]; │ │ │ │ │ + p2 = ring.components[i + 1]; │ │ │ │ │ + area += OpenLayers.Util.rad(p2.x - p1.x) * │ │ │ │ │ + (2 + Math.sin(OpenLayers.Util.rad(p1.y)) + │ │ │ │ │ + Math.sin(OpenLayers.Util.rad(p2.y))); │ │ │ │ │ + } │ │ │ │ │ + area = area * 6378137.0 * 6378137.0 / 2.0; │ │ │ │ │ + } │ │ │ │ │ + return area; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - return applies; │ │ │ │ │ - }, │ │ │ │ │ + /** │ │ │ │ │ + * Method: containsPoint │ │ │ │ │ + * Test if a point is inside a linear ring. For the case where a point │ │ │ │ │ + * is coincident with a linear ring edge, returns 1. Otherwise, │ │ │ │ │ + * returns boolean. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * point - {} │ │ │ │ │ + * │ │ │ │ │ + * 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); │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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; │ │ │ │ │ - }, │ │ │ │ │ + function getX(y, x1, y1, x2, y2) { │ │ │ │ │ + return (y - y2) * ((x2 - x1) / (y2 - y1)) + x2; │ │ │ │ │ + } │ │ │ │ │ + var numSeg = this.components.length - 1; │ │ │ │ │ + var start, end, x1, y1, x2, y2, cx, cy; │ │ │ │ │ + var crosses = 0; │ │ │ │ │ + for (var i = 0; i < numSeg; ++i) { │ │ │ │ │ + start = this.components[i]; │ │ │ │ │ + x1 = approx(start.x, digs); │ │ │ │ │ + y1 = approx(start.y, digs); │ │ │ │ │ + end = this.components[i + 1]; │ │ │ │ │ + x2 = approx(end.x, digs); │ │ │ │ │ + y2 = approx(end.y, digs); │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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 < len; ++i) { │ │ │ │ │ - options.symbolizers[i] = this.symbolizers[i].clone(); │ │ │ │ │ + /** │ │ │ │ │ + * The following conditions enforce five edge-crossing rules: │ │ │ │ │ + * 1. points coincident with edges are considered contained; │ │ │ │ │ + * 2. an upward edge includes its starting endpoint, and │ │ │ │ │ + * excludes its final endpoint; │ │ │ │ │ + * 3. a downward edge excludes its starting endpoint, and │ │ │ │ │ + * includes its final endpoint; │ │ │ │ │ + * 4. horizontal edges are excluded; and │ │ │ │ │ + * 5. the edge-ray intersection point must be strictly right │ │ │ │ │ + * of the point P. │ │ │ │ │ + */ │ │ │ │ │ + if (y1 == y2) { │ │ │ │ │ + // horizontal edge │ │ │ │ │ + if (py == y1) { │ │ │ │ │ + // point on horizontal line │ │ │ │ │ + if (x1 <= x2 && (px >= x1 && px <= x2) || // right or vert │ │ │ │ │ + x1 >= x2 && (px <= x1 && px >= x2)) { // left or vert │ │ │ │ │ + // point on edge │ │ │ │ │ + crosses = -1; │ │ │ │ │ + break; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + // ignore other horizontal edges │ │ │ │ │ + continue; │ │ │ │ │ + } │ │ │ │ │ + cx = approx(getX(py, x1, y1, x2, y2), digs); │ │ │ │ │ + if (cx == px) { │ │ │ │ │ + // point on line │ │ │ │ │ + if (y1 < y2 && (py >= y1 && py <= y2) || // upward │ │ │ │ │ + y1 > y2 && (py <= y1 && py >= y2)) { // downward │ │ │ │ │ + // point on edge │ │ │ │ │ + crosses = -1; │ │ │ │ │ + break; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + if (cx <= px) { │ │ │ │ │ + // no crossing to the right │ │ │ │ │ + continue; │ │ │ │ │ + } │ │ │ │ │ + if (x1 != x2 && (cx < Math.min(x1, x2) || cx > Math.max(x1, x2))) { │ │ │ │ │ + // no crossing │ │ │ │ │ + continue; │ │ │ │ │ + } │ │ │ │ │ + if (y1 < y2 && (py >= y1 && py < y2) || // upward │ │ │ │ │ + y1 > y2 && (py < y1 && py >= y2)) { // downward │ │ │ │ │ + ++crosses; │ │ │ │ │ + } │ │ │ │ │ } │ │ │ │ │ - } else { │ │ │ │ │ - // clone symbolizer │ │ │ │ │ - options.symbolizer = {}; │ │ │ │ │ - var value, type; │ │ │ │ │ - for (var key in this.symbolizer) { │ │ │ │ │ - value = this.symbolizer[key]; │ │ │ │ │ - type = typeof value; │ │ │ │ │ - if (type === "object") { │ │ │ │ │ - options.symbolizer[key] = OpenLayers.Util.extend({}, value); │ │ │ │ │ - } else if (type === "string") { │ │ │ │ │ - options.symbolizer[key] = value; │ │ │ │ │ + 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 < len; ++i) { │ │ │ │ │ + intersect = geometry.components[i].intersects(this); │ │ │ │ │ + if (intersect) { │ │ │ │ │ + break; │ │ │ │ │ + } │ │ │ │ │ } │ │ │ │ │ } │ │ │ │ │ - } │ │ │ │ │ - // clone filter │ │ │ │ │ - options.filter = this.filter && this.filter.clone(); │ │ │ │ │ - // clone context │ │ │ │ │ - options.context = this.context && OpenLayers.Util.extend({}, this.context); │ │ │ │ │ - return new OpenLayers.Rule(options); │ │ │ │ │ - }, │ │ │ │ │ + return intersect; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Rule" │ │ │ │ │ -}); │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: getVertices │ │ │ │ │ + * Return a list of all points in this geometry. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * nodes - {Boolean} For lines, only return vertices that are │ │ │ │ │ + * endpoints. If false, for lines, only vertices that are not │ │ │ │ │ + * endpoints will be returned. If not provided, all vertices will │ │ │ │ │ + * be returned. │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Array} A list of all vertices in the geometry. │ │ │ │ │ + */ │ │ │ │ │ + getVertices: function(nodes) { │ │ │ │ │ + return (nodes === true) ? [] : this.components.slice(0, this.components.length - 1); │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + CLASS_NAME: "OpenLayers.Geometry.LinearRing" │ │ │ │ │ + }); │ │ │ │ │ /* ====================================================================== │ │ │ │ │ - OpenLayers/Tween.js │ │ │ │ │ + OpenLayers/Geometry/Polygon.js │ │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ │ - * @requires OpenLayers/Animation.js │ │ │ │ │ + * @requires OpenLayers/Geometry/Collection.js │ │ │ │ │ + * @requires OpenLayers/Geometry/LinearRing.js │ │ │ │ │ */ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Namespace: OpenLayers.Tween │ │ │ │ │ + * Class: OpenLayers.Geometry.Polygon │ │ │ │ │ + * Polygon is a collection of Geometry.LinearRings. │ │ │ │ │ + * │ │ │ │ │ + * Inherits from: │ │ │ │ │ + * - │ │ │ │ │ + * - │ │ │ │ │ */ │ │ │ │ │ -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, │ │ │ │ │ +OpenLayers.Geometry.Polygon = OpenLayers.Class( │ │ │ │ │ + OpenLayers.Geometry.Collection, { │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIProperty: duration │ │ │ │ │ - * {int} duration of the tween (number of steps) │ │ │ │ │ - */ │ │ │ │ │ - duration: 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: ["OpenLayers.Geometry.LinearRing"], │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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, │ │ │ │ │ + /** │ │ │ │ │ + * 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()} │ │ │ │ │ + */ │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: time │ │ │ │ │ - * {int} Step counter │ │ │ │ │ - */ │ │ │ │ │ - time: null, │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: getArea │ │ │ │ │ + * Calculated by subtracting the areas of the internal holes from the │ │ │ │ │ + * area of the outer hole. │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {float} The area of the geometry │ │ │ │ │ + */ │ │ │ │ │ + getArea: function() { │ │ │ │ │ + var area = 0.0; │ │ │ │ │ + if (this.components && (this.components.length > 0)) { │ │ │ │ │ + area += Math.abs(this.components[0].getArea()); │ │ │ │ │ + for (var i = 1, len = this.components.length; i < len; i++) { │ │ │ │ │ + area -= Math.abs(this.components[i].getArea()); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + return area; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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, │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: getGeodesicArea │ │ │ │ │ + * Calculate the approximate area of the polygon were it projected onto │ │ │ │ │ + * the earth. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * projection - {} The spatial reference system │ │ │ │ │ + * for the geometry coordinates. If not provided, Geographic/WGS84 is │ │ │ │ │ + * assumed. │ │ │ │ │ + * │ │ │ │ │ + * Reference: │ │ │ │ │ + * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for │ │ │ │ │ + * Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion │ │ │ │ │ + * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409 │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {float} The approximate geodesic area of the polygon in square meters. │ │ │ │ │ + */ │ │ │ │ │ + getGeodesicArea: function(projection) { │ │ │ │ │ + var area = 0.0; │ │ │ │ │ + if (this.components && (this.components.length > 0)) { │ │ │ │ │ + area += Math.abs(this.components[0].getGeodesicArea(projection)); │ │ │ │ │ + for (var i = 1, len = this.components.length; i < len; i++) { │ │ │ │ │ + area -= Math.abs(this.components[i].getGeodesicArea(projection)); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + return area; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: startTime │ │ │ │ │ - * {Number} The timestamp of the first execution step. Used for skipping │ │ │ │ │ - * frames │ │ │ │ │ - */ │ │ │ │ │ - startTime: null, │ │ │ │ │ + /** │ │ │ │ │ + * Method: containsPoint │ │ │ │ │ + * Test if a point is inside a polygon. Points on a polygon edge are │ │ │ │ │ + * considered inside. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * point - {} │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Boolean | Number} The point is inside the polygon. Returns 1 if the │ │ │ │ │ + * point is on an edge. Returns boolean otherwise. │ │ │ │ │ + */ │ │ │ │ │ + containsPoint: function(point) { │ │ │ │ │ + var numRings = this.components.length; │ │ │ │ │ + var contained = false; │ │ │ │ │ + if (numRings > 0) { │ │ │ │ │ + // check exterior ring - 1 means on edge, boolean otherwise │ │ │ │ │ + contained = this.components[0].containsPoint(point); │ │ │ │ │ + if (contained !== 1) { │ │ │ │ │ + if (contained && numRings > 1) { │ │ │ │ │ + // check interior rings │ │ │ │ │ + var hole; │ │ │ │ │ + for (var i = 1; i < numRings; ++i) { │ │ │ │ │ + hole = this.components[i].containsPoint(point); │ │ │ │ │ + if (hole) { │ │ │ │ │ + if (hole === 1) { │ │ │ │ │ + // on edge │ │ │ │ │ + contained = 1; │ │ │ │ │ + } else { │ │ │ │ │ + // in hole │ │ │ │ │ + contained = false; │ │ │ │ │ + } │ │ │ │ │ + break; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + return contained; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: animationId │ │ │ │ │ - * {int} Loop id returned by OpenLayers.Animation.start │ │ │ │ │ - */ │ │ │ │ │ - animationId: null, │ │ │ │ │ + /** │ │ │ │ │ + * 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; │ │ │ │ │ + var i, len; │ │ │ │ │ + if (geometry.CLASS_NAME == "OpenLayers.Geometry.Point") { │ │ │ │ │ + intersect = this.containsPoint(geometry); │ │ │ │ │ + } else if (geometry.CLASS_NAME == "OpenLayers.Geometry.LineString" || │ │ │ │ │ + geometry.CLASS_NAME == "OpenLayers.Geometry.LinearRing") { │ │ │ │ │ + // check if rings/linestrings intersect │ │ │ │ │ + for (i = 0, len = this.components.length; i < len; ++i) { │ │ │ │ │ + intersect = geometry.intersects(this.components[i]); │ │ │ │ │ + if (intersect) { │ │ │ │ │ + break; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + if (!intersect) { │ │ │ │ │ + // check if this poly contains points of the ring/linestring │ │ │ │ │ + for (i = 0, len = geometry.components.length; i < len; ++i) { │ │ │ │ │ + intersect = this.containsPoint(geometry.components[i]); │ │ │ │ │ + if (intersect) { │ │ │ │ │ + break; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } else { │ │ │ │ │ + for (i = 0, len = geometry.components.length; i < len; ++i) { │ │ │ │ │ + intersect = this.intersects(geometry.components[i]); │ │ │ │ │ + if (intersect) { │ │ │ │ │ + break; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + // check case where this poly is wholly contained by another │ │ │ │ │ + if (!intersect && geometry.CLASS_NAME == "OpenLayers.Geometry.Polygon") { │ │ │ │ │ + // exterior ring points will be contained in the other geometry │ │ │ │ │ + var ring = this.components[0]; │ │ │ │ │ + for (i = 0, len = ring.components.length; i < len; ++i) { │ │ │ │ │ + intersect = geometry.containsPoint(ring.components[i]); │ │ │ │ │ + if (intersect) { │ │ │ │ │ + break; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + return intersect; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: playing │ │ │ │ │ - * {Boolean} Tells if the easing is currently playing │ │ │ │ │ - */ │ │ │ │ │ - playing: false, │ │ │ │ │ + /** │ │ │ │ │ + * 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 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; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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; │ │ │ │ │ - }, │ │ │ │ │ + CLASS_NAME: "OpenLayers.Geometry.Polygon" │ │ │ │ │ + }); │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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: 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 < sides; ++i) { │ │ │ │ │ + rotatedAngle = angle + (i * 2 * Math.PI / sides); │ │ │ │ │ + x = origin.x + (radius * Math.cos(rotatedAngle)); │ │ │ │ │ + y = origin.y + (radius * Math.sin(rotatedAngle)); │ │ │ │ │ + points.push(new OpenLayers.Geometry.Point(x, y)); │ │ │ │ │ + } │ │ │ │ │ + var ring = new OpenLayers.Geometry.LinearRing(points); │ │ │ │ │ + return new OpenLayers.Geometry.Polygon([ring]); │ │ │ │ │ +}; │ │ │ │ │ +/* ====================================================================== │ │ │ │ │ + OpenLayers/Geometry/MultiPolygon.js │ │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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; │ │ │ │ │ - } │ │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ + * full text of the license. */ │ │ │ │ │ │ │ │ │ │ - if (this.callbacks && this.callbacks.done) { │ │ │ │ │ - this.callbacks.done.call(this, this.finish); │ │ │ │ │ - } │ │ │ │ │ - OpenLayers.Animation.stop(this.animationId); │ │ │ │ │ - this.animationId = null; │ │ │ │ │ - this.playing = false; │ │ │ │ │ - }, │ │ │ │ │ +/** │ │ │ │ │ + * @requires OpenLayers/Geometry/Collection.js │ │ │ │ │ + * @requires OpenLayers/Geometry/Polygon.js │ │ │ │ │ + */ │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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'); │ │ │ │ │ - } │ │ │ │ │ +/** │ │ │ │ │ + * Class: OpenLayers.Geometry.MultiPolygon │ │ │ │ │ + * MultiPolygon is a geometry with multiple │ │ │ │ │ + * components. Create a new instance with the │ │ │ │ │ + * constructor. │ │ │ │ │ + * │ │ │ │ │ + * Inherits from: │ │ │ │ │ + * - │ │ │ │ │ + */ │ │ │ │ │ +OpenLayers.Geometry.MultiPolygon = OpenLayers.Class( │ │ │ │ │ + OpenLayers.Geometry.Collection, { │ │ │ │ │ │ │ │ │ │ - var c = f - b; │ │ │ │ │ - value[i] = this.easing.apply(this, [this.time, b, c, this.duration]); │ │ │ │ │ - } │ │ │ │ │ - this.time++; │ │ │ │ │ + /** │ │ │ │ │ + * 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"], │ │ │ │ │ │ │ │ │ │ - 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); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ + /** │ │ │ │ │ + * Constructor: OpenLayers.Geometry.MultiPolygon │ │ │ │ │ + * Create a new MultiPolygon geometry │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * components - {Array()} An array of polygons │ │ │ │ │ + * used to generate the MultiPolygon │ │ │ │ │ + * │ │ │ │ │ + */ │ │ │ │ │ │ │ │ │ │ - if (this.time > this.duration) { │ │ │ │ │ - this.stop(); │ │ │ │ │ - } │ │ │ │ │ - }, │ │ │ │ │ + CLASS_NAME: "OpenLayers.Geometry.MultiPolygon" │ │ │ │ │ + }); │ │ │ │ │ +/* ====================================================================== │ │ │ │ │ + OpenLayers/Format/WKT.js │ │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Create empty functions for all easing methods. │ │ │ │ │ - */ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Tween" │ │ │ │ │ -}); │ │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ + * full text of the license. */ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Namespace: OpenLayers.Easing │ │ │ │ │ - * │ │ │ │ │ - * Credits: │ │ │ │ │ - * Easing Equations by Robert Penner, │ │ │ │ │ + * @requires OpenLayers/Format.js │ │ │ │ │ + * @requires OpenLayers/Feature/Vector.js │ │ │ │ │ + * @requires OpenLayers/Geometry/Point.js │ │ │ │ │ + * @requires OpenLayers/Geometry/MultiPoint.js │ │ │ │ │ + * @requires OpenLayers/Geometry/LineString.js │ │ │ │ │ + * @requires OpenLayers/Geometry/MultiLineString.js │ │ │ │ │ + * @requires OpenLayers/Geometry/Polygon.js │ │ │ │ │ + * @requires OpenLayers/Geometry/MultiPolygon.js │ │ │ │ │ */ │ │ │ │ │ -OpenLayers.Easing = { │ │ │ │ │ - /** │ │ │ │ │ - * Create empty functions for all easing methods. │ │ │ │ │ - */ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Easing" │ │ │ │ │ -}; │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Namespace: OpenLayers.Easing.Linear │ │ │ │ │ + * Class: OpenLayers.Format.WKT │ │ │ │ │ + * Class for reading and writing Well-Known Text. Create a new instance │ │ │ │ │ + * with the constructor. │ │ │ │ │ + * │ │ │ │ │ + * Inherits from: │ │ │ │ │ + * - │ │ │ │ │ */ │ │ │ │ │ -OpenLayers.Easing.Linear = { │ │ │ │ │ +OpenLayers.Format.WKT = OpenLayers.Class(OpenLayers.Format, { │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Function: easeIn │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * t - {Float} time │ │ │ │ │ - * b - {Float} beginning position │ │ │ │ │ - * c - {Float} total change │ │ │ │ │ - * d - {Float} duration of the transition │ │ │ │ │ + * Constructor: OpenLayers.Format.WKT │ │ │ │ │ + * Create a new parser for WKT │ │ │ │ │ * │ │ │ │ │ - * 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 │ │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ │ + * this instance │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {Float} │ │ │ │ │ + * {} A new WKT parser. │ │ │ │ │ */ │ │ │ │ │ - easeOut: function(t, b, c, d) { │ │ │ │ │ - return c * t / d + b; │ │ │ │ │ + initialize: function(options) { │ │ │ │ │ + this.regExes = { │ │ │ │ │ + 'typeStr': /^\s*(\w+)\s*\(\s*(.*)\s*\)\s*$/, │ │ │ │ │ + 'spaces': /\s+/, │ │ │ │ │ + 'parenComma': /\)\s*,\s*\(/, │ │ │ │ │ + 'doubleParenComma': /\)\s*\)\s*,\s*\(\s*\(/, // can't use {2} here │ │ │ │ │ + 'trimParens': /^\s*\(?(.*?)\)?\s*$/ │ │ │ │ │ + }; │ │ │ │ │ + OpenLayers.Format.prototype.initialize.apply(this, [options]); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Function: easeInOut │ │ │ │ │ - * │ │ │ │ │ + * APIMethod: read │ │ │ │ │ + * Deserialize a WKT string and return a vector feature or an │ │ │ │ │ + * array of vector features. Supports WKT for POINT, MULTIPOINT, │ │ │ │ │ + * LINESTRING, MULTILINESTRING, POLYGON, MULTIPOLYGON, and │ │ │ │ │ + * GEOMETRYCOLLECTION. │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * t - {Float} time │ │ │ │ │ - * b - {Float} beginning position │ │ │ │ │ - * c - {Float} total change │ │ │ │ │ - * d - {Float} duration of the transition │ │ │ │ │ + * wkt - {String} A WKT string │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {Float} │ │ │ │ │ + * {|Array} A feature or array of features for │ │ │ │ │ + * GEOMETRYCOLLECTION WKT. │ │ │ │ │ */ │ │ │ │ │ - easeInOut: function(t, b, c, d) { │ │ │ │ │ - return c * t / d + b; │ │ │ │ │ + read: function(wkt) { │ │ │ │ │ + var features, type, str; │ │ │ │ │ + wkt = wkt.replace(/[\n\r]/g, " "); │ │ │ │ │ + var matches = this.regExes.typeStr.exec(wkt); │ │ │ │ │ + if (matches) { │ │ │ │ │ + type = matches[1].toLowerCase(); │ │ │ │ │ + str = matches[2]; │ │ │ │ │ + if (this.parse[type]) { │ │ │ │ │ + features = this.parse[type].apply(this, [str]); │ │ │ │ │ + } │ │ │ │ │ + if (this.internalProjection && this.externalProjection) { │ │ │ │ │ + if (features && │ │ │ │ │ + features.CLASS_NAME == "OpenLayers.Feature.Vector") { │ │ │ │ │ + features.geometry.transform(this.externalProjection, │ │ │ │ │ + this.internalProjection); │ │ │ │ │ + } else if (features && │ │ │ │ │ + type != "geometrycollection" && │ │ │ │ │ + typeof features == "object") { │ │ │ │ │ + for (var i = 0, len = features.length; i < len; i++) { │ │ │ │ │ + var component = features[i]; │ │ │ │ │ + component.geometry.transform(this.externalProjection, │ │ │ │ │ + this.internalProjection); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + return features; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Easing.Linear" │ │ │ │ │ -}; │ │ │ │ │ - │ │ │ │ │ -/** │ │ │ │ │ - * Namespace: OpenLayers.Easing.Expo │ │ │ │ │ - */ │ │ │ │ │ -OpenLayers.Easing.Expo = { │ │ │ │ │ - │ │ │ │ │ /** │ │ │ │ │ - * Function: easeIn │ │ │ │ │ - * │ │ │ │ │ + * APIMethod: write │ │ │ │ │ + * Serialize a feature or array of features into a WKT string. │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * t - {Float} time │ │ │ │ │ - * b - {Float} beginning position │ │ │ │ │ - * c - {Float} total change │ │ │ │ │ - * d - {Float} duration of the transition │ │ │ │ │ + * features - {|Array} A feature or array of │ │ │ │ │ + * features │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {Float} │ │ │ │ │ + * {String} The WKT string representation of the input geometries │ │ │ │ │ */ │ │ │ │ │ - easeIn: function(t, b, c, d) { │ │ │ │ │ - return (t == 0) ? b : c * Math.pow(2, 10 * (t / d - 1)) + b; │ │ │ │ │ + write: function(features) { │ │ │ │ │ + var collection, geometry, isCollection; │ │ │ │ │ + if (features.constructor == Array) { │ │ │ │ │ + collection = features; │ │ │ │ │ + isCollection = true; │ │ │ │ │ + } else { │ │ │ │ │ + collection = [features]; │ │ │ │ │ + isCollection = false; │ │ │ │ │ + } │ │ │ │ │ + var pieces = []; │ │ │ │ │ + if (isCollection) { │ │ │ │ │ + pieces.push('GEOMETRYCOLLECTION('); │ │ │ │ │ + } │ │ │ │ │ + for (var i = 0, len = collection.length; i < len; ++i) { │ │ │ │ │ + if (isCollection && i > 0) { │ │ │ │ │ + pieces.push(','); │ │ │ │ │ + } │ │ │ │ │ + geometry = collection[i].geometry; │ │ │ │ │ + pieces.push(this.extractGeometry(geometry)); │ │ │ │ │ + } │ │ │ │ │ + if (isCollection) { │ │ │ │ │ + pieces.push(')'); │ │ │ │ │ + } │ │ │ │ │ + return pieces.join(''); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Function: easeOut │ │ │ │ │ - * │ │ │ │ │ + * Method: extractGeometry │ │ │ │ │ + * Entry point to construct the WKT for a single Geometry object. │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * t - {Float} time │ │ │ │ │ - * b - {Float} beginning position │ │ │ │ │ - * c - {Float} total change │ │ │ │ │ - * d - {Float} duration of the transition │ │ │ │ │ + * geometry - {} │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {Float} │ │ │ │ │ + * {String} A WKT string of representing the geometry │ │ │ │ │ */ │ │ │ │ │ - easeOut: function(t, b, c, d) { │ │ │ │ │ - return (t == d) ? b + c : c * (-Math.pow(2, -10 * t / d) + 1) + b; │ │ │ │ │ + extractGeometry: function(geometry) { │ │ │ │ │ + var type = geometry.CLASS_NAME.split('.')[2].toLowerCase(); │ │ │ │ │ + if (!this.extract[type]) { │ │ │ │ │ + return null; │ │ │ │ │ + } │ │ │ │ │ + if (this.internalProjection && this.externalProjection) { │ │ │ │ │ + geometry = geometry.clone(); │ │ │ │ │ + geometry.transform(this.internalProjection, this.externalProjection); │ │ │ │ │ + } │ │ │ │ │ + var wktType = type == 'collection' ? 'GEOMETRYCOLLECTION' : type.toUpperCase(); │ │ │ │ │ + var data = wktType + '(' + this.extract[type].apply(this, [geometry]) + ')'; │ │ │ │ │ + return data; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Function: easeInOut │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * t - {Float} time │ │ │ │ │ - * b - {Float} beginning position │ │ │ │ │ - * c - {Float} total change │ │ │ │ │ - * d - {Float} duration of the transition │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Float} │ │ │ │ │ + * Object with properties corresponding to the geometry types. │ │ │ │ │ + * Property values are functions that do the actual data extraction. │ │ │ │ │ */ │ │ │ │ │ - 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; │ │ │ │ │ - }, │ │ │ │ │ + extract: { │ │ │ │ │ + /** │ │ │ │ │ + * Return a space delimited string of point coordinates. │ │ │ │ │ + * @param {OpenLayers.Geometry.Point} point │ │ │ │ │ + * @returns {String} A string of coordinates representing the point │ │ │ │ │ + */ │ │ │ │ │ + 'point': function(point) { │ │ │ │ │ + return point.x + ' ' + point.y; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Easing.Expo" │ │ │ │ │ -}; │ │ │ │ │ + /** │ │ │ │ │ + * Return a comma delimited string of point coordinates from a multipoint. │ │ │ │ │ + * @param {OpenLayers.Geometry.MultiPoint} multipoint │ │ │ │ │ + * @returns {String} A string of point coordinate strings representing │ │ │ │ │ + * the multipoint │ │ │ │ │ + */ │ │ │ │ │ + 'multipoint': function(multipoint) { │ │ │ │ │ + var array = []; │ │ │ │ │ + for (var i = 0, len = multipoint.components.length; i < len; ++i) { │ │ │ │ │ + array.push('(' + │ │ │ │ │ + this.extract.point.apply(this, [multipoint.components[i]]) + │ │ │ │ │ + ')'); │ │ │ │ │ + } │ │ │ │ │ + return array.join(','); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * Namespace: OpenLayers.Easing.Quad │ │ │ │ │ - */ │ │ │ │ │ -OpenLayers.Easing.Quad = { │ │ │ │ │ + /** │ │ │ │ │ + * Return a comma delimited string of point coordinates from a line. │ │ │ │ │ + * @param {OpenLayers.Geometry.LineString} linestring │ │ │ │ │ + * @returns {String} A string of point coordinate strings representing │ │ │ │ │ + * the linestring │ │ │ │ │ + */ │ │ │ │ │ + 'linestring': function(linestring) { │ │ │ │ │ + var array = []; │ │ │ │ │ + for (var i = 0, len = linestring.components.length; i < len; ++i) { │ │ │ │ │ + array.push(this.extract.point.apply(this, [linestring.components[i]])); │ │ │ │ │ + } │ │ │ │ │ + return array.join(','); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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; │ │ │ │ │ - }, │ │ │ │ │ + /** │ │ │ │ │ + * Return a comma delimited string of linestring strings from a multilinestring. │ │ │ │ │ + * @param {OpenLayers.Geometry.MultiLineString} multilinestring │ │ │ │ │ + * @returns {String} A string of of linestring strings representing │ │ │ │ │ + * the multilinestring │ │ │ │ │ + */ │ │ │ │ │ + 'multilinestring': function(multilinestring) { │ │ │ │ │ + var array = []; │ │ │ │ │ + for (var i = 0, len = multilinestring.components.length; i < len; ++i) { │ │ │ │ │ + array.push('(' + │ │ │ │ │ + this.extract.linestring.apply(this, [multilinestring.components[i]]) + │ │ │ │ │ + ')'); │ │ │ │ │ + } │ │ │ │ │ + return array.join(','); │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Return a comma delimited string of linear ring arrays from a polygon. │ │ │ │ │ + * @param {OpenLayers.Geometry.Polygon} polygon │ │ │ │ │ + * @returns {String} An array of linear ring arrays representing the polygon │ │ │ │ │ + */ │ │ │ │ │ + 'polygon': function(polygon) { │ │ │ │ │ + var array = []; │ │ │ │ │ + for (var i = 0, len = polygon.components.length; i < len; ++i) { │ │ │ │ │ + array.push('(' + │ │ │ │ │ + this.extract.linestring.apply(this, [polygon.components[i]]) + │ │ │ │ │ + ')'); │ │ │ │ │ + } │ │ │ │ │ + return array.join(','); │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Return an array of polygon arrays from a multipolygon. │ │ │ │ │ + * @param {OpenLayers.Geometry.MultiPolygon} multipolygon │ │ │ │ │ + * @returns {String} An array of polygon arrays representing │ │ │ │ │ + * the multipolygon │ │ │ │ │ + */ │ │ │ │ │ + 'multipolygon': function(multipolygon) { │ │ │ │ │ + var array = []; │ │ │ │ │ + for (var i = 0, len = multipolygon.components.length; i < len; ++i) { │ │ │ │ │ + array.push('(' + │ │ │ │ │ + this.extract.polygon.apply(this, [multipolygon.components[i]]) + │ │ │ │ │ + ')'); │ │ │ │ │ + } │ │ │ │ │ + return array.join(','); │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Return the WKT portion between 'GEOMETRYCOLLECTION(' and ')' for an │ │ │ │ │ + * @param {OpenLayers.Geometry.Collection} collection │ │ │ │ │ + * @returns {String} internal WKT representation of the collection │ │ │ │ │ + */ │ │ │ │ │ + 'collection': function(collection) { │ │ │ │ │ + var array = []; │ │ │ │ │ + for (var i = 0, len = collection.components.length; i < len; ++i) { │ │ │ │ │ + array.push(this.extractGeometry.apply(this, [collection.components[i]])); │ │ │ │ │ + } │ │ │ │ │ + return array.join(','); │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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} │ │ │ │ │ + * Object with properties corresponding to the geometry types. │ │ │ │ │ + * Property values are functions that do the actual parsing. │ │ │ │ │ */ │ │ │ │ │ - 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; │ │ │ │ │ + parse: { │ │ │ │ │ + /** │ │ │ │ │ + * Return point feature given a point WKT fragment. │ │ │ │ │ + * @param {String} str A WKT fragment representing the point │ │ │ │ │ + * @returns {OpenLayers.Feature.Vector} A point feature │ │ │ │ │ + * @private │ │ │ │ │ + */ │ │ │ │ │ + 'point': function(str) { │ │ │ │ │ + var coords = OpenLayers.String.trim(str).split(this.regExes.spaces); │ │ │ │ │ + return new OpenLayers.Feature.Vector( │ │ │ │ │ + new OpenLayers.Geometry.Point(coords[0], coords[1]) │ │ │ │ │ + ); │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Return a multipoint feature given a multipoint WKT fragment. │ │ │ │ │ + * @param {String} str A WKT fragment representing the multipoint │ │ │ │ │ + * @returns {OpenLayers.Feature.Vector} A multipoint feature │ │ │ │ │ + * @private │ │ │ │ │ + */ │ │ │ │ │ + 'multipoint': function(str) { │ │ │ │ │ + var point; │ │ │ │ │ + var points = OpenLayers.String.trim(str).split(','); │ │ │ │ │ + var components = []; │ │ │ │ │ + for (var i = 0, len = points.length; i < len; ++i) { │ │ │ │ │ + point = points[i].replace(this.regExes.trimParens, '$1'); │ │ │ │ │ + components.push(this.parse.point.apply(this, [point]).geometry); │ │ │ │ │ + } │ │ │ │ │ + return new OpenLayers.Feature.Vector( │ │ │ │ │ + new OpenLayers.Geometry.MultiPoint(components) │ │ │ │ │ + ); │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Return a linestring feature given a linestring WKT fragment. │ │ │ │ │ + * @param {String} str A WKT fragment representing the linestring │ │ │ │ │ + * @returns {OpenLayers.Feature.Vector} A linestring feature │ │ │ │ │ + * @private │ │ │ │ │ + */ │ │ │ │ │ + 'linestring': function(str) { │ │ │ │ │ + var points = OpenLayers.String.trim(str).split(','); │ │ │ │ │ + var components = []; │ │ │ │ │ + for (var i = 0, len = points.length; i < len; ++i) { │ │ │ │ │ + components.push(this.parse.point.apply(this, [points[i]]).geometry); │ │ │ │ │ + } │ │ │ │ │ + return new OpenLayers.Feature.Vector( │ │ │ │ │ + new OpenLayers.Geometry.LineString(components) │ │ │ │ │ + ); │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Return a multilinestring feature given a multilinestring WKT fragment. │ │ │ │ │ + * @param {String} str A WKT fragment representing the multilinestring │ │ │ │ │ + * @returns {OpenLayers.Feature.Vector} A multilinestring feature │ │ │ │ │ + * @private │ │ │ │ │ + */ │ │ │ │ │ + 'multilinestring': function(str) { │ │ │ │ │ + var line; │ │ │ │ │ + var lines = OpenLayers.String.trim(str).split(this.regExes.parenComma); │ │ │ │ │ + var components = []; │ │ │ │ │ + for (var i = 0, len = lines.length; i < len; ++i) { │ │ │ │ │ + line = lines[i].replace(this.regExes.trimParens, '$1'); │ │ │ │ │ + components.push(this.parse.linestring.apply(this, [line]).geometry); │ │ │ │ │ + } │ │ │ │ │ + return new OpenLayers.Feature.Vector( │ │ │ │ │ + new OpenLayers.Geometry.MultiLineString(components) │ │ │ │ │ + ); │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Return a polygon feature given a polygon WKT fragment. │ │ │ │ │ + * @param {String} str A WKT fragment representing the polygon │ │ │ │ │ + * @returns {OpenLayers.Feature.Vector} A polygon feature │ │ │ │ │ + * @private │ │ │ │ │ + */ │ │ │ │ │ + 'polygon': function(str) { │ │ │ │ │ + var ring, linestring, linearring; │ │ │ │ │ + var rings = OpenLayers.String.trim(str).split(this.regExes.parenComma); │ │ │ │ │ + var components = []; │ │ │ │ │ + for (var i = 0, len = rings.length; i < len; ++i) { │ │ │ │ │ + ring = rings[i].replace(this.regExes.trimParens, '$1'); │ │ │ │ │ + linestring = this.parse.linestring.apply(this, [ring]).geometry; │ │ │ │ │ + linearring = new OpenLayers.Geometry.LinearRing(linestring.components); │ │ │ │ │ + components.push(linearring); │ │ │ │ │ + } │ │ │ │ │ + return new OpenLayers.Feature.Vector( │ │ │ │ │ + new OpenLayers.Geometry.Polygon(components) │ │ │ │ │ + ); │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Return a multipolygon feature given a multipolygon WKT fragment. │ │ │ │ │ + * @param {String} str A WKT fragment representing the multipolygon │ │ │ │ │ + * @returns {OpenLayers.Feature.Vector} A multipolygon feature │ │ │ │ │ + * @private │ │ │ │ │ + */ │ │ │ │ │ + 'multipolygon': function(str) { │ │ │ │ │ + var polygon; │ │ │ │ │ + var polygons = OpenLayers.String.trim(str).split(this.regExes.doubleParenComma); │ │ │ │ │ + var components = []; │ │ │ │ │ + for (var i = 0, len = polygons.length; i < len; ++i) { │ │ │ │ │ + polygon = polygons[i].replace(this.regExes.trimParens, '$1'); │ │ │ │ │ + components.push(this.parse.polygon.apply(this, [polygon]).geometry); │ │ │ │ │ + } │ │ │ │ │ + return new OpenLayers.Feature.Vector( │ │ │ │ │ + new OpenLayers.Geometry.MultiPolygon(components) │ │ │ │ │ + ); │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Return an array of features given a geometrycollection WKT fragment. │ │ │ │ │ + * @param {String} str A WKT fragment representing the geometrycollection │ │ │ │ │ + * @returns {Array} An array of OpenLayers.Feature.Vector │ │ │ │ │ + * @private │ │ │ │ │ + */ │ │ │ │ │ + 'geometrycollection': function(str) { │ │ │ │ │ + // separate components of the collection with | │ │ │ │ │ + str = str.replace(/,\s*([A-Za-z])/g, '|$1'); │ │ │ │ │ + var wktArray = OpenLayers.String.trim(str).split('|'); │ │ │ │ │ + var components = []; │ │ │ │ │ + for (var i = 0, len = wktArray.length; i < len; ++i) { │ │ │ │ │ + components.push(OpenLayers.Format.WKT.prototype.read.apply(this, [wktArray[i]])); │ │ │ │ │ + } │ │ │ │ │ + return components; │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Easing.Quad" │ │ │ │ │ -}; │ │ │ │ │ + CLASS_NAME: "OpenLayers.Format.WKT" │ │ │ │ │ +}); │ │ │ │ │ /* ====================================================================== │ │ │ │ │ - OpenLayers/Projection.js │ │ │ │ │ + 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. */ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ │ - * @requires OpenLayers/Util.js │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * @requires OpenLayers/Format.js │ │ │ │ │ + */ │ │ │ │ │ + │ │ │ │ │ +/** │ │ │ │ │ + * Class: OpenLayers.Format.JSON │ │ │ │ │ + * A parser to read/write JSON safely. Create a new instance with the │ │ │ │ │ + * constructor. │ │ │ │ │ * │ │ │ │ │ - * 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. │ │ │ │ │ + * Inherits from: │ │ │ │ │ + * - │ │ │ │ │ */ │ │ │ │ │ -OpenLayers.Projection = OpenLayers.Class({ │ │ │ │ │ +OpenLayers.Format.JSON = OpenLayers.Class(OpenLayers.Format, { │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: proj │ │ │ │ │ - * {Object} Proj4js.Proj instance. │ │ │ │ │ + * APIProperty: indent │ │ │ │ │ + * {String} For "pretty" printing, the indent string will be used once for │ │ │ │ │ + * each indentation level. │ │ │ │ │ */ │ │ │ │ │ - proj: null, │ │ │ │ │ + indent: " ", │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: projCode │ │ │ │ │ - * {String} │ │ │ │ │ + * APIProperty: space │ │ │ │ │ + * {String} For "pretty" printing, the space string will be used after │ │ │ │ │ + * the ":" separating a name/value pair. │ │ │ │ │ */ │ │ │ │ │ - projCode: null, │ │ │ │ │ + space: " ", │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: titleRegEx │ │ │ │ │ - * {RegExp} regular expression to strip the title from a proj4js definition │ │ │ │ │ + * APIProperty: newline │ │ │ │ │ + * {String} For "pretty" printing, the newline string will be used at the │ │ │ │ │ + * end of each name/value pair or array item. │ │ │ │ │ */ │ │ │ │ │ - titleRegEx: /\+title=[^\+]*/, │ │ │ │ │ + newline: "\n", │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Constructor: OpenLayers.Projection │ │ │ │ │ - * This class offers several methods for interacting with a wrapped │ │ │ │ │ - * pro4js projection object. │ │ │ │ │ + * 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: │ │ │ │ │ - * projCode - {String} A string identifying the Well Known Identifier for │ │ │ │ │ - * the projection. │ │ │ │ │ - * options - {Object} An optional object to set additional properties │ │ │ │ │ - * on the projection. │ │ │ │ │ + * 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: │ │ │ │ │ - * {} A projection object. │ │ │ │ │ + * {Object} An object, array, string, or number . │ │ │ │ │ */ │ │ │ │ │ - initialize: function(projCode, options) { │ │ │ │ │ - OpenLayers.Util.extend(this, options); │ │ │ │ │ - this.projCode = projCode; │ │ │ │ │ - if (typeof Proj4js == "object") { │ │ │ │ │ - this.proj = new Proj4js.Proj(projCode); │ │ │ │ │ + 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: getCode │ │ │ │ │ - * Get the string SRS code. │ │ │ │ │ + * 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 SRS code. │ │ │ │ │ + * {String} The JSON string representation of the input value. │ │ │ │ │ */ │ │ │ │ │ - getCode: function() { │ │ │ │ │ - return this.proj ? this.proj.srsCode : this.projCode; │ │ │ │ │ + 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; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: getUnits │ │ │ │ │ - * Get the units string for the projection -- returns null if │ │ │ │ │ - * proj4js is not available. │ │ │ │ │ + * Method: writeIndent │ │ │ │ │ + * Output an indentation string depending on the indentation level. │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {String} The units abbreviation. │ │ │ │ │ + * {String} An appropriate indentation string. │ │ │ │ │ */ │ │ │ │ │ - getUnits: function() { │ │ │ │ │ - return this.proj ? this.proj.units : null; │ │ │ │ │ + writeIndent: function() { │ │ │ │ │ + var pieces = []; │ │ │ │ │ + if (this.pretty) { │ │ │ │ │ + for (var i = 0; i < this.level; ++i) { │ │ │ │ │ + pieces.push(this.indent); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + return pieces.join(''); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: toString │ │ │ │ │ - * Convert projection to string (getCode wrapper). │ │ │ │ │ + * Method: writeNewline │ │ │ │ │ + * Output a string representing a newline if in pretty printing mode. │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {String} The projection code. │ │ │ │ │ + * {String} A string representing a new line. │ │ │ │ │ */ │ │ │ │ │ - toString: function() { │ │ │ │ │ - return this.getCode(); │ │ │ │ │ + writeNewline: function() { │ │ │ │ │ + return (this.pretty) ? this.newline : ''; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: equals │ │ │ │ │ - * Test equality of two projection instances. Determines equality based │ │ │ │ │ - * soley on the projection code. │ │ │ │ │ + * Method: writeSpace │ │ │ │ │ + * Output a string representing a space if in pretty printing mode. │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {Boolean} The two projections are equivalent. │ │ │ │ │ + * {String} A space. │ │ │ │ │ */ │ │ │ │ │ - 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; │ │ │ │ │ + writeSpace: function() { │ │ │ │ │ + return (this.pretty) ? this.space : ''; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /* Method: destroy │ │ │ │ │ - * Destroy projection object. │ │ │ │ │ + /** │ │ │ │ │ + * Property: serialize │ │ │ │ │ + * Object with properties corresponding to the serializable data types. │ │ │ │ │ + * Property values are functions that do the actual serializing. │ │ │ │ │ */ │ │ │ │ │ - destroy: function() { │ │ │ │ │ - delete this.proj; │ │ │ │ │ - delete this.projCode; │ │ │ │ │ - }, │ │ │ │ │ - │ │ │ │ │ - CLASS_NAME: "OpenLayers.Projection" │ │ │ │ │ -}); │ │ │ │ │ + serialize: { │ │ │ │ │ + /** │ │ │ │ │ + * Method: serialize.object │ │ │ │ │ + * Transform an object into a JSON string. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * object - {Object} The object to be serialized. │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {String} A JSON string representing the object. │ │ │ │ │ + */ │ │ │ │ │ + 'object': function(object) { │ │ │ │ │ + // three special objects that we want to treat differently │ │ │ │ │ + if (object == null) { │ │ │ │ │ + return "null"; │ │ │ │ │ + } │ │ │ │ │ + if (object.constructor == Date) { │ │ │ │ │ + return this.serialize.date.apply(this, [object]); │ │ │ │ │ + } │ │ │ │ │ + if (object.constructor == Array) { │ │ │ │ │ + return this.serialize.array.apply(this, [object]); │ │ │ │ │ + } │ │ │ │ │ + var pieces = ['{']; │ │ │ │ │ + this.level += 1; │ │ │ │ │ + var key, keyJSON, valueJSON; │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * 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 = {}; │ │ │ │ │ + var addComma = false; │ │ │ │ │ + for (key in object) { │ │ │ │ │ + if (object.hasOwnProperty(key)) { │ │ │ │ │ + // recursive calls need to allow for sub-classing │ │ │ │ │ + keyJSON = OpenLayers.Format.JSON.prototype.write.apply(this, │ │ │ │ │ + [key, this.pretty]); │ │ │ │ │ + valueJSON = OpenLayers.Format.JSON.prototype.write.apply(this, │ │ │ │ │ + [object[key], this.pretty]); │ │ │ │ │ + if (keyJSON != null && valueJSON != null) { │ │ │ │ │ + if (addComma) { │ │ │ │ │ + pieces.push(','); │ │ │ │ │ + } │ │ │ │ │ + pieces.push(this.writeNewline(), this.writeIndent(), │ │ │ │ │ + keyJSON, ':', this.writeSpace(), valueJSON); │ │ │ │ │ + addComma = true; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * 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] │ │ │ │ │ - } │ │ │ │ │ -}; │ │ │ │ │ + this.level -= 1; │ │ │ │ │ + pieces.push(this.writeNewline(), this.writeIndent(), '}'); │ │ │ │ │ + return pieces.join(''); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * 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; │ │ │ │ │ -}; │ │ │ │ │ + /** │ │ │ │ │ + * Method: serialize.array │ │ │ │ │ + * Transform an array into a JSON string. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * array - {Array} The array to be serialized │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {String} A JSON string representing the array. │ │ │ │ │ + */ │ │ │ │ │ + 'array': function(array) { │ │ │ │ │ + var json; │ │ │ │ │ + var pieces = ['[']; │ │ │ │ │ + this.level += 1; │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * 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); │ │ │ │ │ + for (var i = 0, len = array.length; i < len; ++i) { │ │ │ │ │ + // recursive calls need to allow for sub-classing │ │ │ │ │ + json = OpenLayers.Format.JSON.prototype.write.apply(this, │ │ │ │ │ + [array[i], this.pretty]); │ │ │ │ │ + if (json != null) { │ │ │ │ │ + if (i > 0) { │ │ │ │ │ + pieces.push(','); │ │ │ │ │ + } │ │ │ │ │ + pieces.push(this.writeNewline(), this.writeIndent(), json); │ │ │ │ │ + } │ │ │ │ │ } │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - 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() { │ │ │ │ │ + this.level -= 1; │ │ │ │ │ + pieces.push(this.writeNewline(), this.writeIndent(), ']'); │ │ │ │ │ + return pieces.join(''); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - var pole = 20037508.34; │ │ │ │ │ + /** │ │ │ │ │ + * 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 + '"'; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - 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; │ │ │ │ │ - } │ │ │ │ │ + /** │ │ │ │ │ + * 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"; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - 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; │ │ │ │ │ - } │ │ │ │ │ + /** │ │ │ │ │ + * 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); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - function map(base, codes) { │ │ │ │ │ - var add = OpenLayers.Projection.addTransform; │ │ │ │ │ - var same = OpenLayers.Projection.nullTransform; │ │ │ │ │ - var i, len, code, other, j; │ │ │ │ │ - for (i = 0, len = codes.length; i < len; ++i) { │ │ │ │ │ - code = codes[i]; │ │ │ │ │ - add(base, code, forwardMercator); │ │ │ │ │ - add(code, base, inverseMercator); │ │ │ │ │ - for (j = i + 1; j < len; ++j) { │ │ │ │ │ - other = codes[j]; │ │ │ │ │ - add(code, other, same); │ │ │ │ │ - add(other, code, same); │ │ │ │ │ + /** │ │ │ │ │ + * 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()) + '"'; │ │ │ │ │ } │ │ │ │ │ - } │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - // list of equivalent codes for web mercator │ │ │ │ │ - var mercator = ["EPSG:900913", "EPSG:3857", "EPSG:102113", "EPSG:102100"], │ │ │ │ │ - geographic = ["CRS:84", "urn:ogc:def:crs:EPSG:6.6:4326", "EPSG:4326"], │ │ │ │ │ - i; │ │ │ │ │ - for (i = mercator.length - 1; i >= 0; --i) { │ │ │ │ │ - map(mercator[i], geographic); │ │ │ │ │ - } │ │ │ │ │ - for (i = geographic.length - 1; i >= 0; --i) { │ │ │ │ │ - map(geographic[i], mercator); │ │ │ │ │ - } │ │ │ │ │ + CLASS_NAME: "OpenLayers.Format.JSON" │ │ │ │ │ │ │ │ │ │ -})(); │ │ │ │ │ +}); │ │ │ │ │ /* ====================================================================== │ │ │ │ │ - OpenLayers/Map.js │ │ │ │ │ + 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/BaseTypes/Class.js │ │ │ │ │ - * @requires OpenLayers/Util.js │ │ │ │ │ - * @requires OpenLayers/Util/vendorPrefix.js │ │ │ │ │ - * @requires OpenLayers/Events.js │ │ │ │ │ - * @requires OpenLayers/Tween.js │ │ │ │ │ - * @requires OpenLayers/Projection.js │ │ │ │ │ + * @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.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. │ │ │ │ │ + * Class: OpenLayers.Format.GeoJSON │ │ │ │ │ + * Read and write GeoJSON. Create a new parser with the │ │ │ │ │ + * constructor. │ │ │ │ │ + * │ │ │ │ │ + * Inherits from: │ │ │ │ │ + * - │ │ │ │ │ */ │ │ │ │ │ -OpenLayers.Map = OpenLayers.Class({ │ │ │ │ │ +OpenLayers.Format.GeoJSON = OpenLayers.Class(OpenLayers.Format.JSON, { │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Constant: Z_INDEX_BASE │ │ │ │ │ - * {Object} Base z-indexes for different classes of thing │ │ │ │ │ + * APIProperty: ignoreExtraDims │ │ │ │ │ + * {Boolean} Ignore dimensions higher than 2 when reading geometry │ │ │ │ │ + * coordinates. │ │ │ │ │ */ │ │ │ │ │ - Z_INDEX_BASE: { │ │ │ │ │ - BaseLayer: 100, │ │ │ │ │ - Overlay: 325, │ │ │ │ │ - Feature: 725, │ │ │ │ │ - Popup: 750, │ │ │ │ │ - Control: 1000 │ │ │ │ │ - }, │ │ │ │ │ + ignoreExtraDims: false, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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). │ │ │ │ │ + * Constructor: OpenLayers.Format.GeoJSON │ │ │ │ │ + * Create a new parser for GeoJSON. │ │ │ │ │ * │ │ │ │ │ - * 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 │ │ │ │ │ + * Parameters: │ │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ │ + * this instance. │ │ │ │ │ */ │ │ │ │ │ - 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. │ │ │ │ │ + * APIMethod: read │ │ │ │ │ + * Deserialize a GeoJSON string. │ │ │ │ │ * │ │ │ │ │ - * 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. │ │ │ │ │ + * 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. │ │ │ │ │ * │ │ │ │ │ - * 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 │ │ │ │ │ + * 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 │ │ │ │ │ + * . │ │ │ │ │ */ │ │ │ │ │ - events: null, │ │ │ │ │ + read: function(json, type, filter) { │ │ │ │ │ + type = (type) ? type : "FeatureCollection"; │ │ │ │ │ + var results = null; │ │ │ │ │ + var obj = null; │ │ │ │ │ + if (typeof json == "string") { │ │ │ │ │ + obj = OpenLayers.Format.JSON.prototype.read.apply(this, │ │ │ │ │ + [json, filter]); │ │ │ │ │ + } else { │ │ │ │ │ + obj = json; │ │ │ │ │ + } │ │ │ │ │ + if (!obj) { │ │ │ │ │ + OpenLayers.Console.error("Bad JSON: " + json); │ │ │ │ │ + } else if (typeof(obj.type) != "string") { │ │ │ │ │ + OpenLayers.Console.error("Bad GeoJSON - no type: " + json); │ │ │ │ │ + } else if (this.isValidType(obj, type)) { │ │ │ │ │ + switch (type) { │ │ │ │ │ + case "Geometry": │ │ │ │ │ + try { │ │ │ │ │ + results = this.parseGeometry(obj); │ │ │ │ │ + } catch (err) { │ │ │ │ │ + OpenLayers.Console.error(err); │ │ │ │ │ + } │ │ │ │ │ + break; │ │ │ │ │ + case "Feature": │ │ │ │ │ + try { │ │ │ │ │ + results = this.parseFeature(obj); │ │ │ │ │ + results.type = "Feature"; │ │ │ │ │ + } catch (err) { │ │ │ │ │ + OpenLayers.Console.error(err); │ │ │ │ │ + } │ │ │ │ │ + break; │ │ │ │ │ + case "FeatureCollection": │ │ │ │ │ + // for type FeatureCollection, we allow input to be any type │ │ │ │ │ + results = []; │ │ │ │ │ + switch (obj.type) { │ │ │ │ │ + case "Feature": │ │ │ │ │ + try { │ │ │ │ │ + results.push(this.parseFeature(obj)); │ │ │ │ │ + } catch (err) { │ │ │ │ │ + results = null; │ │ │ │ │ + OpenLayers.Console.error(err); │ │ │ │ │ + } │ │ │ │ │ + break; │ │ │ │ │ + case "FeatureCollection": │ │ │ │ │ + for (var i = 0, len = obj.features.length; i < len; ++i) { │ │ │ │ │ + try { │ │ │ │ │ + results.push(this.parseFeature(obj.features[i])); │ │ │ │ │ + } catch (err) { │ │ │ │ │ + results = null; │ │ │ │ │ + OpenLayers.Console.error(err); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + break; │ │ │ │ │ + default: │ │ │ │ │ + try { │ │ │ │ │ + var geom = this.parseGeometry(obj); │ │ │ │ │ + results.push(new OpenLayers.Feature.Vector(geom)); │ │ │ │ │ + } catch (err) { │ │ │ │ │ + results = null; │ │ │ │ │ + OpenLayers.Console.error(err); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + break; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + return results; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * Method: isValidType │ │ │ │ │ + * Check if a GeoJSON object is a valid representative of the given type. │ │ │ │ │ * │ │ │ │ │ - * 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. │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Boolean} The object is valid GeoJSON object of the given type. │ │ │ │ │ */ │ │ │ │ │ - layerContainerDiv: null, │ │ │ │ │ + isValidType: function(obj, type) { │ │ │ │ │ + var valid = false; │ │ │ │ │ + switch (type) { │ │ │ │ │ + case "Geometry": │ │ │ │ │ + if (OpenLayers.Util.indexOf( │ │ │ │ │ + ["Point", "MultiPoint", "LineString", "MultiLineString", │ │ │ │ │ + "Polygon", "MultiPolygon", "Box", "GeometryCollection" │ │ │ │ │ + ], │ │ │ │ │ + obj.type) == -1) { │ │ │ │ │ + // unsupported geometry type │ │ │ │ │ + OpenLayers.Console.error("Unsupported geometry type: " + │ │ │ │ │ + obj.type); │ │ │ │ │ + } else { │ │ │ │ │ + valid = true; │ │ │ │ │ + } │ │ │ │ │ + break; │ │ │ │ │ + case "FeatureCollection": │ │ │ │ │ + // allow for any type to be converted to a feature collection │ │ │ │ │ + valid = true; │ │ │ │ │ + break; │ │ │ │ │ + default: │ │ │ │ │ + // for Feature types must match │ │ │ │ │ + if (obj.type == type) { │ │ │ │ │ + valid = true; │ │ │ │ │ + } else { │ │ │ │ │ + OpenLayers.Console.error("Cannot convert types from " + │ │ │ │ │ + obj.type + " to " + type); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + return valid; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: layers │ │ │ │ │ - * {Array()} Ordered list of layers in the map │ │ │ │ │ + * Method: parseFeature │ │ │ │ │ + * Convert a feature object from GeoJSON into an │ │ │ │ │ + * . │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * obj - {Object} An object created from a GeoJSON object │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {} A feature. │ │ │ │ │ */ │ │ │ │ │ - layers: null, │ │ │ │ │ + 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; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: controls │ │ │ │ │ - * {Array()} List of controls associated with the map. │ │ │ │ │ + * Method: parseGeometry │ │ │ │ │ + * Convert a geometry object from GeoJSON into an . │ │ │ │ │ * │ │ │ │ │ - * 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 │ │ │ │ │ - * - │ │ │ │ │ - * - │ │ │ │ │ + * Parameters: │ │ │ │ │ + * obj - {Object} An object created from a GeoJSON object │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {} A geometry. │ │ │ │ │ */ │ │ │ │ │ - controls: null, │ │ │ │ │ + parseGeometry: function(obj) { │ │ │ │ │ + if (obj == null) { │ │ │ │ │ + return null; │ │ │ │ │ + } │ │ │ │ │ + var geometry, collection = false; │ │ │ │ │ + if (obj.type == "GeometryCollection") { │ │ │ │ │ + if (!(OpenLayers.Util.isArray(obj.geometries))) { │ │ │ │ │ + throw "GeometryCollection must have geometries array: " + obj; │ │ │ │ │ + } │ │ │ │ │ + var numGeom = obj.geometries.length; │ │ │ │ │ + var components = new Array(numGeom); │ │ │ │ │ + for (var i = 0; i < numGeom; ++i) { │ │ │ │ │ + components[i] = this.parseGeometry.apply( │ │ │ │ │ + this, [obj.geometries[i]] │ │ │ │ │ + ); │ │ │ │ │ + } │ │ │ │ │ + geometry = new OpenLayers.Geometry.Collection(components); │ │ │ │ │ + collection = true; │ │ │ │ │ + } else { │ │ │ │ │ + if (!(OpenLayers.Util.isArray(obj.coordinates))) { │ │ │ │ │ + throw "Geometry must have coordinates array: " + obj; │ │ │ │ │ + } │ │ │ │ │ + if (!this.parseCoords[obj.type.toLowerCase()]) { │ │ │ │ │ + throw "Unsupported geometry type: " + obj.type; │ │ │ │ │ + } │ │ │ │ │ + try { │ │ │ │ │ + geometry = this.parseCoords[obj.type.toLowerCase()].apply( │ │ │ │ │ + this, [obj.coordinates] │ │ │ │ │ + ); │ │ │ │ │ + } catch (err) { │ │ │ │ │ + // deal with bad coordinates │ │ │ │ │ + throw err; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + // We don't reproject collections because the children are reprojected │ │ │ │ │ + // for us when they are created. │ │ │ │ │ + if (this.internalProjection && this.externalProjection && !collection) { │ │ │ │ │ + geometry.transform(this.externalProjection, │ │ │ │ │ + this.internalProjection); │ │ │ │ │ + } │ │ │ │ │ + return geometry; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: popups │ │ │ │ │ - * {Array()} List of popups associated with the map │ │ │ │ │ + * Property: parseCoords │ │ │ │ │ + * Object with properties corresponding to the GeoJSON geometry types. │ │ │ │ │ + * Property values are functions that do the actual parsing. │ │ │ │ │ */ │ │ │ │ │ - popups: null, │ │ │ │ │ + parseCoords: { │ │ │ │ │ + /** │ │ │ │ │ + * Method: parseCoords.point │ │ │ │ │ + * Convert a coordinate array from GeoJSON into an │ │ │ │ │ + * . │ │ │ │ │ + * │ │ │ │ │ + * 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]); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIProperty: baseLayer │ │ │ │ │ - * {} The currently selected base layer. This determines │ │ │ │ │ - * min/max zoom level, projection, etc. │ │ │ │ │ - */ │ │ │ │ │ - baseLayer: null, │ │ │ │ │ + /** │ │ │ │ │ + * 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 < len; ++i) { │ │ │ │ │ + try { │ │ │ │ │ + p = this.parseCoords["point"].apply(this, [array[i]]); │ │ │ │ │ + } catch (err) { │ │ │ │ │ + throw err; │ │ │ │ │ + } │ │ │ │ │ + points.push(p); │ │ │ │ │ + } │ │ │ │ │ + return new OpenLayers.Geometry.MultiPoint(points); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: center │ │ │ │ │ - * {} The current center of the map │ │ │ │ │ - */ │ │ │ │ │ - center: null, │ │ │ │ │ + /** │ │ │ │ │ + * Method: parseCoords.linestring │ │ │ │ │ + * Convert a coordinate array from GeoJSON into an │ │ │ │ │ + * . │ │ │ │ │ + * │ │ │ │ │ + * 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 < len; ++i) { │ │ │ │ │ + try { │ │ │ │ │ + p = this.parseCoords["point"].apply(this, [array[i]]); │ │ │ │ │ + } catch (err) { │ │ │ │ │ + throw err; │ │ │ │ │ + } │ │ │ │ │ + points.push(p); │ │ │ │ │ + } │ │ │ │ │ + return new OpenLayers.Geometry.LineString(points); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: resolution │ │ │ │ │ - * {Float} The resolution of the map. │ │ │ │ │ - */ │ │ │ │ │ - resolution: null, │ │ │ │ │ + /** │ │ │ │ │ + * Method: parseCoords.multilinestring │ │ │ │ │ + * Convert a coordinate array from GeoJSON into an │ │ │ │ │ + * . │ │ │ │ │ + * │ │ │ │ │ + * 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 < len; ++i) { │ │ │ │ │ + try { │ │ │ │ │ + l = this.parseCoords["linestring"].apply(this, [array[i]]); │ │ │ │ │ + } catch (err) { │ │ │ │ │ + throw err; │ │ │ │ │ + } │ │ │ │ │ + lines.push(l); │ │ │ │ │ + } │ │ │ │ │ + return new OpenLayers.Geometry.MultiLineString(lines); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: zoom │ │ │ │ │ - * {Integer} The current zoom level of the map │ │ │ │ │ - */ │ │ │ │ │ - zoom: 0, │ │ │ │ │ + /** │ │ │ │ │ + * Method: parseCoords.polygon │ │ │ │ │ + * Convert a coordinate array from GeoJSON into an │ │ │ │ │ + * . │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {} A geometry. │ │ │ │ │ + */ │ │ │ │ │ + "polygon": function(array) { │ │ │ │ │ + var rings = []; │ │ │ │ │ + var r, l; │ │ │ │ │ + for (var i = 0, len = array.length; i < len; ++i) { │ │ │ │ │ + try { │ │ │ │ │ + l = this.parseCoords["linestring"].apply(this, [array[i]]); │ │ │ │ │ + } catch (err) { │ │ │ │ │ + throw err; │ │ │ │ │ + } │ │ │ │ │ + r = new OpenLayers.Geometry.LinearRing(l.components); │ │ │ │ │ + rings.push(r); │ │ │ │ │ + } │ │ │ │ │ + return new OpenLayers.Geometry.Polygon(rings); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: panRatio │ │ │ │ │ - * {Float} The ratio of the current extent within │ │ │ │ │ - * which panning will tween. │ │ │ │ │ - */ │ │ │ │ │ - panRatio: 1.5, │ │ │ │ │ + /** │ │ │ │ │ + * Method: parseCoords.multipolygon │ │ │ │ │ + * Convert a coordinate array from GeoJSON into an │ │ │ │ │ + * . │ │ │ │ │ + * │ │ │ │ │ + * 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 < len; ++i) { │ │ │ │ │ + try { │ │ │ │ │ + p = this.parseCoords["polygon"].apply(this, [array[i]]); │ │ │ │ │ + } catch (err) { │ │ │ │ │ + throw err; │ │ │ │ │ + } │ │ │ │ │ + polys.push(p); │ │ │ │ │ + } │ │ │ │ │ + return new OpenLayers.Geometry.MultiPolygon(polys); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIProperty: options │ │ │ │ │ - * {Object} The options object passed to the class constructor. Read-only. │ │ │ │ │ - */ │ │ │ │ │ - options: null, │ │ │ │ │ + /** │ │ │ │ │ + * Method: parseCoords.box │ │ │ │ │ + * Convert a coordinate array from GeoJSON into an │ │ │ │ │ + * . │ │ │ │ │ + * │ │ │ │ │ + * 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]) │ │ │ │ │ + ]) │ │ │ │ │ + ]); │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ - // Options │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: tileSize │ │ │ │ │ - * {} Set in the map options to override the default tile │ │ │ │ │ - * size for this map. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - tileSize: null, │ │ │ │ │ + write: function(obj, pretty) { │ │ │ │ │ + var geojson = { │ │ │ │ │ + "type": null │ │ │ │ │ + }; │ │ │ │ │ + if (OpenLayers.Util.isArray(obj)) { │ │ │ │ │ + geojson.type = "FeatureCollection"; │ │ │ │ │ + var numFeatures = obj.length; │ │ │ │ │ + geojson.features = new Array(numFeatures); │ │ │ │ │ + for (var i = 0; i < numFeatures; ++i) { │ │ │ │ │ + var element = obj[i]; │ │ │ │ │ + if (!element instanceof OpenLayers.Feature.Vector) { │ │ │ │ │ + var msg = "FeatureCollection only supports collections " + │ │ │ │ │ + "of features: " + element; │ │ │ │ │ + throw msg; │ │ │ │ │ + } │ │ │ │ │ + geojson.features[i] = this.extract.feature.apply( │ │ │ │ │ + this, [element] │ │ │ │ │ + ); │ │ │ │ │ + } │ │ │ │ │ + } else if (obj.CLASS_NAME.indexOf("OpenLayers.Geometry") == 0) { │ │ │ │ │ + geojson = this.extract.geometry.apply(this, [obj]); │ │ │ │ │ + } else if (obj instanceof OpenLayers.Feature.Vector) { │ │ │ │ │ + geojson = this.extract.feature.apply(this, [obj]); │ │ │ │ │ + if (obj.layer && obj.layer.projection) { │ │ │ │ │ + geojson.crs = this.createCRSObject(obj); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + return OpenLayers.Format.JSON.prototype.write.apply(this, │ │ │ │ │ + [geojson, pretty]); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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 ). │ │ │ │ │ + * Method: createCRSObject │ │ │ │ │ + * Create the CRS object for an object. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * object - {} │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Object} An object which can be assigned to the crs property │ │ │ │ │ + * of a GeoJSON object. │ │ │ │ │ */ │ │ │ │ │ - projection: "EPSG:4326", │ │ │ │ │ + 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; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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 │ │ │ │ │ + * Property: extract │ │ │ │ │ + * Object with properties corresponding to the GeoJSON types. │ │ │ │ │ + * Property values are functions that do the actual value extraction. │ │ │ │ │ */ │ │ │ │ │ - units: null, │ │ │ │ │ + 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; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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, │ │ │ │ │ + /** │ │ │ │ │ + * 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 │ │ │ │ │ + }; │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIProperty: maxResolution │ │ │ │ │ - * {Float} Required if you are not displaying the whole world on a tile │ │ │ │ │ - * with the size specified in . │ │ │ │ │ - */ │ │ │ │ │ - maxResolution: null, │ │ │ │ │ + return json; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIProperty: minResolution │ │ │ │ │ - * {Float} │ │ │ │ │ - */ │ │ │ │ │ - minResolution: null, │ │ │ │ │ + /** │ │ │ │ │ + * 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]; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIProperty: maxScale │ │ │ │ │ - * {Float} │ │ │ │ │ - */ │ │ │ │ │ - maxScale: null, │ │ │ │ │ + /** │ │ │ │ │ + * 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 < len; ++i) { │ │ │ │ │ + array.push(this.extract.point.apply(this, [multipoint.components[i]])); │ │ │ │ │ + } │ │ │ │ │ + return array; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIProperty: minScale │ │ │ │ │ - * {Float} │ │ │ │ │ - */ │ │ │ │ │ - minScale: null, │ │ │ │ │ + /** │ │ │ │ │ + * Method: extract.linestring │ │ │ │ │ + * Return an array of coordinate arrays from a linestring. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * linestring - {} │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Array} An array of coordinate arrays representing │ │ │ │ │ + * the linestring. │ │ │ │ │ + */ │ │ │ │ │ + 'linestring': function(linestring) { │ │ │ │ │ + var array = []; │ │ │ │ │ + for (var i = 0, len = linestring.components.length; i < len; ++i) { │ │ │ │ │ + array.push(this.extract.point.apply(this, [linestring.components[i]])); │ │ │ │ │ + } │ │ │ │ │ + return array; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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, │ │ │ │ │ + /** │ │ │ │ │ + * Method: extract.multilinestring │ │ │ │ │ + * Return an array of linestring arrays from a linestring. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * multilinestring - {} │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Array} An array of linestring arrays representing │ │ │ │ │ + * the multilinestring. │ │ │ │ │ + */ │ │ │ │ │ + 'multilinestring': function(multilinestring) { │ │ │ │ │ + var array = []; │ │ │ │ │ + for (var i = 0, len = multilinestring.components.length; i < len; ++i) { │ │ │ │ │ + array.push(this.extract.linestring.apply(this, [multilinestring.components[i]])); │ │ │ │ │ + } │ │ │ │ │ + return array; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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, │ │ │ │ │ + /** │ │ │ │ │ + * Method: extract.polygon │ │ │ │ │ + * Return an array of linear ring arrays from a polygon. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * polygon - {} │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Array} An array of linear ring arrays representing the polygon. │ │ │ │ │ + */ │ │ │ │ │ + 'polygon': function(polygon) { │ │ │ │ │ + var array = []; │ │ │ │ │ + for (var i = 0, len = polygon.components.length; i < len; ++i) { │ │ │ │ │ + array.push(this.extract.linestring.apply(this, [polygon.components[i]])); │ │ │ │ │ + } │ │ │ │ │ + return array; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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, │ │ │ │ │ + /** │ │ │ │ │ + * Method: extract.multipolygon │ │ │ │ │ + * Return an array of polygon arrays from a multipolygon. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * multipolygon - {} │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Array} An array of polygon arrays representing │ │ │ │ │ + * the multipolygon │ │ │ │ │ + */ │ │ │ │ │ + 'multipolygon': function(multipolygon) { │ │ │ │ │ + var array = []; │ │ │ │ │ + for (var i = 0, len = multipolygon.components.length; i < len; ++i) { │ │ │ │ │ + array.push(this.extract.polygon.apply(this, [multipolygon.components[i]])); │ │ │ │ │ + } │ │ │ │ │ + return array; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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, │ │ │ │ │ + /** │ │ │ │ │ + * Method: extract.collection │ │ │ │ │ + * Return an array of geometries from a geometry collection. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * collection - {} │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Array} An array of geometry objects representing the geometry │ │ │ │ │ + * collection. │ │ │ │ │ + */ │ │ │ │ │ + 'collection': function(collection) { │ │ │ │ │ + var len = collection.components.length; │ │ │ │ │ + var array = new Array(len); │ │ │ │ │ + for (var i = 0; i < len; ++i) { │ │ │ │ │ + array[i] = this.extract.geometry.apply( │ │ │ │ │ + this, [collection.components[i]] │ │ │ │ │ + ); │ │ │ │ │ + } │ │ │ │ │ + return array; │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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 {}. │ │ │ │ │ - */ │ │ │ │ │ + CLASS_NAME: "OpenLayers.Format.GeoJSON" │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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, │ │ │ │ │ +}); │ │ │ │ │ +/* ====================================================================== │ │ │ │ │ + OpenLayers/Format/XML.js │ │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIProperty: autoUpdateSize │ │ │ │ │ - * {Boolean} Should OpenLayers automatically update the size of the map │ │ │ │ │ - * when the resize event is fired. Default is true. │ │ │ │ │ - */ │ │ │ │ │ - autoUpdateSize: true, │ │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ + * full text of the license. */ │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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, │ │ │ │ │ +/** │ │ │ │ │ + * @requires OpenLayers/Format.js │ │ │ │ │ + */ │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: panTween │ │ │ │ │ - * {} Animated panning tween object, see panTo() │ │ │ │ │ - */ │ │ │ │ │ - panTween: null, │ │ │ │ │ +/** │ │ │ │ │ + * 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, { │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - panMethod: OpenLayers.Easing.Expo.easeOut, │ │ │ │ │ + namespaces: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: panDuration │ │ │ │ │ - * {Integer} The number of steps to be passed to the │ │ │ │ │ - * OpenLayers.Tween.start() method when the map is │ │ │ │ │ - * panned. │ │ │ │ │ - * Default is 50. │ │ │ │ │ + * Property: namespaceAlias │ │ │ │ │ + * {Object} Mapping of namespace URI to namespace alias. This object │ │ │ │ │ + * is read-only. Use to add or set a namespace alias. │ │ │ │ │ */ │ │ │ │ │ - panDuration: 50, │ │ │ │ │ + namespaceAlias: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: zoomTween │ │ │ │ │ - * {} Animated zooming tween object, see zoomTo() │ │ │ │ │ + * Property: defaultPrefix │ │ │ │ │ + * {String} The default namespace alias for creating element nodes. │ │ │ │ │ */ │ │ │ │ │ - zoomTween: null, │ │ │ │ │ + defaultPrefix: 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. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - zoomMethod: OpenLayers.Easing.Quad.easeOut, │ │ │ │ │ + readers: {}, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: zoomDuration │ │ │ │ │ - * {Integer} The number of steps to be passed to the │ │ │ │ │ - * OpenLayers.Tween.start() method when the map is zoomed. │ │ │ │ │ - * Default is 20. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - zoomDuration: 20, │ │ │ │ │ + writers: {}, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: paddingForPopups │ │ │ │ │ - * {} Outside margin of the popup. Used to prevent │ │ │ │ │ - * the popup from getting too close to the map border. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - paddingForPopups: null, │ │ │ │ │ + xmldom: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: layerContainerOriginPx │ │ │ │ │ - * {Object} Cached object representing the layer container origin (in pixels). │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - layerContainerOriginPx: null, │ │ │ │ │ + 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; │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * APIMethod: destroy │ │ │ │ │ + * Clean up. │ │ │ │ │ */ │ │ │ │ │ - minPx: null, │ │ │ │ │ + destroy: function() { │ │ │ │ │ + this.xmldom = null; │ │ │ │ │ + OpenLayers.Format.prototype.destroy.apply(this, arguments); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * Method: setNamespace │ │ │ │ │ + * Set a namespace alias and URI for the format. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * alias - {String} The namespace alias (prefix). │ │ │ │ │ + * uri - {String} The namespace URI. │ │ │ │ │ */ │ │ │ │ │ - maxPx: null, │ │ │ │ │ + setNamespace: function(alias, uri) { │ │ │ │ │ + this.namespaces[alias] = uri; │ │ │ │ │ + this.namespaceAlias[uri] = alias; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Constructor: OpenLayers.Map │ │ │ │ │ - * Constructor for a new OpenLayers.Map instance. There are two possible │ │ │ │ │ - * ways to call the map constructor. See the examples below. │ │ │ │ │ + * APIMethod: read │ │ │ │ │ + * Deserialize a XML string and return a DOM node. │ │ │ │ │ * │ │ │ │ │ * 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) │ │ │ │ │ + * text - {String} A XML string │ │ │ │ │ + │ │ │ │ │ + * Returns: │ │ │ │ │ + * {DOMElement} A DOM node │ │ │ │ │ */ │ │ │ │ │ - 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"; │ │ │ │ │ + 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; │ │ │ │ │ │ │ │ │ │ - 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 │ │ │ │ │ + } │ │ │ │ │ + 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 (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.keepData) { │ │ │ │ │ + this.data = node; │ │ │ │ │ } │ │ │ │ │ │ │ │ │ │ - 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); │ │ │ │ │ - } │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - // only append link stylesheet if the theme property is set │ │ │ │ │ - if (this.theme) { │ │ │ │ │ - // check existing links for equivalent url │ │ │ │ │ - var addNode = true; │ │ │ │ │ - var nodes = document.getElementsByTagName('link'); │ │ │ │ │ - for (var i = 0, len = nodes.length; i < len; ++i) { │ │ │ │ │ - if (OpenLayers.Util.isEquivalentUrl(nodes.item(i).href, │ │ │ │ │ - this.theme)) { │ │ │ │ │ - addNode = false; │ │ │ │ │ - break; │ │ │ │ │ + /** │ │ │ │ │ + * 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); │ │ │ │ │ } │ │ │ │ │ - } │ │ │ │ │ - // only add a new node if one with an equivalent url hasn't already │ │ │ │ │ - // been added │ │ │ │ │ - if (addNode) { │ │ │ │ │ - var cssNode = document.createElement('link'); │ │ │ │ │ - cssNode.setAttribute('rel', 'stylesheet'); │ │ │ │ │ - cssNode.setAttribute('type', 'text/css'); │ │ │ │ │ - cssNode.setAttribute('href', this.theme); │ │ │ │ │ - document.getElementsByTagName('head')[0].appendChild(cssNode); │ │ │ │ │ + doc.appendChild(node); │ │ │ │ │ + data = serializer.serializeToString(doc); │ │ │ │ │ + } else { │ │ │ │ │ + data = serializer.serializeToString(node); │ │ │ │ │ } │ │ │ │ │ } │ │ │ │ │ + return data; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - if (this.controls == null) { // default controls │ │ │ │ │ - this.controls = []; │ │ │ │ │ - if (OpenLayers.Control != null) { // running full or lite? │ │ │ │ │ - // Navigation or TouchNavigation depending on what is in build │ │ │ │ │ - if (OpenLayers.Control.Navigation) { │ │ │ │ │ - this.controls.push(new OpenLayers.Control.Navigation()); │ │ │ │ │ - } else if (OpenLayers.Control.TouchNavigation) { │ │ │ │ │ - this.controls.push(new OpenLayers.Control.TouchNavigation()); │ │ │ │ │ - } │ │ │ │ │ - if (OpenLayers.Control.Zoom) { │ │ │ │ │ - this.controls.push(new OpenLayers.Control.Zoom()); │ │ │ │ │ - } else if (OpenLayers.Control.PanZoom) { │ │ │ │ │ - this.controls.push(new OpenLayers.Control.PanZoom()); │ │ │ │ │ - } │ │ │ │ │ - │ │ │ │ │ - if (OpenLayers.Control.ArgParser) { │ │ │ │ │ - this.controls.push(new OpenLayers.Control.ArgParser()); │ │ │ │ │ - } │ │ │ │ │ - if (OpenLayers.Control.Attribution) { │ │ │ │ │ - this.controls.push(new OpenLayers.Control.Attribution()); │ │ │ │ │ - } │ │ │ │ │ + /** │ │ │ │ │ + * 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; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - for (var i = 0, len = this.controls.length; i < len; i++) { │ │ │ │ │ - this.addControlToMap(this.controls[i]); │ │ │ │ │ - } │ │ │ │ │ - │ │ │ │ │ - this.popups = []; │ │ │ │ │ - │ │ │ │ │ - this.unloadDestroy = OpenLayers.Function.bind(this.destroy, this); │ │ │ │ │ - │ │ │ │ │ - │ │ │ │ │ - // always call map.destroy() │ │ │ │ │ - OpenLayers.Event.observe(window, 'unload', this.unloadDestroy); │ │ │ │ │ - │ │ │ │ │ - // add any initial layers │ │ │ │ │ - if (options && options.layers) { │ │ │ │ │ - /** │ │ │ │ │ - * If you have set options.center, the map center property will be │ │ │ │ │ - * set at this point. However, since setCenter has not been called, │ │ │ │ │ - * addLayers gets confused. So we delete the map center in this │ │ │ │ │ - * case. Because the check below uses options.center, it will │ │ │ │ │ - * be properly set below. │ │ │ │ │ - */ │ │ │ │ │ - delete this.center; │ │ │ │ │ - delete this.zoom; │ │ │ │ │ - this.addLayers(options.layers); │ │ │ │ │ - // set center (and optionally zoom) │ │ │ │ │ - if (options.center && !this.getCenter()) { │ │ │ │ │ - // zoom can be undefined here │ │ │ │ │ - this.setCenter(options.center, options.zoom); │ │ │ │ │ - } │ │ │ │ │ + /** │ │ │ │ │ + * 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; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - if (this.panMethod) { │ │ │ │ │ - this.panTween = new OpenLayers.Tween(this.panMethod); │ │ │ │ │ + /** │ │ │ │ │ + * 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.zoomMethod && this.applyTransform.transform) { │ │ │ │ │ - this.zoomTween = new OpenLayers.Tween(this.zoomMethod); │ │ │ │ │ + if (this.xmldom) { │ │ │ │ │ + node = this.xmldom.createTextNode(text); │ │ │ │ │ + } else { │ │ │ │ │ + node = document.createTextNode(text); │ │ │ │ │ } │ │ │ │ │ + return node; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIMethod: getViewport │ │ │ │ │ - * Get the DOMElement representing the view port. │ │ │ │ │ - * │ │ │ │ │ + /** │ │ │ │ │ + * 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: │ │ │ │ │ - * {DOMElement} │ │ │ │ │ + * {NodeList} A node list or array of elements. │ │ │ │ │ */ │ │ │ │ │ - getViewport: function() { │ │ │ │ │ - return this.viewPortDiv; │ │ │ │ │ + getElementsByTagNameNS: function(node, uri, name) { │ │ │ │ │ + var elements = []; │ │ │ │ │ + if (node.getElementsByTagNameNS) { │ │ │ │ │ + elements = node.getElementsByTagNameNS(uri, name); │ │ │ │ │ + } else { │ │ │ │ │ + // brute force method │ │ │ │ │ + var allNodes = node.getElementsByTagName("*"); │ │ │ │ │ + var potentialNode, fullName; │ │ │ │ │ + for (var i = 0, len = allNodes.length; i < len; ++i) { │ │ │ │ │ + potentialNode = allNodes[i]; │ │ │ │ │ + fullName = (potentialNode.prefix) ? │ │ │ │ │ + (potentialNode.prefix + ":" + name) : name; │ │ │ │ │ + if ((name == "*") || (fullName == potentialNode.nodeName)) { │ │ │ │ │ + if ((uri == "*") || (uri == potentialNode.namespaceURI)) { │ │ │ │ │ + elements.push(potentialNode); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + return elements; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: render │ │ │ │ │ - * Render the map to a specified container. │ │ │ │ │ + * APIMethod: getAttributeNodeNS │ │ │ │ │ + * Get an attribute node given the namespace URI and local name. │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * div - {String|DOMElement} The container that the map should be rendered │ │ │ │ │ - * to. If different than the current container, the map viewport │ │ │ │ │ - * will be moved from the current to the new container. │ │ │ │ │ + * node - {Element} Node on which to search for attribute nodes. │ │ │ │ │ + * uri - {String} Namespace URI. │ │ │ │ │ + * name - {String} Local name of the attribute (without the prefix). │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {DOMElement} An attribute node or null if none found. │ │ │ │ │ */ │ │ │ │ │ - render: function(div) { │ │ │ │ │ - this.div = OpenLayers.Util.getElement(div); │ │ │ │ │ - OpenLayers.Element.addClass(this.div, 'olMap'); │ │ │ │ │ - this.viewPortDiv.parentNode.removeChild(this.viewPortDiv); │ │ │ │ │ - this.div.appendChild(this.viewPortDiv); │ │ │ │ │ - this.updateSize(); │ │ │ │ │ + getAttributeNodeNS: function(node, uri, name) { │ │ │ │ │ + var attributeNode = null; │ │ │ │ │ + if (node.getAttributeNodeNS) { │ │ │ │ │ + attributeNode = node.getAttributeNodeNS(uri, name); │ │ │ │ │ + } else { │ │ │ │ │ + var attributes = node.attributes; │ │ │ │ │ + var potentialNode, fullName; │ │ │ │ │ + for (var i = 0, len = attributes.length; i < len; ++i) { │ │ │ │ │ + potentialNode = attributes[i]; │ │ │ │ │ + if (potentialNode.namespaceURI == uri) { │ │ │ │ │ + fullName = (potentialNode.prefix) ? │ │ │ │ │ + (potentialNode.prefix + ":" + name) : name; │ │ │ │ │ + if (fullName == potentialNode.nodeName) { │ │ │ │ │ + attributeNode = potentialNode; │ │ │ │ │ + break; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + return attributeNode; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: unloadDestroy │ │ │ │ │ - * Function that is called to destroy the map on page unload. stored here │ │ │ │ │ - * so that if map is manually destroyed, we can unregister this. │ │ │ │ │ + * APIMethod: getAttributeNS │ │ │ │ │ + * Get an attribute value given the namespace URI and local name. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * node - {Element} Node on which to search for an attribute. │ │ │ │ │ + * uri - {String} Namespace URI. │ │ │ │ │ + * name - {String} Local name of the attribute (without the prefix). │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {String} An attribute value or and empty string if none found. │ │ │ │ │ */ │ │ │ │ │ - unloadDestroy: null, │ │ │ │ │ + getAttributeNS: function(node, uri, name) { │ │ │ │ │ + var attributeValue = ""; │ │ │ │ │ + if (node.getAttributeNS) { │ │ │ │ │ + attributeValue = node.getAttributeNS(uri, name) || ""; │ │ │ │ │ + } else { │ │ │ │ │ + var attributeNode = this.getAttributeNodeNS(node, uri, name); │ │ │ │ │ + if (attributeNode) { │ │ │ │ │ + attributeValue = attributeNode.nodeValue; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + return attributeValue; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: updateSizeDestroy │ │ │ │ │ - * When the map is destroyed, we need to stop listening to updateSize │ │ │ │ │ - * events: this method stores the function we need to unregister in │ │ │ │ │ - * non-IE browsers. │ │ │ │ │ + * APIMethod: getChildValue │ │ │ │ │ + * Get the textual value of the node if it exists, or return an │ │ │ │ │ + * optional default string. Returns an empty string if no first child │ │ │ │ │ + * exists and no default value is supplied. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * node - {DOMElement} The element used to look for a first child value. │ │ │ │ │ + * def - {String} Optional string to return in the event that no │ │ │ │ │ + * first child value exists. │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {String} The value of the first child of the given node. │ │ │ │ │ */ │ │ │ │ │ - updateSizeDestroy: null, │ │ │ │ │ + getChildValue: function(node, def) { │ │ │ │ │ + var value = def || ""; │ │ │ │ │ + if (node) { │ │ │ │ │ + for (var child = node.firstChild; child; child = child.nextSibling) { │ │ │ │ │ + switch (child.nodeType) { │ │ │ │ │ + case 3: // text node │ │ │ │ │ + case 4: // cdata section │ │ │ │ │ + value += child.nodeValue; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + return value; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: destroy │ │ │ │ │ - * Destroy this map. │ │ │ │ │ - * Note that if you are using an application which removes a container │ │ │ │ │ - * of the map from the DOM, you need to ensure that you destroy the │ │ │ │ │ - * map *before* this happens; otherwise, the page unload handler │ │ │ │ │ - * will fail because the DOM elements that map.destroy() wants │ │ │ │ │ - * to clean up will be gone. (See │ │ │ │ │ - * http://trac.osgeo.org/openlayers/ticket/2277 for more information). │ │ │ │ │ - * This will apply to GeoExt and also to other applications which │ │ │ │ │ - * modify the DOM of the container of the OpenLayers Map. │ │ │ │ │ + * APIMethod: isSimpleContent │ │ │ │ │ + * Test if the given node has only simple content (i.e. no child element │ │ │ │ │ + * nodes). │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * node - {DOMElement} An element node. │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Boolean} The node has no child element nodes (nodes of type 1). │ │ │ │ │ */ │ │ │ │ │ - destroy: function() { │ │ │ │ │ - // if unloadDestroy is null, we've already been destroyed │ │ │ │ │ - if (!this.unloadDestroy) { │ │ │ │ │ - return false; │ │ │ │ │ - } │ │ │ │ │ - │ │ │ │ │ - // make sure panning doesn't continue after destruction │ │ │ │ │ - if (this.panTween) { │ │ │ │ │ - this.panTween.stop(); │ │ │ │ │ - this.panTween = null; │ │ │ │ │ - } │ │ │ │ │ - // make sure zooming doesn't continue after destruction │ │ │ │ │ - if (this.zoomTween) { │ │ │ │ │ - this.zoomTween.stop(); │ │ │ │ │ - this.zoomTween = null; │ │ │ │ │ + isSimpleContent: function(node) { │ │ │ │ │ + var simple = true; │ │ │ │ │ + for (var child = node.firstChild; child; child = child.nextSibling) { │ │ │ │ │ + if (child.nodeType === 1) { │ │ │ │ │ + simple = false; │ │ │ │ │ + break; │ │ │ │ │ + } │ │ │ │ │ } │ │ │ │ │ + return simple; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - // map has been destroyed. dont do it again! │ │ │ │ │ - OpenLayers.Event.stopObserving(window, 'unload', this.unloadDestroy); │ │ │ │ │ - this.unloadDestroy = null; │ │ │ │ │ - │ │ │ │ │ - if (this.updateSizeDestroy) { │ │ │ │ │ - OpenLayers.Event.stopObserving(window, 'resize', │ │ │ │ │ - this.updateSizeDestroy); │ │ │ │ │ - } │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: contentType │ │ │ │ │ + * Determine the content type for a given node. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * node - {DOMElement} │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Integer} One of OpenLayers.Format.XML.CONTENT_TYPE.{EMPTY,SIMPLE,COMPLEX,MIXED} │ │ │ │ │ + * if the node has no, simple, complex, or mixed content. │ │ │ │ │ + */ │ │ │ │ │ + contentType: function(node) { │ │ │ │ │ + var simple = false, │ │ │ │ │ + complex = false; │ │ │ │ │ │ │ │ │ │ - this.paddingForPopups = null; │ │ │ │ │ + var type = OpenLayers.Format.XML.CONTENT_TYPE.EMPTY; │ │ │ │ │ │ │ │ │ │ - if (this.controls != null) { │ │ │ │ │ - for (var i = this.controls.length - 1; i >= 0; --i) { │ │ │ │ │ - this.controls[i].destroy(); │ │ │ │ │ + for (var child = node.firstChild; child; child = child.nextSibling) { │ │ │ │ │ + switch (child.nodeType) { │ │ │ │ │ + case 1: // element │ │ │ │ │ + complex = true; │ │ │ │ │ + break; │ │ │ │ │ + case 8: // comment │ │ │ │ │ + break; │ │ │ │ │ + default: │ │ │ │ │ + simple = true; │ │ │ │ │ } │ │ │ │ │ - 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); │ │ │ │ │ + if (complex && simple) { │ │ │ │ │ + break; │ │ │ │ │ } │ │ │ │ │ - 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 (complex && simple) { │ │ │ │ │ + type = OpenLayers.Format.XML.CONTENT_TYPE.MIXED; │ │ │ │ │ + } else if (complex) { │ │ │ │ │ + return OpenLayers.Format.XML.CONTENT_TYPE.COMPLEX; │ │ │ │ │ + } else if (simple) { │ │ │ │ │ + return OpenLayers.Format.XML.CONTENT_TYPE.SIMPLE; │ │ │ │ │ } │ │ │ │ │ + return type; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - if (this.eventListeners) { │ │ │ │ │ - this.events.un(this.eventListeners); │ │ │ │ │ - this.eventListeners = null; │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: hasAttributeNS │ │ │ │ │ + * Determine whether a node has a particular attribute matching the given │ │ │ │ │ + * name and namespace. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * node - {Element} Node on which to search for an attribute. │ │ │ │ │ + * uri - {String} Namespace URI. │ │ │ │ │ + * name - {String} Local name of the attribute (without the prefix). │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Boolean} The node has an attribute matching the name and namespace. │ │ │ │ │ + */ │ │ │ │ │ + hasAttributeNS: function(node, uri, name) { │ │ │ │ │ + var found = false; │ │ │ │ │ + if (node.hasAttributeNS) { │ │ │ │ │ + found = node.hasAttributeNS(uri, name); │ │ │ │ │ + } else { │ │ │ │ │ + found = !!this.getAttributeNodeNS(node, uri, name); │ │ │ │ │ } │ │ │ │ │ - this.events.destroy(); │ │ │ │ │ - this.events = null; │ │ │ │ │ - │ │ │ │ │ - this.options = null; │ │ │ │ │ + return found; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: setOptions │ │ │ │ │ - * Change the map options │ │ │ │ │ + * APIMethod: setAttributeNS │ │ │ │ │ + * Adds a new attribute or changes the value of an attribute with the given │ │ │ │ │ + * namespace and name. │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * options - {Object} Hashtable of options to tag to the map │ │ │ │ │ + * node - {Element} Element node on which to set the attribute. │ │ │ │ │ + * uri - {String} Namespace URI for the attribute. │ │ │ │ │ + * name - {String} Qualified name (prefix:localname) for the attribute. │ │ │ │ │ + * value - {String} Attribute value. │ │ │ │ │ */ │ │ │ │ │ - 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 │ │ │ │ │ - }); │ │ │ │ │ + setAttributeNS: function(node, uri, name, value) { │ │ │ │ │ + if (node.setAttributeNS) { │ │ │ │ │ + node.setAttributeNS(uri, name, value); │ │ │ │ │ + } else { │ │ │ │ │ + if (this.xmldom) { │ │ │ │ │ + if (uri) { │ │ │ │ │ + var attribute = node.ownerDocument.createNode( │ │ │ │ │ + 2, name, uri │ │ │ │ │ + ); │ │ │ │ │ + attribute.nodeValue = value; │ │ │ │ │ + node.setAttributeNode(attribute); │ │ │ │ │ + } else { │ │ │ │ │ + node.setAttribute(name, value); │ │ │ │ │ + } │ │ │ │ │ + } else { │ │ │ │ │ + throw "setAttributeNS not implemented"; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: getTileSize │ │ │ │ │ - * Get the tile size for the map │ │ │ │ │ + * Method: createElementNSPlus │ │ │ │ │ + * Shorthand for creating namespaced elements with optional attributes and │ │ │ │ │ + * child text nodes. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * name - {String} The qualified node name. │ │ │ │ │ + * options - {Object} Optional object for node configuration. │ │ │ │ │ + * │ │ │ │ │ + * Valid options: │ │ │ │ │ + * uri - {String} Optional namespace uri for the element - supply a prefix │ │ │ │ │ + * instead if the namespace uri is a property of the format's namespace │ │ │ │ │ + * object. │ │ │ │ │ + * attributes - {Object} Optional attributes to be set using the │ │ │ │ │ + * method. │ │ │ │ │ + * value - {String} Optional text to be appended as a text node. │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {} │ │ │ │ │ + * {Element} An element node. │ │ │ │ │ */ │ │ │ │ │ - getTileSize: function() { │ │ │ │ │ - return this.tileSize; │ │ │ │ │ + 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; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: getBy │ │ │ │ │ - * Get a list of objects given a property and a match item. │ │ │ │ │ + * Method: setAttributes │ │ │ │ │ + * Set multiple attributes given key value pairs from an object. │ │ │ │ │ * │ │ │ │ │ * 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. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - 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; │ │ │ │ │ + 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); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: getLayersBy │ │ │ │ │ - * Get a list of layers with properties matching the given criteria. │ │ │ │ │ + * 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: │ │ │ │ │ - * 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. │ │ │ │ │ + * node - {DOMElement} The node to be read (required). │ │ │ │ │ + * obj - {Object} The object to be modified (optional). │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {Array()} A list of layers matching the given criteria. │ │ │ │ │ - * An empty array is returned if no matches are found. │ │ │ │ │ + * {Object} The input object, modified (or a new one if none was provided). │ │ │ │ │ */ │ │ │ │ │ - getLayersBy: function(property, match) { │ │ │ │ │ - return this.getBy("layers", property, match); │ │ │ │ │ + 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; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: getLayersByName │ │ │ │ │ - * Get a list of layers with names matching the given name. │ │ │ │ │ + * Method: readChildNodes │ │ │ │ │ + * Shorthand for applying the named readers to all children of a node. │ │ │ │ │ + * For each child of type 1 (element), is called. │ │ │ │ │ * │ │ │ │ │ * 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. │ │ │ │ │ + * node - {DOMElement} The node to be read (required). │ │ │ │ │ + * obj - {Object} The object to be modified (optional). │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {Array()} A list of layers matching the given name. │ │ │ │ │ - * An empty array is returned if no matches are found. │ │ │ │ │ + * {Object} The input object, modified. │ │ │ │ │ */ │ │ │ │ │ - getLayersByName: function(match) { │ │ │ │ │ - return this.getLayersBy("name", match); │ │ │ │ │ + readChildNodes: function(node, obj) { │ │ │ │ │ + if (!obj) { │ │ │ │ │ + obj = {}; │ │ │ │ │ + } │ │ │ │ │ + var children = node.childNodes; │ │ │ │ │ + var child; │ │ │ │ │ + for (var i = 0, len = children.length; i < len; ++i) { │ │ │ │ │ + child = children[i]; │ │ │ │ │ + if (child.nodeType == 1) { │ │ │ │ │ + this.readNode(child, obj); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + return obj; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: getLayersByClass │ │ │ │ │ - * Get a list of layers of a given class (CLASS_NAME). │ │ │ │ │ + * Method: writeNode │ │ │ │ │ + * Shorthand for applying one of the named writers and appending the │ │ │ │ │ + * results to a node. If a qualified name is not provided for the │ │ │ │ │ + * second argument (and a local name is used instead), the namespace │ │ │ │ │ + * of the parent node will be assumed. │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * 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. │ │ │ │ │ + * name - {String} The name of a node to generate. If a qualified name │ │ │ │ │ + * (e.g. "pre:Name") is used, the namespace prefix is assumed to be │ │ │ │ │ + * in the 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: │ │ │ │ │ - * {Array()} A list of layers matching the given class. │ │ │ │ │ - * An empty array is returned if no matches are found. │ │ │ │ │ + * {DOMElement} The child node. │ │ │ │ │ */ │ │ │ │ │ - getLayersByClass: function(match) { │ │ │ │ │ - return this.getLayersBy("CLASS_NAME", match); │ │ │ │ │ + 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: getControlsBy │ │ │ │ │ - * Get a list of controls with properties matching the given criteria. │ │ │ │ │ + * APIMethod: getChildEl │ │ │ │ │ + * Get the first child element. Optionally only return the first child │ │ │ │ │ + * if it matches the given name and namespace URI. │ │ │ │ │ * │ │ │ │ │ * 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. │ │ │ │ │ + * node - {DOMElement} The parent node. │ │ │ │ │ + * name - {String} Optional node name (local) to search for. │ │ │ │ │ + * uri - {String} Optional namespace URI to search for. │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {Array()} A list of controls matching the given │ │ │ │ │ - * criteria. An empty array is returned if no matches are found. │ │ │ │ │ + * {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. │ │ │ │ │ */ │ │ │ │ │ - getControlsBy: function(property, match) { │ │ │ │ │ - return this.getBy("controls", property, match); │ │ │ │ │ + getChildEl: function(node, name, uri) { │ │ │ │ │ + return node && this.getThisOrNextEl(node.firstChild, name, uri); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: getControlsByClass │ │ │ │ │ - * Get a list of controls of a given class (CLASS_NAME). │ │ │ │ │ + * APIMethod: getNextEl │ │ │ │ │ + * Get the next sibling element. Optionally get the first sibling only │ │ │ │ │ + * if it matches the given local name and namespace URI. │ │ │ │ │ * │ │ │ │ │ * 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. │ │ │ │ │ + * 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: │ │ │ │ │ - * {Array()} A list of controls matching the given class. │ │ │ │ │ - * An empty array is returned if no matches are found. │ │ │ │ │ + * {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. │ │ │ │ │ */ │ │ │ │ │ - getControlsByClass: function(match) { │ │ │ │ │ - return this.getControlsBy("CLASS_NAME", match); │ │ │ │ │ + getNextEl: function(node, name, uri) { │ │ │ │ │ + return node && this.getThisOrNextEl(node.nextSibling, name, uri); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /********************************************************/ │ │ │ │ │ - /* */ │ │ │ │ │ - /* 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 │ │ │ │ │ + * Method: getThisOrNextEl │ │ │ │ │ + * Return this node or the next element node. Optionally get the first │ │ │ │ │ + * sibling with the given local name or namespace URI. │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * id - {String} A layer id │ │ │ │ │ + * 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: │ │ │ │ │ - * {} The Layer with the corresponding id from the map's │ │ │ │ │ - * layer collection, or null if not found. │ │ │ │ │ + * {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. │ │ │ │ │ */ │ │ │ │ │ - getLayer: function(id) { │ │ │ │ │ - var foundLayer = null; │ │ │ │ │ - for (var i = 0, len = this.layers.length; i < len; i++) { │ │ │ │ │ - var layer = this.layers[i]; │ │ │ │ │ - if (layer.id == id) { │ │ │ │ │ - foundLayer = layer; │ │ │ │ │ - break; │ │ │ │ │ - } │ │ │ │ │ + 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 foundLayer; │ │ │ │ │ + return sibling || null; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: setLayerZIndex │ │ │ │ │ - * │ │ │ │ │ + * 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: │ │ │ │ │ - * layer - {} │ │ │ │ │ - * zIdx - {int} │ │ │ │ │ - */ │ │ │ │ │ - setLayerZIndex: function(layer, zIdx) { │ │ │ │ │ - layer.setZIndex( │ │ │ │ │ - this.Z_INDEX_BASE[layer.isBaseLayer ? 'BaseLayer' : 'Overlay'] + │ │ │ │ │ - zIdx * 5); │ │ │ │ │ - }, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Method: resetLayersZIndex │ │ │ │ │ - * Reset each layer's z-index based on layer's array index │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - resetLayersZIndex: function() { │ │ │ │ │ - for (var i = 0, len = this.layers.length; i < len; i++) { │ │ │ │ │ - var layer = this.layers[i]; │ │ │ │ │ - this.setLayerZIndex(layer, i); │ │ │ │ │ + lookupNamespaceURI: function(node, prefix) { │ │ │ │ │ + var uri = null; │ │ │ │ │ + if (node) { │ │ │ │ │ + if (node.lookupNamespaceURI) { │ │ │ │ │ + uri = node.lookupNamespaceURI(prefix); │ │ │ │ │ + } else { │ │ │ │ │ + outer: switch (node.nodeType) { │ │ │ │ │ + case 1: // ELEMENT_NODE │ │ │ │ │ + if (node.namespaceURI !== null && node.prefix === prefix) { │ │ │ │ │ + uri = node.namespaceURI; │ │ │ │ │ + break outer; │ │ │ │ │ + } │ │ │ │ │ + var len = node.attributes.length; │ │ │ │ │ + if (len) { │ │ │ │ │ + var attr; │ │ │ │ │ + for (var i = 0; i < len; ++i) { │ │ │ │ │ + attr = node.attributes[i]; │ │ │ │ │ + if (attr.prefix === "xmlns" && attr.name === "xmlns:" + prefix) { │ │ │ │ │ + uri = attr.value || null; │ │ │ │ │ + break outer; │ │ │ │ │ + } else if (attr.name === "xmlns" && prefix === null) { │ │ │ │ │ + uri = attr.value || null; │ │ │ │ │ + break outer; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + uri = this.lookupNamespaceURI(node.parentNode, prefix); │ │ │ │ │ + break outer; │ │ │ │ │ + case 2: // ATTRIBUTE_NODE │ │ │ │ │ + uri = this.lookupNamespaceURI(node.ownerElement, prefix); │ │ │ │ │ + break outer; │ │ │ │ │ + case 9: // DOCUMENT_NODE │ │ │ │ │ + uri = this.lookupNamespaceURI(node.documentElement, prefix); │ │ │ │ │ + break outer; │ │ │ │ │ + case 6: // ENTITY_NODE │ │ │ │ │ + case 12: // NOTATION_NODE │ │ │ │ │ + case 10: // DOCUMENT_TYPE_NODE │ │ │ │ │ + case 11: // DOCUMENT_FRAGMENT_NODE │ │ │ │ │ + break outer; │ │ │ │ │ + default: │ │ │ │ │ + // TEXT_NODE (3), CDATA_SECTION_NODE (4), ENTITY_REFERENCE_NODE (5), │ │ │ │ │ + // PROCESSING_INSTRUCTION_NODE (7), COMMENT_NODE (8) │ │ │ │ │ + uri = this.lookupNamespaceURI(node.parentNode, prefix); │ │ │ │ │ + break outer; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ } │ │ │ │ │ + return uri; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: addLayer │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * layer - {} │ │ │ │ │ + * Method: getXMLDoc │ │ │ │ │ + * Get an XML document for nodes that are not supported in HTML (e.g. │ │ │ │ │ + * createCDATASection). On IE, this will either return an existing or │ │ │ │ │ + * create a new on the instance. On other browsers, this will │ │ │ │ │ + * either return an existing or create a new shared document (see │ │ │ │ │ + * ). │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {Boolean} True if the layer has been added to the map. │ │ │ │ │ + * {XMLDocument} │ │ │ │ │ */ │ │ │ │ │ - addLayer: function(layer) { │ │ │ │ │ - for (var i = 0, len = this.layers.length; i < len; i++) { │ │ │ │ │ - if (this.layers[i] == layer) { │ │ │ │ │ - return false; │ │ │ │ │ + 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"); │ │ │ │ │ } │ │ │ │ │ } │ │ │ │ │ - if (this.events.triggerEvent("preaddlayer", { │ │ │ │ │ - layer: layer │ │ │ │ │ - }) === false) { │ │ │ │ │ - return false; │ │ │ │ │ - } │ │ │ │ │ - if (this.allOverlays) { │ │ │ │ │ - layer.isBaseLayer = false; │ │ │ │ │ - } │ │ │ │ │ + return OpenLayers.Format.XML.document || this.xmldom; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - layer.div.className = "olLayerDiv"; │ │ │ │ │ - layer.div.style.overflow = ""; │ │ │ │ │ - this.setLayerZIndex(layer, this.layers.length); │ │ │ │ │ + CLASS_NAME: "OpenLayers.Format.XML" │ │ │ │ │ │ │ │ │ │ - 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(); │ │ │ │ │ - } │ │ │ │ │ +OpenLayers.Format.XML.CONTENT_TYPE = { │ │ │ │ │ + EMPTY: 0, │ │ │ │ │ + SIMPLE: 1, │ │ │ │ │ + COMPLEX: 2, │ │ │ │ │ + MIXED: 3 │ │ │ │ │ +}; │ │ │ │ │ │ │ │ │ │ - this.events.triggerEvent("addlayer", { │ │ │ │ │ - layer: layer │ │ │ │ │ - }); │ │ │ │ │ - layer.events.triggerEvent("added", { │ │ │ │ │ - map: this, │ │ │ │ │ - layer: layer │ │ │ │ │ - }); │ │ │ │ │ - layer.afterAdd(); │ │ │ │ │ +/** │ │ │ │ │ + * 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 │ │ │ │ │ +); │ │ │ │ │ │ │ │ │ │ - return true; │ │ │ │ │ - }, │ │ │ │ │ +/** │ │ │ │ │ + * Property: OpenLayers.Format.XML.document │ │ │ │ │ + * {XMLDocument} XML document to reuse for creating non-HTML compliant nodes, │ │ │ │ │ + * like document.createCDATASection. │ │ │ │ │ + */ │ │ │ │ │ +OpenLayers.Format.XML.document = null; │ │ │ │ │ +/* ====================================================================== │ │ │ │ │ + OpenLayers/Format/OGCExceptionReport.js │ │ │ │ │ + ====================================================================== */ │ │ │ │ │ + │ │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ + * full text of the license. */ │ │ │ │ │ + │ │ │ │ │ +/** │ │ │ │ │ + * @requires OpenLayers/Format/XML.js │ │ │ │ │ + */ │ │ │ │ │ + │ │ │ │ │ +/** │ │ │ │ │ + * Class: OpenLayers.Format.OGCExceptionReport │ │ │ │ │ + * Class to read exception reports for various OGC services and versions. │ │ │ │ │ + * │ │ │ │ │ + * Inherits from: │ │ │ │ │ + * - │ │ │ │ │ + */ │ │ │ │ │ +OpenLayers.Format.OGCExceptionReport = OpenLayers.Class(OpenLayers.Format.XML, { │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: addLayers │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * layers - {Array()} │ │ │ │ │ + * Property: namespaces │ │ │ │ │ + * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ │ */ │ │ │ │ │ - addLayers: function(layers) { │ │ │ │ │ - for (var i = 0, len = layers.length; i < len; i++) { │ │ │ │ │ - this.addLayer(layers[i]); │ │ │ │ │ - } │ │ │ │ │ + namespaces: { │ │ │ │ │ + ogc: "http://www.opengis.net/ogc" │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIMethod: removeLayer │ │ │ │ │ - * Removes a layer from the map by removing its visual element (the │ │ │ │ │ - * layer.div property), then removing it from the map's internal list │ │ │ │ │ - * of layers, setting the layer's map property to null. │ │ │ │ │ - * │ │ │ │ │ - * a "removelayer" event is triggered. │ │ │ │ │ - * │ │ │ │ │ - * very worthy of mention is that simply removing a layer from a map │ │ │ │ │ - * will not cause the removal of any popups which may have been created │ │ │ │ │ - * by the layer. this is due to the fact that it was decided at some │ │ │ │ │ - * point that popups would not belong to layers. thus there is no way │ │ │ │ │ - * for us to know here to which layer the popup belongs. │ │ │ │ │ - * │ │ │ │ │ - * A simple solution to this is simply to call destroy() on the layer. │ │ │ │ │ - * the default OpenLayers.Layer class's destroy() function │ │ │ │ │ - * automatically takes care to remove itself from whatever map it has │ │ │ │ │ - * been attached to. │ │ │ │ │ - * │ │ │ │ │ - * The correct solution is for the layer itself to register an │ │ │ │ │ - * event-handler on "removelayer" and when it is called, if it │ │ │ │ │ - * recognizes itself as the layer being removed, then it cycles through │ │ │ │ │ - * its own personal list of popups, removing them from the map. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * layer - {} │ │ │ │ │ - * setNewBaseLayer - {Boolean} Default is true │ │ │ │ │ + /** │ │ │ │ │ + * Property: regExes │ │ │ │ │ + * Compiled regular expressions for manipulating strings. │ │ │ │ │ */ │ │ │ │ │ - removeLayer: function(layer, setNewBaseLayer) { │ │ │ │ │ - if (this.events.triggerEvent("preremovelayer", { │ │ │ │ │ - layer: layer │ │ │ │ │ - }) === false) { │ │ │ │ │ - return; │ │ │ │ │ - } │ │ │ │ │ - if (setNewBaseLayer == null) { │ │ │ │ │ - setNewBaseLayer = true; │ │ │ │ │ - } │ │ │ │ │ - │ │ │ │ │ - if (layer.isFixed) { │ │ │ │ │ - this.viewPortDiv.removeChild(layer.div); │ │ │ │ │ - } else { │ │ │ │ │ - this.layerContainerDiv.removeChild(layer.div); │ │ │ │ │ - } │ │ │ │ │ - OpenLayers.Util.removeItem(this.layers, layer); │ │ │ │ │ - layer.removeMap(this); │ │ │ │ │ - layer.map = null; │ │ │ │ │ - │ │ │ │ │ - // if we removed the base layer, need to set a new one │ │ │ │ │ - if (this.baseLayer == layer) { │ │ │ │ │ - this.baseLayer = null; │ │ │ │ │ - if (setNewBaseLayer) { │ │ │ │ │ - for (var i = 0, len = this.layers.length; i < len; i++) { │ │ │ │ │ - var iLayer = this.layers[i]; │ │ │ │ │ - if (iLayer.isBaseLayer || this.allOverlays) { │ │ │ │ │ - this.setBaseLayer(iLayer); │ │ │ │ │ - break; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - │ │ │ │ │ - this.resetLayersZIndex(); │ │ │ │ │ - │ │ │ │ │ - this.events.triggerEvent("removelayer", { │ │ │ │ │ - layer: layer │ │ │ │ │ - }); │ │ │ │ │ - layer.events.triggerEvent("removed", { │ │ │ │ │ - map: this, │ │ │ │ │ - layer: layer │ │ │ │ │ - }); │ │ │ │ │ + regExes: { │ │ │ │ │ + trimSpace: (/^\s*|\s*$/g), │ │ │ │ │ + removeSpace: (/\s*/g), │ │ │ │ │ + splitSpace: (/\s+/), │ │ │ │ │ + trimComma: (/\s*,\s*/g) │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: getNumLayers │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Int} The number of layers attached to the map. │ │ │ │ │ + * Property: defaultPrefix │ │ │ │ │ */ │ │ │ │ │ - getNumLayers: function() { │ │ │ │ │ - return this.layers.length; │ │ │ │ │ - }, │ │ │ │ │ + defaultPrefix: "ogc", │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIMethod: getLayerIndex │ │ │ │ │ + /** │ │ │ │ │ + * Constructor: OpenLayers.Format.OGCExceptionReport │ │ │ │ │ + * Create a new parser for OGC exception reports. │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * layer - {} │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Integer} The current (zero-based) index of the given layer in the map's │ │ │ │ │ - * layer stack. Returns -1 if the layer isn't on the map. │ │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ │ + * this instance. │ │ │ │ │ */ │ │ │ │ │ - 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. │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: read │ │ │ │ │ + * Read OGC exception report data from a string, and return an object with │ │ │ │ │ + * information about the exceptions. │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * layer - {} │ │ │ │ │ - * idx - {int} │ │ │ │ │ + * data - {String} or {DOMElement} data to read/parse. │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Object} Information about the exceptions that occurred. │ │ │ │ │ */ │ │ │ │ │ - setLayerIndex: function(layer, idx) { │ │ │ │ │ - var base = this.getLayerIndex(layer); │ │ │ │ │ - if (idx < 0) { │ │ │ │ │ - idx = 0; │ │ │ │ │ - } else if (idx > this.layers.length) { │ │ │ │ │ - idx = this.layers.length; │ │ │ │ │ + read: function(data) { │ │ │ │ │ + var result; │ │ │ │ │ + if (typeof data == "string") { │ │ │ │ │ + data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); │ │ │ │ │ } │ │ │ │ │ - if (base != idx) { │ │ │ │ │ - this.layers.splice(base, 1); │ │ │ │ │ - this.layers.splice(idx, 0, layer); │ │ │ │ │ - for (var i = 0, len = this.layers.length; i < len; i++) { │ │ │ │ │ - this.setLayerZIndex(this.layers[i], i); │ │ │ │ │ - } │ │ │ │ │ - this.events.triggerEvent("changelayer", { │ │ │ │ │ - layer: layer, │ │ │ │ │ - property: "order" │ │ │ │ │ - }); │ │ │ │ │ - if (this.allOverlays) { │ │ │ │ │ - if (idx === 0) { │ │ │ │ │ - this.setBaseLayer(layer); │ │ │ │ │ - } else if (this.baseLayer !== this.layers[0]) { │ │ │ │ │ - this.setBaseLayer(this.layers[0]); │ │ │ │ │ - } │ │ │ │ │ + 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; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIMethod: raiseLayer │ │ │ │ │ - * Change the index of the given layer by delta. If delta is positive, │ │ │ │ │ - * the layer is moved up the map's layer stack; if delta is negative, │ │ │ │ │ - * the layer is moved down. Again, note that this cannot (or at least │ │ │ │ │ - * should not) be effectively used to raise base layers above overlays. │ │ │ │ │ - * │ │ │ │ │ - * Paremeters: │ │ │ │ │ - * layer - {} │ │ │ │ │ - * delta - {int} │ │ │ │ │ + /** │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - raiseLayer: function(layer, delta) { │ │ │ │ │ - var idx = this.getLayerIndex(layer) + delta; │ │ │ │ │ - this.setLayerIndex(layer, idx); │ │ │ │ │ + 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); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIMethod: setBaseLayer │ │ │ │ │ - * Allows user to specify one of the currently-loaded layers as the Map's │ │ │ │ │ - * new base layer. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * newBaseLayer - {} │ │ │ │ │ - */ │ │ │ │ │ - setBaseLayer: function(newBaseLayer) { │ │ │ │ │ + CLASS_NAME: "OpenLayers.Format.OGCExceptionReport" │ │ │ │ │ │ │ │ │ │ - if (newBaseLayer != this.baseLayer) { │ │ │ │ │ +}); │ │ │ │ │ +/* ====================================================================== │ │ │ │ │ + OpenLayers/Format/XML/VersionedOGC.js │ │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ │ │ - // ensure newBaseLayer is already loaded │ │ │ │ │ - if (OpenLayers.Util.indexOf(this.layers, newBaseLayer) != -1) { │ │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ + * full text of the license. */ │ │ │ │ │ │ │ │ │ │ - // preserve center and scale when changing base layers │ │ │ │ │ - var center = this.getCachedCenter(); │ │ │ │ │ - var newResolution = OpenLayers.Util.getResolutionFromScale( │ │ │ │ │ - this.getScale(), newBaseLayer.units │ │ │ │ │ - ); │ │ │ │ │ +/** │ │ │ │ │ + * @requires OpenLayers/Format/XML.js │ │ │ │ │ + * @requires OpenLayers/Format/OGCExceptionReport.js │ │ │ │ │ + */ │ │ │ │ │ │ │ │ │ │ - // make the old base layer invisible │ │ │ │ │ - if (this.baseLayer != null && !this.allOverlays) { │ │ │ │ │ - this.baseLayer.setVisibility(false); │ │ │ │ │ - } │ │ │ │ │ +/** │ │ │ │ │ + * 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, { │ │ │ │ │ │ │ │ │ │ - // set new baselayer │ │ │ │ │ - this.baseLayer = newBaseLayer; │ │ │ │ │ + /** │ │ │ │ │ + * APIProperty: defaultVersion │ │ │ │ │ + * {String} Version number to assume if none found. │ │ │ │ │ + */ │ │ │ │ │ + defaultVersion: null, │ │ │ │ │ │ │ │ │ │ - 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(); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ + /** │ │ │ │ │ + * APIProperty: version │ │ │ │ │ + * {String} Specify a version string if one is known. │ │ │ │ │ + */ │ │ │ │ │ + version: null, │ │ │ │ │ │ │ │ │ │ - // 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); │ │ │ │ │ - } │ │ │ │ │ + /** │ │ │ │ │ + * APIProperty: profile │ │ │ │ │ + * {String} If provided, use a custom profile. │ │ │ │ │ + */ │ │ │ │ │ + profile: null, │ │ │ │ │ │ │ │ │ │ - this.events.triggerEvent("changebaselayer", { │ │ │ │ │ - layer: this.baseLayer │ │ │ │ │ - }); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - }, │ │ │ │ │ + /** │ │ │ │ │ + * 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, │ │ │ │ │ │ │ │ │ │ - /********************************************************/ │ │ │ │ │ - /* */ │ │ │ │ │ - /* Control Functions */ │ │ │ │ │ - /* */ │ │ │ │ │ - /* The following functions deal with adding and */ │ │ │ │ │ - /* removing Controls to and from the Map */ │ │ │ │ │ - /* */ │ │ │ │ │ - /********************************************************/ │ │ │ │ │ + /** │ │ │ │ │ + * APIProperty: stringifyOutput │ │ │ │ │ + * {Boolean} If true, write will return a string otherwise a DOMElement. │ │ │ │ │ + * Default is false. │ │ │ │ │ + */ │ │ │ │ │ + stringifyOutput: false, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: addControl │ │ │ │ │ - * Add the passed over control to the map. Optionally │ │ │ │ │ - * position the control at the given pixel. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * control - {} │ │ │ │ │ - * px - {} │ │ │ │ │ + * Property: parser │ │ │ │ │ + * {Object} Instance of the versioned parser. Cached for multiple read and │ │ │ │ │ + * write calls of the same version. │ │ │ │ │ */ │ │ │ │ │ - addControl: function(control, px) { │ │ │ │ │ - this.controls.push(control); │ │ │ │ │ - this.addControlToMap(control, px); │ │ │ │ │ - }, │ │ │ │ │ + parser: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ - * │ │ │ │ │ + * Constructor: OpenLayers.Format.XML.VersionedOGC. │ │ │ │ │ + * Constructor. │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * controls - {Array()} │ │ │ │ │ - * pixels - {Array()} │ │ │ │ │ + * options - {Object} Optional object whose properties will be set on │ │ │ │ │ + * the object. │ │ │ │ │ */ │ │ │ │ │ - addControls: function(controls, pixels) { │ │ │ │ │ - var pxs = (arguments.length === 1) ? [] : pixels; │ │ │ │ │ - for (var i = 0, len = controls.length; i < len; i++) { │ │ │ │ │ - var ctrl = controls[i]; │ │ │ │ │ - var px = (pxs[i]) ? pxs[i] : null; │ │ │ │ │ - this.addControl(ctrl, px); │ │ │ │ │ - } │ │ │ │ │ + initialize: function(options) { │ │ │ │ │ + OpenLayers.Format.XML.prototype.initialize.apply(this, [options]); │ │ │ │ │ + var className = this.CLASS_NAME; │ │ │ │ │ + this.name = className.substring(className.lastIndexOf(".") + 1); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: addControlToMap │ │ │ │ │ - * │ │ │ │ │ + * Method: getVersion │ │ │ │ │ + * Returns the version to use. Subclasses can override this function │ │ │ │ │ + * if a different version detection is needed. │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * │ │ │ │ │ - * control - {} │ │ │ │ │ - * px - {} │ │ │ │ │ + * root - {DOMElement} │ │ │ │ │ + * options - {Object} Optional configuration object. │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {String} The version to use. │ │ │ │ │ */ │ │ │ │ │ - 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); │ │ │ │ │ + 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; │ │ │ │ │ } │ │ │ │ │ - if (control.autoActivate) { │ │ │ │ │ - control.activate(); │ │ │ │ │ - } │ │ │ │ │ + return version; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: getControl │ │ │ │ │ - * │ │ │ │ │ + * Method: getParser │ │ │ │ │ + * Get an instance of the cached parser if available, otherwise create one. │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * id - {String} ID of the control to return. │ │ │ │ │ - * │ │ │ │ │ + * version - {String} │ │ │ │ │ + * │ │ │ │ │ * 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 < len; i++) { │ │ │ │ │ - var control = this.controls[i]; │ │ │ │ │ - if (control.id == id) { │ │ │ │ │ - returnControl = control; │ │ │ │ │ - break; │ │ │ │ │ + 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 returnControl; │ │ │ │ │ + return this.parser; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIMethod: removeControl │ │ │ │ │ - * Remove a control from the map. Removes the control both from the map │ │ │ │ │ - * object's internal array of controls, as well as from the map's │ │ │ │ │ - * viewPort (assuming the control was not added outsideViewport) │ │ │ │ │ - * │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: write │ │ │ │ │ + * Write a document. │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * control - {} The control to remove. │ │ │ │ │ + * obj - {Object} An object representing the document. │ │ │ │ │ + * options - {Object} Optional configuration object. │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {String} The document as a string │ │ │ │ │ */ │ │ │ │ │ - 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); │ │ │ │ │ + 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]); │ │ │ │ │ } │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /********************************************************/ │ │ │ │ │ - /* */ │ │ │ │ │ - /* Popup Functions */ │ │ │ │ │ - /* */ │ │ │ │ │ - /* The following functions deal with adding and */ │ │ │ │ │ - /* removing Popups to and from the Map */ │ │ │ │ │ - /* */ │ │ │ │ │ - /********************************************************/ │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * APIMethod: addPopup │ │ │ │ │ - * │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: read │ │ │ │ │ + * Read a doc and return an object representing the document. │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * popup - {} │ │ │ │ │ - * exclusive - {Boolean} If true, closes all other popups first │ │ │ │ │ + * data - {String | DOMElement} Data to read. │ │ │ │ │ + * options - {Object} Options for the reader. │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Object} An object representing the document. │ │ │ │ │ */ │ │ │ │ │ - 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]); │ │ │ │ │ - } │ │ │ │ │ + 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 │ │ │ │ │ │ │ │ │ │ - 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); │ │ │ │ │ + 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; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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; │ │ │ │ │ - }, │ │ │ │ │ + CLASS_NAME: "OpenLayers.Format.XML.VersionedOGC" │ │ │ │ │ +}); │ │ │ │ │ +/* ====================================================================== │ │ │ │ │ + OpenLayers/Format/OWSCommon.js │ │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ │ │ - /********************************************************/ │ │ │ │ │ - /* */ │ │ │ │ │ - /* Container Div Functions */ │ │ │ │ │ - /* */ │ │ │ │ │ - /* The following functions deal with the access to */ │ │ │ │ │ - /* and maintenance of the size of the container div */ │ │ │ │ │ - /* */ │ │ │ │ │ - /********************************************************/ │ │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ + * full list of contributors). Published 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, { │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * APIProperty: defaultVersion │ │ │ │ │ + * {String} Version number to assume if none found. Default is "1.0.0". │ │ │ │ │ */ │ │ │ │ │ - getSize: function() { │ │ │ │ │ - var size = null; │ │ │ │ │ - if (this.size != null) { │ │ │ │ │ - size = this.size.clone(); │ │ │ │ │ - } │ │ │ │ │ - return size; │ │ │ │ │ - }, │ │ │ │ │ + defaultVersion: "1.0.0", │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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) │ │ │ │ │ + * Constructor: OpenLayers.Format.OWSCommon │ │ │ │ │ + * Create a new parser for OWSCommon. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ │ + * this instance. │ │ │ │ │ */ │ │ │ │ │ - updateSize: function() { │ │ │ │ │ - // the div might have moved on the page, also │ │ │ │ │ - var newSize = this.getCurrentSize(); │ │ │ │ │ - if (newSize && !isNaN(newSize.h) && !isNaN(newSize.w)) { │ │ │ │ │ - this.events.clearMouseCache(); │ │ │ │ │ - var oldSize = this.getSize(); │ │ │ │ │ - if (oldSize == null) { │ │ │ │ │ - this.size = oldSize = newSize; │ │ │ │ │ - } │ │ │ │ │ - if (!newSize.equals(oldSize)) { │ │ │ │ │ - │ │ │ │ │ - // store the new size │ │ │ │ │ - this.size = newSize; │ │ │ │ │ - │ │ │ │ │ - //notify layers of mapresize │ │ │ │ │ - for (var i = 0, len = this.layers.length; i < len; i++) { │ │ │ │ │ - this.layers[i].onMapResize(); │ │ │ │ │ - } │ │ │ │ │ - │ │ │ │ │ - var center = this.getCachedCenter(); │ │ │ │ │ - │ │ │ │ │ - if (this.baseLayer != null && center != null) { │ │ │ │ │ - var zoom = this.getZoom(); │ │ │ │ │ - this.zoom = null; │ │ │ │ │ - this.setCenter(center, zoom); │ │ │ │ │ - } │ │ │ │ │ - │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - this.events.triggerEvent("updatesize"); │ │ │ │ │ - }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: getCurrentSize │ │ │ │ │ - * │ │ │ │ │ + * 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: │ │ │ │ │ - * {} A new object with the dimensions │ │ │ │ │ - * of the map div │ │ │ │ │ + * {String} The version to use. │ │ │ │ │ */ │ │ │ │ │ - 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); │ │ │ │ │ + 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 size; │ │ │ │ │ + return version; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: calculateBounds │ │ │ │ │ - * │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: read │ │ │ │ │ + * Read an OWSCommon document and return an object. │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * center - {} Default is this.getCenter() │ │ │ │ │ - * resolution - {float} Default is this.getResolution() │ │ │ │ │ - * │ │ │ │ │ + * data - {String | DOMElement} Data to read. │ │ │ │ │ + * options - {Object} Options for the reader. │ │ │ │ │ + * │ │ │ │ │ * Returns: │ │ │ │ │ - * {} A bounds based on resolution, center, and │ │ │ │ │ - * current mapsize. │ │ │ │ │ + * {Object} An object representing the structure of the document. │ │ │ │ │ */ │ │ │ │ │ - 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; │ │ │ │ │ + CLASS_NAME: "OpenLayers.Format.OWSCommon" │ │ │ │ │ +}); │ │ │ │ │ +/* ====================================================================== │ │ │ │ │ + OpenLayers/Format/OWSCommon/v1.js │ │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ │ │ - extent = new OpenLayers.Bounds(center.lon - halfWDeg, │ │ │ │ │ - center.lat - halfHDeg, │ │ │ │ │ - center.lon + halfWDeg, │ │ │ │ │ - center.lat + halfHDeg); │ │ │ │ │ - } │ │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ + * full text of the license. */ │ │ │ │ │ │ │ │ │ │ - return extent; │ │ │ │ │ - }, │ │ │ │ │ +/** │ │ │ │ │ + * @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, { │ │ │ │ │ │ │ │ │ │ - /********************************************************/ │ │ │ │ │ - /* */ │ │ │ │ │ - /* 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: │ │ │ │ │ - * {} │ │ │ │ │ + * Property: regExes │ │ │ │ │ + * Compiled regular expressions for manipulating strings. │ │ │ │ │ */ │ │ │ │ │ - getCenter: function() { │ │ │ │ │ - var center = null; │ │ │ │ │ - var cachedCenter = this.getCachedCenter(); │ │ │ │ │ - if (cachedCenter) { │ │ │ │ │ - center = cachedCenter.clone(); │ │ │ │ │ - } │ │ │ │ │ - return center; │ │ │ │ │ + regExes: { │ │ │ │ │ + trimSpace: (/^\s*|\s*$/g), │ │ │ │ │ + removeSpace: (/\s*/g), │ │ │ │ │ + splitSpace: (/\s+/), │ │ │ │ │ + trimComma: (/\s*,\s*/g) │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: getCachedCenter │ │ │ │ │ + * Method: read │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * data - {DOMElement} An OWSCommon document element. │ │ │ │ │ + * options - {Object} Options for the reader. │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {} │ │ │ │ │ + * {Object} An object representing the OWSCommon document. │ │ │ │ │ */ │ │ │ │ │ - getCachedCenter: function() { │ │ │ │ │ - if (!this.center && this.size) { │ │ │ │ │ - this.center = this.getLonLatFromViewPortPx({ │ │ │ │ │ - x: this.size.w / 2, │ │ │ │ │ - y: this.size.h / 2 │ │ │ │ │ - }); │ │ │ │ │ - } │ │ │ │ │ - return this.center; │ │ │ │ │ + read: function(data, options) { │ │ │ │ │ + options = OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ │ + var ows = {}; │ │ │ │ │ + this.readChildNodes(data, ows); │ │ │ │ │ + return ows; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: getZoom │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Integer} │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - getZoom: function() { │ │ │ │ │ - return this.zoom; │ │ │ │ │ + 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); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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. │ │ │ │ │ + /** │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - 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"); │ │ │ │ │ + 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; │ │ │ │ │ } │ │ │ │ │ } │ │ │ │ │ - │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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(); │ │ │ │ │ + CLASS_NAME: "OpenLayers.Format.OWSCommon.v1" │ │ │ │ │ │ │ │ │ │ - // center will not change, don't do nothing │ │ │ │ │ - if (lonlat.equals(center)) { │ │ │ │ │ - return; │ │ │ │ │ - } │ │ │ │ │ +}); │ │ │ │ │ +/* ====================================================================== │ │ │ │ │ + OpenLayers/Format/OWSCommon/v1_1_0.js │ │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ │ │ - 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 │ │ │ │ │ - }; │ │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ + * full text of the license. */ │ │ │ │ │ │ │ │ │ │ - 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); │ │ │ │ │ - } │ │ │ │ │ - }, │ │ │ │ │ +/** │ │ │ │ │ + * @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, { │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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 │ │ │ │ │ + * Property: namespaces │ │ │ │ │ + * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ │ */ │ │ │ │ │ - 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 │ │ │ │ │ - }); │ │ │ │ │ + namespaces: { │ │ │ │ │ + ows: "http://www.opengis.net/ows/1.1", │ │ │ │ │ + xlink: "http://www.w3.org/1999/xlink" │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: moveByPx │ │ │ │ │ - * Drag the map by pixels. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * dx - {Number} │ │ │ │ │ - * dy - {Number} │ │ │ │ │ + /** │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - moveByPx: function(dx, dy) { │ │ │ │ │ - var hw = this.size.w / 2; │ │ │ │ │ - var hh = this.size.h / 2; │ │ │ │ │ - var x = hw + dx; │ │ │ │ │ - var y = hh + dy; │ │ │ │ │ - var wrapDateLine = this.baseLayer.wrapDateLine; │ │ │ │ │ - var xRestriction = 0; │ │ │ │ │ - var yRestriction = 0; │ │ │ │ │ - if (this.restrictedExtent) { │ │ │ │ │ - xRestriction = hw; │ │ │ │ │ - yRestriction = hh; │ │ │ │ │ - // wrapping the date line makes no sense for restricted extents │ │ │ │ │ - wrapDateLine = false; │ │ │ │ │ - } │ │ │ │ │ - dx = wrapDateLine || │ │ │ │ │ - x <= this.maxPx.x - xRestriction && │ │ │ │ │ - x >= this.minPx.x + xRestriction ? Math.round(dx) : 0; │ │ │ │ │ - dy = y <= this.maxPx.y - yRestriction && │ │ │ │ │ - y >= this.minPx.y + yRestriction ? Math.round(dy) : 0; │ │ │ │ │ - if (dx || dy) { │ │ │ │ │ - if (!this.dragging) { │ │ │ │ │ - this.dragging = true; │ │ │ │ │ - this.events.triggerEvent("movestart"); │ │ │ │ │ - } │ │ │ │ │ - this.center = null; │ │ │ │ │ - if (dx) { │ │ │ │ │ - this.layerContainerOriginPx.x -= dx; │ │ │ │ │ - this.minPx.x -= dx; │ │ │ │ │ - this.maxPx.x -= dx; │ │ │ │ │ - } │ │ │ │ │ - if (dy) { │ │ │ │ │ - this.layerContainerOriginPx.y -= dy; │ │ │ │ │ - this.minPx.y -= dy; │ │ │ │ │ - this.maxPx.y -= dy; │ │ │ │ │ - } │ │ │ │ │ - this.applyTransform(); │ │ │ │ │ - var layer, i, len; │ │ │ │ │ - for (i = 0, len = this.layers.length; i < len; ++i) { │ │ │ │ │ - layer = this.layers[i]; │ │ │ │ │ - if (layer.visibility && │ │ │ │ │ - (layer === this.baseLayer || layer.inRange)) { │ │ │ │ │ - layer.moveByPx(dx, dy); │ │ │ │ │ - layer.events.triggerEvent("move"); │ │ │ │ │ - } │ │ │ │ │ + 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); │ │ │ │ │ } │ │ │ │ │ - this.events.triggerEvent("move"); │ │ │ │ │ - } │ │ │ │ │ + }, OpenLayers.Format.OWSCommon.v1.prototype.readers["ows"]) │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: adjustZoom │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * zoom - {Number} The zoom level to adjust │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Integer} Adjusted zoom level that shows a map not wider than its │ │ │ │ │ - * 's maxExtent. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - adjustZoom: function(zoom) { │ │ │ │ │ - if (this.baseLayer && this.baseLayer.wrapDateLine) { │ │ │ │ │ - var resolution, resolutions = this.baseLayer.resolutions, │ │ │ │ │ - maxResolution = this.getMaxExtent().getWidth() / this.size.w; │ │ │ │ │ - if (this.getResolutionForZoom(zoom) > maxResolution) { │ │ │ │ │ - if (this.fractionalZoom) { │ │ │ │ │ - zoom = this.getZoomForResolution(maxResolution); │ │ │ │ │ - } else { │ │ │ │ │ - for (var i = zoom | 0, ii = resolutions.length; i < ii; ++i) { │ │ │ │ │ - if (resolutions[i] <= maxResolution) { │ │ │ │ │ - zoom = i; │ │ │ │ │ - break; │ │ │ │ │ - } │ │ │ │ │ + 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; │ │ │ │ │ } │ │ │ │ │ - } │ │ │ │ │ - return zoom; │ │ │ │ │ + }, 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" │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: getMinZoom │ │ │ │ │ - * Returns the minimum zoom level for the current map view. If the base │ │ │ │ │ - * layer is configured with 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. │ │ │ │ │ + * Property: regExes │ │ │ │ │ + * Compiled regular expressions for manipulating strings. │ │ │ │ │ */ │ │ │ │ │ - getMinZoom: function() { │ │ │ │ │ - return this.adjustZoom(0); │ │ │ │ │ + regExes: { │ │ │ │ │ + trimSpace: (/^\s*|\s*$/g), │ │ │ │ │ + removeSpace: (/\s*/g), │ │ │ │ │ + splitSpace: (/\s+/), │ │ │ │ │ + trimComma: (/\s*,\s*/g) │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: moveTo │ │ │ │ │ + * 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: │ │ │ │ │ - * lonlat - {} │ │ │ │ │ - * zoom - {Integer} │ │ │ │ │ - * options - {Object} │ │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ │ + * this instance. │ │ │ │ │ */ │ │ │ │ │ - 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(); │ │ │ │ │ - } │ │ │ │ │ + /** │ │ │ │ │ + * 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]); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - 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); │ │ │ │ │ + /** │ │ │ │ │ + * 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); │ │ │ │ │ } │ │ │ │ │ - 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); │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "TemporalSubset": function(temporalSubset) { │ │ │ │ │ + var node = this.createElementNSPlus("wcs:TemporalSubset", {}); │ │ │ │ │ + for (var i = 0, len = temporalSubset.timePeriods.length; i < len; ++i) { │ │ │ │ │ + this.writeNode("wcs:TimePeriod", temporalSubset.timePeriods[i], node); │ │ │ │ │ } │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - │ │ │ │ │ - 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); │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "TimePeriod": function(timePeriod) { │ │ │ │ │ + var node = this.createElementNSPlus("wcs:TimePeriod", {}); │ │ │ │ │ + this.writeNode("wcs:BeginPosition", timePeriod.begin, node); │ │ │ │ │ + this.writeNode("wcs:EndPosition", timePeriod.end, node); │ │ │ │ │ + if (timePeriod.resolution) { │ │ │ │ │ + this.writeNode("wcs:TimeResolution", timePeriod.resolution, node); │ │ │ │ │ } │ │ │ │ │ - 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 │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "BeginPosition": function(begin) { │ │ │ │ │ + var node = this.createElementNSPlus("wcs:BeginPosition", { │ │ │ │ │ + value: begin │ │ │ │ │ + }); │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "EndPosition": function(end) { │ │ │ │ │ + var node = this.createElementNSPlus("wcs:EndPosition", { │ │ │ │ │ + value: end │ │ │ │ │ + }); │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "TimeResolution": function(resolution) { │ │ │ │ │ + var node = this.createElementNSPlus("wcs:TimeResolution", { │ │ │ │ │ + value: resolution │ │ │ │ │ + }); │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "Output": function(output) { │ │ │ │ │ + var node = this.createElementNSPlus("wcs:Output", { │ │ │ │ │ + attributes: { │ │ │ │ │ + format: output.format, │ │ │ │ │ + store: output.store │ │ │ │ │ + } │ │ │ │ │ + }); │ │ │ │ │ + if (output.gridCRS) { │ │ │ │ │ + this.writeNode("wcs:GridCRS", output.gridCRS, node); │ │ │ │ │ + } │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "GridCRS": function(gridCRS) { │ │ │ │ │ + var node = this.createElementNSPlus("wcs:GridCRS", {}); │ │ │ │ │ + this.writeNode("wcs:GridBaseCRS", gridCRS.baseCRS, node); │ │ │ │ │ + if (gridCRS.type) { │ │ │ │ │ + this.writeNode("wcs:GridType", gridCRS.type, node); │ │ │ │ │ + } │ │ │ │ │ + if (gridCRS.origin) { │ │ │ │ │ + this.writeNode("wcs:GridOrigin", gridCRS.origin, node); │ │ │ │ │ + } │ │ │ │ │ + this.writeNode("wcs:GridOffsets", gridCRS.offsets, node); │ │ │ │ │ + if (gridCRS.CS) { │ │ │ │ │ + this.writeNode("wcs:GridCS", gridCRS.CS, node); │ │ │ │ │ + } │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "GridBaseCRS": function(baseCRS) { │ │ │ │ │ + return this.createElementNSPlus("wcs:GridBaseCRS", { │ │ │ │ │ + value: baseCRS │ │ │ │ │ + }); │ │ │ │ │ + }, │ │ │ │ │ + "GridOrigin": function(origin) { │ │ │ │ │ + return this.createElementNSPlus("wcs:GridOrigin", { │ │ │ │ │ + value: origin │ │ │ │ │ + }); │ │ │ │ │ + }, │ │ │ │ │ + "GridType": function(type) { │ │ │ │ │ + return this.createElementNSPlus("wcs:GridType", { │ │ │ │ │ + value: type │ │ │ │ │ + }); │ │ │ │ │ + }, │ │ │ │ │ + "GridOffsets": function(offsets) { │ │ │ │ │ + return this.createElementNSPlus("wcs:GridOffsets", { │ │ │ │ │ + value: offsets │ │ │ │ │ + }); │ │ │ │ │ + }, │ │ │ │ │ + "GridCS": function(CS) { │ │ │ │ │ + return this.createElementNSPlus("wcs:GridCS", { │ │ │ │ │ + value: CS │ │ │ │ │ }); │ │ │ │ │ - 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) │ │ │ │ │ - }; │ │ │ │ │ } │ │ │ │ │ + }, │ │ │ │ │ + "ows": OpenLayers.Format.OWSCommon.v1_1_0.prototype.writers.ows │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - if (zoomChanged) { │ │ │ │ │ - this.zoom = zoom; │ │ │ │ │ - this.resolution = res; │ │ │ │ │ - } │ │ │ │ │ + CLASS_NAME: "OpenLayers.Format.WCSGetCoverage" │ │ │ │ │ │ │ │ │ │ - var bounds = this.getExtent(); │ │ │ │ │ +}); │ │ │ │ │ +/* ====================================================================== │ │ │ │ │ + OpenLayers/Format/WFST.js │ │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ │ │ - //send the move call to the baselayer and all the overlays │ │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ + * full text of the license. */ │ │ │ │ │ │ │ │ │ │ - if (this.baseLayer.visibility) { │ │ │ │ │ - this.baseLayer.moveTo(bounds, zoomChanged, options.dragging); │ │ │ │ │ - options.dragging || this.baseLayer.events.triggerEvent( │ │ │ │ │ - "moveend", { │ │ │ │ │ - zoomChanged: zoomChanged │ │ │ │ │ - } │ │ │ │ │ - ); │ │ │ │ │ - } │ │ │ │ │ +/** │ │ │ │ │ + * @requires OpenLayers/Format.js │ │ │ │ │ + */ │ │ │ │ │ │ │ │ │ │ - bounds = this.baseLayer.getExtent(); │ │ │ │ │ +/** │ │ │ │ │ + * 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); │ │ │ │ │ +}; │ │ │ │ │ │ │ │ │ │ - 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 │ │ │ │ │ - } │ │ │ │ │ - ); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ +/** │ │ │ │ │ + * Constant: OpenLayers.Format.WFST.DEFAULTS │ │ │ │ │ + * {Object} Default properties for the WFST format. │ │ │ │ │ + */ │ │ │ │ │ +OpenLayers.Format.WFST.DEFAULTS = { │ │ │ │ │ + "version": "1.0.0" │ │ │ │ │ +}; │ │ │ │ │ +/* ====================================================================== │ │ │ │ │ + OpenLayers/Filter.js │ │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ │ │ - this.events.triggerEvent("move"); │ │ │ │ │ - dragging || this.events.triggerEvent("moveend"); │ │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ + * full text of the license. */ │ │ │ │ │ │ │ │ │ │ - if (zoomChanged) { │ │ │ │ │ - //redraw popups │ │ │ │ │ - for (var i = 0, len = this.popups.length; i < len; i++) { │ │ │ │ │ - this.popups[i].updatePosition(); │ │ │ │ │ - } │ │ │ │ │ - this.events.triggerEvent("zoomend"); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: centerLayerContainer │ │ │ │ │ - * This function takes care to recenter the layerContainerDiv. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * lonlat - {} │ │ │ │ │ - */ │ │ │ │ │ - centerLayerContainer: function(lonlat) { │ │ │ │ │ - var originPx = this.getViewPortPxFromLonLat(this.layerContainerOrigin); │ │ │ │ │ - var newPx = this.getViewPortPxFromLonLat(lonlat); │ │ │ │ │ +/** │ │ │ │ │ + * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ │ + * @requires OpenLayers/Util.js │ │ │ │ │ + * @requires OpenLayers/Style.js │ │ │ │ │ + */ │ │ │ │ │ │ │ │ │ │ - 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; │ │ │ │ │ - } │ │ │ │ │ - }, │ │ │ │ │ +/** │ │ │ │ │ + * Class: OpenLayers.Filter │ │ │ │ │ + * This class represents an OGC Filter. │ │ │ │ │ + */ │ │ │ │ │ +OpenLayers.Filter = OpenLayers.Class({ │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: isValidZoomLevel │ │ │ │ │ - * │ │ │ │ │ + /** │ │ │ │ │ + * Constructor: OpenLayers.Filter │ │ │ │ │ + * This class represents a generic filter. │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * zoomLevel - {Integer} │ │ │ │ │ + * options - {Object} Optional object whose properties will be set on the │ │ │ │ │ + * instance. │ │ │ │ │ * │ │ │ │ │ * 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())); │ │ │ │ │ + initialize: function(options) { │ │ │ │ │ + OpenLayers.Util.extend(this, options); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: destroy │ │ │ │ │ + * Remove reference to anything added. │ │ │ │ │ + */ │ │ │ │ │ + destroy: function() {}, │ │ │ │ │ + │ │ │ │ │ /** │ │ │ │ │ - * Method: isValidLonLat │ │ │ │ │ + * APIMethod: evaluate │ │ │ │ │ + * Evaluates this filter in a specific context. Instances or subclasses │ │ │ │ │ + * are supposed to override this method. │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * lonlat - {} │ │ │ │ │ + * 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} Whether or not the lonlat passed in is non-null and within │ │ │ │ │ - * the maxExtent bounds │ │ │ │ │ + * {Boolean} The filter applies. │ │ │ │ │ */ │ │ │ │ │ - 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; │ │ │ │ │ + evaluate: function(context) { │ │ │ │ │ + return true; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /********************************************************/ │ │ │ │ │ - /* */ │ │ │ │ │ - /* 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. │ │ │ │ │ + * APIMethod: clone │ │ │ │ │ + * Clones this filter. Should be implemented by subclasses. │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {String} The Projection string from the base layer or null. │ │ │ │ │ + * {} Clone of this filter. │ │ │ │ │ */ │ │ │ │ │ - getProjection: function() { │ │ │ │ │ - var projection = this.getProjectionObject(); │ │ │ │ │ - return projection ? projection.getCode() : null; │ │ │ │ │ + clone: function() { │ │ │ │ │ + return null; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: getProjectionObject │ │ │ │ │ - * Returns the projection obect from the baselayer. │ │ │ │ │ + * APIMethod: toString │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {} The Projection of the base layer. │ │ │ │ │ + * {String} Include in your build to get a CQL │ │ │ │ │ + * representation of the filter returned. Otherwise "[Object object]" │ │ │ │ │ + * will be returned. │ │ │ │ │ */ │ │ │ │ │ - getProjectionObject: function() { │ │ │ │ │ - var projection = null; │ │ │ │ │ - if (this.baseLayer != null) { │ │ │ │ │ - projection = this.baseLayer.projection; │ │ │ │ │ + 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 projection; │ │ │ │ │ + return string; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIMethod: getMaxResolution │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {String} The Map's Maximum Resolution │ │ │ │ │ - */ │ │ │ │ │ - getMaxResolution: function() { │ │ │ │ │ - var maxResolution = null; │ │ │ │ │ - if (this.baseLayer != null) { │ │ │ │ │ - maxResolution = this.baseLayer.maxResolution; │ │ │ │ │ - } │ │ │ │ │ - return maxResolution; │ │ │ │ │ - }, │ │ │ │ │ + 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, { │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: getMaxExtent │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * options - {Object} │ │ │ │ │ - * │ │ │ │ │ - * Allowed Options: │ │ │ │ │ - * restricted - {Boolean} If true, returns restricted extent (if it is │ │ │ │ │ - * available.) │ │ │ │ │ + * APIProperty: type │ │ │ │ │ + * {String} Type of spatial filter. │ │ │ │ │ * │ │ │ │ │ - * 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). │ │ │ │ │ + * 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 │ │ │ │ │ */ │ │ │ │ │ - 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; │ │ │ │ │ - }, │ │ │ │ │ + type: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: getNumZoomLevels │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Integer} The total number of zoom levels that can be displayed by the │ │ │ │ │ - * current baseLayer. │ │ │ │ │ + * APIProperty: property │ │ │ │ │ + * {String} Name of the context property to compare. │ │ │ │ │ */ │ │ │ │ │ - getNumZoomLevels: function() { │ │ │ │ │ - var numZoomLevels = null; │ │ │ │ │ - if (this.baseLayer != null) { │ │ │ │ │ - numZoomLevels = this.baseLayer.numZoomLevels; │ │ │ │ │ - } │ │ │ │ │ - return numZoomLevels; │ │ │ │ │ - }, │ │ │ │ │ + property: null, │ │ │ │ │ │ │ │ │ │ - /********************************************************/ │ │ │ │ │ - /* */ │ │ │ │ │ - /* 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 */ │ │ │ │ │ - /* */ │ │ │ │ │ - /********************************************************/ │ │ │ │ │ + /** │ │ │ │ │ + * 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, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: getExtent │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {} A Bounds object which represents the lon/lat │ │ │ │ │ - * bounds of the current viewPort. │ │ │ │ │ - * If no baselayer is set, returns null. │ │ │ │ │ + * APIProperty: distance │ │ │ │ │ + * {Number} The distance to use in a DWithin spatial filter. │ │ │ │ │ */ │ │ │ │ │ - getExtent: function() { │ │ │ │ │ - var extent = null; │ │ │ │ │ - if (this.baseLayer != null) { │ │ │ │ │ - extent = this.baseLayer.getExtent(); │ │ │ │ │ - } │ │ │ │ │ - return extent; │ │ │ │ │ - }, │ │ │ │ │ + distance: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: getResolution │ │ │ │ │ + * 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: │ │ │ │ │ - * {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 │ │ │ │ │ + * Method: evaluate │ │ │ │ │ + * Evaluates this filter for a specific feature. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * feature - {} feature to apply the filter to. │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {Float} The current units of the map. │ │ │ │ │ - * If no baselayer is set, returns null. │ │ │ │ │ + * {Boolean} The feature meets filter criteria. │ │ │ │ │ */ │ │ │ │ │ - getUnits: function() { │ │ │ │ │ - var units = null; │ │ │ │ │ - if (this.baseLayer != null) { │ │ │ │ │ - units = this.baseLayer.units; │ │ │ │ │ + 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 units; │ │ │ │ │ + return intersect; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: getScale │ │ │ │ │ + * APIMethod: clone │ │ │ │ │ + * Clones this filter. │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {Float} The current scale denominator of the map. │ │ │ │ │ - * If no baselayer is set, returns null. │ │ │ │ │ + * {} Clone of this filter. │ │ │ │ │ */ │ │ │ │ │ - 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; │ │ │ │ │ + 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. */ │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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. │ │ │ │ │ + │ │ │ │ │ +/** │ │ │ │ │ + * @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: │ │ │ │ │ - * {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; │ │ │ │ │ + initialize: function(options) { │ │ │ │ │ + this.fids = []; │ │ │ │ │ + OpenLayers.Filter.prototype.initialize.apply(this, [options]); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: getResolutionForZoom │ │ │ │ │ + * APIMethod: evaluate │ │ │ │ │ + * evaluates this rule for a specific feature │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * zoom - {Float} │ │ │ │ │ + * feature - {} feature to apply the rule to. │ │ │ │ │ + * For vector features, the check is run against the fid, │ │ │ │ │ + * for plain features against the id. │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {Float} A suitable resolution for the specified zoom. If no baselayer │ │ │ │ │ - * is set, returns null. │ │ │ │ │ + * {Boolean} true if the rule applies, false if it does not │ │ │ │ │ */ │ │ │ │ │ - getResolutionForZoom: function(zoom) { │ │ │ │ │ - var resolution = null; │ │ │ │ │ - if (this.baseLayer) { │ │ │ │ │ - resolution = this.baseLayer.getResolutionForZoom(zoom); │ │ │ │ │ + evaluate: function(feature) { │ │ │ │ │ + for (var i = 0, len = this.fids.length; i < len; i++) { │ │ │ │ │ + var fid = feature.fid || feature.id; │ │ │ │ │ + if (fid == this.fids[i]) { │ │ │ │ │ + return true; │ │ │ │ │ + } │ │ │ │ │ } │ │ │ │ │ - return resolution; │ │ │ │ │ + return false; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * APIMethod: clone │ │ │ │ │ + * Clones this filter. │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {Integer} A suitable zoom level for the specified resolution. │ │ │ │ │ - * If no baselayer is set, returns null. │ │ │ │ │ + * {} Clone of this filter. │ │ │ │ │ */ │ │ │ │ │ - getZoomForResolution: function(resolution, closest) { │ │ │ │ │ - var zoom = null; │ │ │ │ │ - if (this.baseLayer != null) { │ │ │ │ │ - zoom = this.baseLayer.getZoomForResolution(resolution, closest); │ │ │ │ │ - } │ │ │ │ │ - return zoom; │ │ │ │ │ + clone: function() { │ │ │ │ │ + var filter = new OpenLayers.Filter.FeatureId(); │ │ │ │ │ + OpenLayers.Util.extend(filter, this); │ │ │ │ │ + filter.fids = this.fids.slice(); │ │ │ │ │ + return filter; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /********************************************************/ │ │ │ │ │ - /* */ │ │ │ │ │ - /* Zooming Functions */ │ │ │ │ │ - /* */ │ │ │ │ │ - /* The following functions, all publicly exposed */ │ │ │ │ │ - /* in the API, are all merely wrappers to the */ │ │ │ │ │ - /* the setCenter() function */ │ │ │ │ │ - /* */ │ │ │ │ │ - /********************************************************/ │ │ │ │ │ + CLASS_NAME: "OpenLayers.Filter.FeatureId" │ │ │ │ │ +}); │ │ │ │ │ +/* ====================================================================== │ │ │ │ │ + OpenLayers/Format/WFST/v1.js │ │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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 │ │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ + * full list of 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 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); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - }, │ │ │ │ │ +/** │ │ │ │ │ + * @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, { │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: zoomIn │ │ │ │ │ - * │ │ │ │ │ + * Property: namespaces │ │ │ │ │ + * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ │ */ │ │ │ │ │ - zoomIn: function() { │ │ │ │ │ - this.zoomTo(this.getZoom() + 1); │ │ │ │ │ + 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" │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: zoomOut │ │ │ │ │ - * │ │ │ │ │ + * Property: defaultPrefix │ │ │ │ │ */ │ │ │ │ │ - zoomOut: function() { │ │ │ │ │ - this.zoomTo(this.getZoom() - 1); │ │ │ │ │ - }, │ │ │ │ │ + defaultPrefix: "wfs", │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ - * │ │ │ │ │ + * Property: version │ │ │ │ │ + * {String} WFS version number. │ │ │ │ │ */ │ │ │ │ │ - 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(); │ │ │ │ │ + version: null, │ │ │ │ │ │ │ │ │ │ - //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)); │ │ │ │ │ - }, │ │ │ │ │ + /** │ │ │ │ │ + * Property: schemaLocation │ │ │ │ │ + * {String} Schema location for a particular minor version. │ │ │ │ │ + */ │ │ │ │ │ + schemaLocations: null, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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. │ │ │ │ │ + /** │ │ │ │ │ + * APIProperty: srsName │ │ │ │ │ + * {String} URI for spatial reference system. │ │ │ │ │ */ │ │ │ │ │ - zoomToMaxExtent: function(options) { │ │ │ │ │ - //restricted is true by default │ │ │ │ │ - var restricted = (options) ? options.restricted : true; │ │ │ │ │ + srsName: null, │ │ │ │ │ │ │ │ │ │ - var maxExtent = this.getMaxExtent({ │ │ │ │ │ - 'restricted': restricted │ │ │ │ │ - }); │ │ │ │ │ - this.zoomToExtent(maxExtent); │ │ │ │ │ - }, │ │ │ │ │ + /** │ │ │ │ │ + * APIProperty: extractAttributes │ │ │ │ │ + * {Boolean} Extract attributes from GML. Default is true. │ │ │ │ │ + */ │ │ │ │ │ + extractAttributes: true, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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. │ │ │ │ │ - * │ │ │ │ │ + /** │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - zoomToScale: function(scale, closest) { │ │ │ │ │ - var res = OpenLayers.Util.getResolutionFromScale(scale, │ │ │ │ │ - this.baseLayer.units); │ │ │ │ │ + xy: true, │ │ │ │ │ │ │ │ │ │ - var halfWDeg = (this.size.w * res) / 2; │ │ │ │ │ - var halfHDeg = (this.size.h * res) / 2; │ │ │ │ │ - var center = this.getCachedCenter(); │ │ │ │ │ + /** │ │ │ │ │ + * Property: stateName │ │ │ │ │ + * {Object} Maps feature states to node names. │ │ │ │ │ + */ │ │ │ │ │ + stateName: null, │ │ │ │ │ │ │ │ │ │ - var extent = new OpenLayers.Bounds(center.lon - halfWDeg, │ │ │ │ │ - center.lat - halfHDeg, │ │ │ │ │ - center.lon + halfWDeg, │ │ │ │ │ - center.lat + halfHDeg); │ │ │ │ │ - this.zoomToExtent(extent, closest); │ │ │ │ │ + /** │ │ │ │ │ + * 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]); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /********************************************************/ │ │ │ │ │ - /* */ │ │ │ │ │ - /* Translation Functions */ │ │ │ │ │ - /* */ │ │ │ │ │ - /* The following functions translate between */ │ │ │ │ │ - /* LonLat, LayerPx, and ViewPortPx */ │ │ │ │ │ - /* */ │ │ │ │ │ - /********************************************************/ │ │ │ │ │ - │ │ │ │ │ - // │ │ │ │ │ - // TRANSLATION: LonLat <-> ViewPortPx │ │ │ │ │ - // │ │ │ │ │ + /** │ │ │ │ │ + * 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; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: getLonLatFromViewPortPx │ │ │ │ │ - * │ │ │ │ │ + * 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: │ │ │ │ │ - * viewPortPx - {|Object} An OpenLayers.Pixel or │ │ │ │ │ - * an object with a 'x' │ │ │ │ │ - * and 'y' properties. │ │ │ │ │ - * │ │ │ │ │ + * 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: │ │ │ │ │ - * {} An OpenLayers.LonLat which is the passed-in view │ │ │ │ │ - * port , translated into lon/lat │ │ │ │ │ - * by the current base layer. │ │ │ │ │ + * {Array | Object} Output depending on the output option. │ │ │ │ │ */ │ │ │ │ │ - getLonLatFromViewPortPx: function(viewPortPx) { │ │ │ │ │ - var lonlat = null; │ │ │ │ │ - if (this.baseLayer != null) { │ │ │ │ │ - lonlat = this.baseLayer.getLonLatFromViewPortPx(viewPortPx); │ │ │ │ │ + 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]); │ │ │ │ │ } │ │ │ │ │ - return lonlat; │ │ │ │ │ + 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; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: getViewPortPxFromLonLat │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * lonlat - {} │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {} An OpenLayers.Pixel which is the passed-in │ │ │ │ │ - * , translated into view port │ │ │ │ │ - * pixels by the current base layer. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - getViewPortPxFromLonLat: function(lonlat) { │ │ │ │ │ - var px = null; │ │ │ │ │ - if (this.baseLayer != null) { │ │ │ │ │ - px = this.baseLayer.getViewPortPxFromLonLat(lonlat); │ │ │ │ │ + readers: { │ │ │ │ │ + "wfs": { │ │ │ │ │ + "FeatureCollection": function(node, obj) { │ │ │ │ │ + obj.features = []; │ │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ │ + } │ │ │ │ │ } │ │ │ │ │ - return px; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: getZoomTargetCenter │ │ │ │ │ + * 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: │ │ │ │ │ - * xy - {} The zoom origin pixel location on the screen │ │ │ │ │ - * resolution - {Float} The resolution we want to get the center for │ │ │ │ │ + * 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: │ │ │ │ │ - * {} The location of the map center after the │ │ │ │ │ - * transformation described by the origin xy and the target resolution. │ │ │ │ │ + * {String} A serialized WFS transaction. │ │ │ │ │ */ │ │ │ │ │ - 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 │ │ │ │ │ + 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 lonlat; │ │ │ │ │ + return OpenLayers.Format.XML.prototype.write.apply(this, [node]); │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Property: writers │ │ │ │ │ + * As a compliment to the readers property, this structure contains public │ │ │ │ │ + * writing functions grouped by namespace alias and named like the │ │ │ │ │ + * node names they produce. │ │ │ │ │ + */ │ │ │ │ │ + writers: { │ │ │ │ │ + "wfs": { │ │ │ │ │ + "GetFeature": function(options) { │ │ │ │ │ + var node = this.createElementNSPlus("wfs:GetFeature", { │ │ │ │ │ + attributes: { │ │ │ │ │ + service: "WFS", │ │ │ │ │ + version: this.version, │ │ │ │ │ + handle: options && options.handle, │ │ │ │ │ + outputFormat: options && options.outputFormat, │ │ │ │ │ + maxFeatures: options && options.maxFeatures, │ │ │ │ │ + "xsi:schemaLocation": this.schemaLocationAttr(options) │ │ │ │ │ + } │ │ │ │ │ + }); │ │ │ │ │ + if (typeof this.featureType == "string") { │ │ │ │ │ + this.writeNode("Query", options, node); │ │ │ │ │ + } else { │ │ │ │ │ + for (var i = 0, len = this.featureType.length; i < len; i++) { │ │ │ │ │ + options.featureType = this.featureType[i]; │ │ │ │ │ + this.writeNode("Query", options, node); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "Transaction": function(obj) { │ │ │ │ │ + obj = obj || {}; │ │ │ │ │ + var options = obj.options || {}; │ │ │ │ │ + var node = this.createElementNSPlus("wfs:Transaction", { │ │ │ │ │ + attributes: { │ │ │ │ │ + service: "WFS", │ │ │ │ │ + version: this.version, │ │ │ │ │ + handle: options.handle │ │ │ │ │ + } │ │ │ │ │ + }); │ │ │ │ │ + var i, len; │ │ │ │ │ + var features = obj.features; │ │ │ │ │ + if (features) { │ │ │ │ │ + // temporarily re-assigning geometry types │ │ │ │ │ + if (options.multi === true) { │ │ │ │ │ + OpenLayers.Util.extend(this.geometryTypes, { │ │ │ │ │ + "OpenLayers.Geometry.Point": "MultiPoint", │ │ │ │ │ + "OpenLayers.Geometry.LineString": (this.multiCurve === true) ? "MultiCurve" : "MultiLineString", │ │ │ │ │ + "OpenLayers.Geometry.Polygon": (this.multiSurface === true) ? "MultiSurface" : "MultiPolygon" │ │ │ │ │ + }); │ │ │ │ │ + } │ │ │ │ │ + var name, feature; │ │ │ │ │ + for (i = 0, len = features.length; i < len; ++i) { │ │ │ │ │ + feature = features[i]; │ │ │ │ │ + name = this.stateName[feature.state]; │ │ │ │ │ + if (name) { │ │ │ │ │ + this.writeNode(name, { │ │ │ │ │ + feature: feature, │ │ │ │ │ + options: options │ │ │ │ │ + }, node); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + // switch back to original geometry types assignment │ │ │ │ │ + if (options.multi === true) { │ │ │ │ │ + this.setGeometryTypes(); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + if (options.nativeElements) { │ │ │ │ │ + for (i = 0, len = options.nativeElements.length; i < len; ++i) { │ │ │ │ │ + this.writeNode("wfs:Native", │ │ │ │ │ + options.nativeElements[i], node); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "Native": function(nativeElement) { │ │ │ │ │ + var node = this.createElementNSPlus("wfs:Native", { │ │ │ │ │ + attributes: { │ │ │ │ │ + vendorId: nativeElement.vendorId, │ │ │ │ │ + safeToIgnore: nativeElement.safeToIgnore │ │ │ │ │ + }, │ │ │ │ │ + value: nativeElement.value │ │ │ │ │ + }); │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "Insert": function(obj) { │ │ │ │ │ + var feature = obj.feature; │ │ │ │ │ + var options = obj.options; │ │ │ │ │ + var node = this.createElementNSPlus("wfs:Insert", { │ │ │ │ │ + attributes: { │ │ │ │ │ + handle: options && options.handle │ │ │ │ │ + } │ │ │ │ │ + }); │ │ │ │ │ + this.srsName = this.getSrsName(feature); │ │ │ │ │ + this.writeNode("feature:_typeName", feature, node); │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "Update": function(obj) { │ │ │ │ │ + var feature = obj.feature; │ │ │ │ │ + var options = obj.options; │ │ │ │ │ + var node = this.createElementNSPlus("wfs:Update", { │ │ │ │ │ + attributes: { │ │ │ │ │ + handle: options && options.handle, │ │ │ │ │ + typeName: (this.featureNS ? this.featurePrefix + ":" : "") + │ │ │ │ │ + this.featureType │ │ │ │ │ + } │ │ │ │ │ + }); │ │ │ │ │ + if (this.featureNS) { │ │ │ │ │ + node.setAttribute("xmlns:" + this.featurePrefix, this.featureNS); │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + // add in geometry │ │ │ │ │ + var modified = feature.modified; │ │ │ │ │ + if (this.geometryName !== null && (!modified || modified.geometry !== undefined)) { │ │ │ │ │ + this.srsName = this.getSrsName(feature); │ │ │ │ │ + this.writeNode( │ │ │ │ │ + "Property", { │ │ │ │ │ + name: this.geometryName, │ │ │ │ │ + value: feature.geometry │ │ │ │ │ + }, node │ │ │ │ │ + ); │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + // add in attributes │ │ │ │ │ + for (var key in feature.attributes) { │ │ │ │ │ + if (feature.attributes[key] !== undefined && │ │ │ │ │ + (!modified || !modified.attributes || │ │ │ │ │ + (modified.attributes && modified.attributes[key] !== undefined))) { │ │ │ │ │ + this.writeNode( │ │ │ │ │ + "Property", { │ │ │ │ │ + name: key, │ │ │ │ │ + value: feature.attributes[key] │ │ │ │ │ + }, node │ │ │ │ │ + ); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + // add feature id filter │ │ │ │ │ + this.writeNode("ogc:Filter", new OpenLayers.Filter.FeatureId({ │ │ │ │ │ + fids: [feature.fid] │ │ │ │ │ + }), node); │ │ │ │ │ + │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "Property": function(obj) { │ │ │ │ │ + var node = this.createElementNSPlus("wfs:Property"); │ │ │ │ │ + this.writeNode("Name", obj.name, node); │ │ │ │ │ + if (obj.value !== null) { │ │ │ │ │ + this.writeNode("Value", obj.value, node); │ │ │ │ │ + } │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "Name": function(name) { │ │ │ │ │ + return this.createElementNSPlus("wfs:Name", { │ │ │ │ │ + value: name │ │ │ │ │ + }); │ │ │ │ │ + }, │ │ │ │ │ + "Value": function(obj) { │ │ │ │ │ + var node; │ │ │ │ │ + if (obj instanceof OpenLayers.Geometry) { │ │ │ │ │ + node = this.createElementNSPlus("wfs:Value"); │ │ │ │ │ + var geom = this.writeNode("feature:_geometry", obj).firstChild; │ │ │ │ │ + node.appendChild(geom); │ │ │ │ │ + } else { │ │ │ │ │ + node = this.createElementNSPlus("wfs:Value", { │ │ │ │ │ + value: obj │ │ │ │ │ + }); │ │ │ │ │ + } │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "Delete": function(obj) { │ │ │ │ │ + var feature = obj.feature; │ │ │ │ │ + var options = obj.options; │ │ │ │ │ + var node = this.createElementNSPlus("wfs:Delete", { │ │ │ │ │ + attributes: { │ │ │ │ │ + handle: options && options.handle, │ │ │ │ │ + typeName: (this.featureNS ? this.featurePrefix + ":" : "") + │ │ │ │ │ + this.featureType │ │ │ │ │ + } │ │ │ │ │ + }); │ │ │ │ │ + if (this.featureNS) { │ │ │ │ │ + node.setAttribute("xmlns:" + this.featurePrefix, this.featureNS); │ │ │ │ │ + } │ │ │ │ │ + this.writeNode("ogc:Filter", new OpenLayers.Filter.FeatureId({ │ │ │ │ │ + fids: [feature.fid] │ │ │ │ │ + }), node); │ │ │ │ │ + return node; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - // │ │ │ │ │ - // CONVENIENCE TRANSLATION FUNCTIONS FOR API │ │ │ │ │ - // │ │ │ │ │ - │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: getLonLatFromPixel │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * px - {|Object} An OpenLayers.Pixel or an object with │ │ │ │ │ - * a 'x' and 'y' properties. │ │ │ │ │ + * Method: schemaLocationAttr │ │ │ │ │ + * Generate the xsi:schemaLocation attribute value. │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {} An OpenLayers.LonLat corresponding to the given │ │ │ │ │ - * OpenLayers.Pixel, translated into lon/lat by the │ │ │ │ │ - * current base layer │ │ │ │ │ + * {String} The xsi:schemaLocation attribute or undefined if none. │ │ │ │ │ */ │ │ │ │ │ - getLonLatFromPixel: function(px) { │ │ │ │ │ - return this.getLonLatFromViewPortPx(px); │ │ │ │ │ + schemaLocationAttr: function(options) { │ │ │ │ │ + options = OpenLayers.Util.extend({ │ │ │ │ │ + featurePrefix: this.featurePrefix, │ │ │ │ │ + schema: this.schema │ │ │ │ │ + }, options); │ │ │ │ │ + var schemaLocations = OpenLayers.Util.extend({}, this.schemaLocations); │ │ │ │ │ + if (options.schema) { │ │ │ │ │ + schemaLocations[options.featurePrefix] = options.schema; │ │ │ │ │ + } │ │ │ │ │ + var parts = []; │ │ │ │ │ + var uri; │ │ │ │ │ + for (var key in schemaLocations) { │ │ │ │ │ + uri = this.namespaces[key]; │ │ │ │ │ + if (uri) { │ │ │ │ │ + parts.push(uri + " " + schemaLocations[key]); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + var value = parts.join(" ") || undefined; │ │ │ │ │ + return value; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ - * │ │ │ │ │ + * Method: setFilterProperty │ │ │ │ │ + * Set the property of each spatial filter. │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * lonlat - {} A map location. │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {} An OpenLayers.Pixel corresponding to the │ │ │ │ │ - * translated into view port pixels by the current │ │ │ │ │ - * base layer. │ │ │ │ │ + * filter - {} │ │ │ │ │ */ │ │ │ │ │ - getPixelFromLonLat: function(lonlat) { │ │ │ │ │ - var px = this.getViewPortPxFromLonLat(lonlat); │ │ │ │ │ - px.x = Math.round(px.x); │ │ │ │ │ - px.y = Math.round(px.y); │ │ │ │ │ - return px; │ │ │ │ │ + setFilterProperty: function(filter) { │ │ │ │ │ + if (filter.filters) { │ │ │ │ │ + for (var i = 0, len = filter.filters.length; i < len; ++i) { │ │ │ │ │ + OpenLayers.Format.WFST.v1.prototype.setFilterProperty.call(this, filter.filters[i]); │ │ │ │ │ + } │ │ │ │ │ + } else { │ │ │ │ │ + if (filter instanceof OpenLayers.Filter.Spatial && !filter.property) { │ │ │ │ │ + // got a spatial filter without property, so set it │ │ │ │ │ + filter.property = this.geometryName; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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); │ │ │ │ │ - } │ │ │ │ │ + CLASS_NAME: "OpenLayers.Format.WFST.v1" │ │ │ │ │ │ │ │ │ │ - return new OpenLayers.Size( │ │ │ │ │ - OpenLayers.Util.distVincenty(left, right), │ │ │ │ │ - OpenLayers.Util.distVincenty(bottom, top) │ │ │ │ │ - ); │ │ │ │ │ - }, │ │ │ │ │ +}); │ │ │ │ │ +/* ====================================================================== │ │ │ │ │ + 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 │ │ │ │ │ + */ │ │ │ │ │ │ │ │ │ │ - // │ │ │ │ │ - // TRANSLATION: ViewPortPx <-> LayerPx │ │ │ │ │ - // │ │ │ │ │ +/** │ │ │ │ │ + * Class: OpenLayers.Filter.Logical │ │ │ │ │ + * This class represents ogc:And, ogc:Or and ogc:Not rules. │ │ │ │ │ + * │ │ │ │ │ + * Inherits from: │ │ │ │ │ + * - │ │ │ │ │ + */ │ │ │ │ │ +OpenLayers.Filter.Logical = OpenLayers.Class(OpenLayers.Filter, { │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: getViewPortPxFromLayerPx │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * layerPx - {} │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {} Layer Pixel translated into ViewPort Pixel │ │ │ │ │ - * coordinates │ │ │ │ │ + * APIProperty: filters │ │ │ │ │ + * {Array()} Child filters for this filter. │ │ │ │ │ */ │ │ │ │ │ - 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; │ │ │ │ │ - }, │ │ │ │ │ + filters: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: getLayerPxFromViewPortPx │ │ │ │ │ - * │ │ │ │ │ + * 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: │ │ │ │ │ - * viewPortPx - {} │ │ │ │ │ + * options - {Object} An optional object with properties to set on the │ │ │ │ │ + * filter. │ │ │ │ │ * │ │ │ │ │ * 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; │ │ │ │ │ + initialize: function(options) { │ │ │ │ │ + this.filters = []; │ │ │ │ │ + OpenLayers.Filter.prototype.initialize.apply(this, [options]); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - // │ │ │ │ │ - // TRANSLATION: LonLat <-> LayerPx │ │ │ │ │ - // │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Method: getLonLatFromLayerPx │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * px - {} │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {} │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: destroy │ │ │ │ │ + * Remove reference to child filters. │ │ │ │ │ */ │ │ │ │ │ - getLonLatFromLayerPx: function(px) { │ │ │ │ │ - //adjust for displacement of layerContainerDiv │ │ │ │ │ - px = this.getViewPortPxFromLayerPx(px); │ │ │ │ │ - return this.getLonLatFromViewPortPx(px); │ │ │ │ │ + destroy: function() { │ │ │ │ │ + this.filters = null; │ │ │ │ │ + OpenLayers.Filter.prototype.destroy.apply(this); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: getLayerPxFromLonLat │ │ │ │ │ + * APIMethod: evaluate │ │ │ │ │ + * Evaluates this filter in a specific context. │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * lonlat - {} lonlat │ │ │ │ │ - * │ │ │ │ │ + * 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: │ │ │ │ │ - * {} 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. │ │ │ │ │ + * {Boolean} The filter applies. │ │ │ │ │ */ │ │ │ │ │ - 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(''); │ │ │ │ │ + evaluate: function(context) { │ │ │ │ │ + var i, len; │ │ │ │ │ + switch (this.type) { │ │ │ │ │ + case OpenLayers.Filter.Logical.AND: │ │ │ │ │ + for (i = 0, len = this.filters.length; i < len; i++) { │ │ │ │ │ + if (this.filters[i].evaluate(context) == false) { │ │ │ │ │ + return false; │ │ │ │ │ + } │ │ │ │ │ } │ │ │ │ │ - // 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(', ')']; │ │ │ │ │ + return true; │ │ │ │ │ + │ │ │ │ │ + case OpenLayers.Filter.Logical.OR: │ │ │ │ │ + for (i = 0, len = this.filters.length; i < len; i++) { │ │ │ │ │ + if (this.filters[i].evaluate(context) == true) { │ │ │ │ │ + return true; │ │ │ │ │ + } │ │ │ │ │ } │ │ │ │ │ - this.applyTransform.template = template; │ │ │ │ │ - } │ │ │ │ │ + return false; │ │ │ │ │ + │ │ │ │ │ + case OpenLayers.Filter.Logical.NOT: │ │ │ │ │ + return (!this.filters[0].evaluate(context)); │ │ │ │ │ } │ │ │ │ │ + return undefined; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - // 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] = ''; │ │ │ │ │ - } │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: clone │ │ │ │ │ + * Clones this filter. │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {} Clone of this filter. │ │ │ │ │ + */ │ │ │ │ │ + clone: function() { │ │ │ │ │ + var filters = []; │ │ │ │ │ + for (var i = 0, len = this.filters.length; i < len; ++i) { │ │ │ │ │ + filters.push(this.filters[i].clone()); │ │ │ │ │ } │ │ │ │ │ + return new OpenLayers.Filter.Logical({ │ │ │ │ │ + type: this.type, │ │ │ │ │ + filters: filters │ │ │ │ │ + }); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Map" │ │ │ │ │ + CLASS_NAME: "OpenLayers.Filter.Logical" │ │ │ │ │ }); │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * 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.Filter.Logical.AND = "&&"; │ │ │ │ │ +OpenLayers.Filter.Logical.OR = "||"; │ │ │ │ │ +OpenLayers.Filter.Logical.NOT = "!"; │ │ │ │ │ /* ====================================================================== │ │ │ │ │ - OpenLayers/Layer.js │ │ │ │ │ + OpenLayers/Filter/Comparison.js │ │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ │ │ - │ │ │ │ │ /** │ │ │ │ │ - * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ │ - * @requires OpenLayers/Map.js │ │ │ │ │ - * @requires OpenLayers/Projection.js │ │ │ │ │ + * @requires OpenLayers/Filter.js │ │ │ │ │ */ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Class: OpenLayers.Layer │ │ │ │ │ + * Class: OpenLayers.Filter.Comparison │ │ │ │ │ + * This class represents a comparison filter. │ │ │ │ │ + * │ │ │ │ │ + * Inherits from: │ │ │ │ │ + * - │ │ │ │ │ */ │ │ │ │ │ -OpenLayers.Layer = OpenLayers.Class({ │ │ │ │ │ +OpenLayers.Filter.Comparison = OpenLayers.Class(OpenLayers.Filter, { │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: id │ │ │ │ │ - * {String} │ │ │ │ │ + * 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"; │ │ │ │ │ */ │ │ │ │ │ - id: null, │ │ │ │ │ + type: null, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIProperty: name │ │ │ │ │ + /** │ │ │ │ │ + * APIProperty: property │ │ │ │ │ * {String} │ │ │ │ │ + * name of the context property to compare │ │ │ │ │ */ │ │ │ │ │ - name: null, │ │ │ │ │ + property: null, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIProperty: div │ │ │ │ │ - * {DOMElement} │ │ │ │ │ + /** │ │ │ │ │ + * 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}" │ │ │ │ │ */ │ │ │ │ │ - div: null, │ │ │ │ │ + value: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: opacity │ │ │ │ │ - * {Float} The layer's opacity. Float number between 0.0 and 1.0. Default │ │ │ │ │ - * is 1. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - opacity: 1, │ │ │ │ │ + matchCase: true, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * 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}" │ │ │ │ │ */ │ │ │ │ │ - alwaysInRange: null, │ │ │ │ │ + lowerBoundary: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Constant: RESOLUTION_PROPERTIES │ │ │ │ │ - * {Array} The properties that are used for calculating resolutions │ │ │ │ │ - * information. │ │ │ │ │ + * 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}" │ │ │ │ │ */ │ │ │ │ │ - RESOLUTION_PROPERTIES: [ │ │ │ │ │ - 'scales', 'resolutions', │ │ │ │ │ - 'maxScale', 'minScale', │ │ │ │ │ - 'maxResolution', 'minResolution', │ │ │ │ │ - 'numZoomLevels', 'maxZoomLevel' │ │ │ │ │ - ], │ │ │ │ │ + upperBoundary: null, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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. │ │ │ │ │ + /** │ │ │ │ │ + * Constructor: OpenLayers.Filter.Comparison │ │ │ │ │ + * Creates a comparison rule. │ │ │ │ │ * │ │ │ │ │ - * 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. │ │ │ │ │ + * Parameters: │ │ │ │ │ + * options - {Object} An optional object with properties to set on the │ │ │ │ │ + * rule │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {} │ │ │ │ │ */ │ │ │ │ │ - events: null, │ │ │ │ │ + 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; │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: map │ │ │ │ │ - * {} This variable is set when the layer is added to │ │ │ │ │ - * the map, via the accessor function setMap(). │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - map: null, │ │ │ │ │ + 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; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: isBaseLayer │ │ │ │ │ - * {Boolean} Whether or not the layer is a base layer. This should be set │ │ │ │ │ - * individually by all subclasses. Default is false │ │ │ │ │ + * 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 │ │ │ │ │ */ │ │ │ │ │ - isBaseLayer: false, │ │ │ │ │ + value2regex: function(wildCard, singleChar, escapeChar) { │ │ │ │ │ + if (wildCard == ".") { │ │ │ │ │ + throw new Error("'.' is an unsupported wildCard character for " + │ │ │ │ │ + "OpenLayers.Filter.Comparison"); │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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, │ │ │ │ │ + // set UMN MapServer defaults for unspecified parameters │ │ │ │ │ + wildCard = wildCard ? wildCard : "*"; │ │ │ │ │ + singleChar = singleChar ? singleChar : "."; │ │ │ │ │ + escapeChar = escapeChar ? escapeChar : "!"; │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIProperty: visibility │ │ │ │ │ - * {Boolean} The layer should be displayed in the map. Default is true. │ │ │ │ │ - */ │ │ │ │ │ - visibility: true, │ │ │ │ │ + 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; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: attribution │ │ │ │ │ - * {String} Attribution string, displayed when an │ │ │ │ │ - * has been added to the map. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - attribution: null, │ │ │ │ │ + regex2value: function() { │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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, │ │ │ │ │ + var value = this.value; │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Propery: imageSize │ │ │ │ │ - * {} For layers with a gutter, the image is larger than │ │ │ │ │ - * the tile by twice the gutter in each dimension. │ │ │ │ │ - */ │ │ │ │ │ - imageSize: null, │ │ │ │ │ + // replace ! with !! │ │ │ │ │ + value = value.replace(/!/g, "!!"); │ │ │ │ │ │ │ │ │ │ - // OPTIONS │ │ │ │ │ + // replace \. with !. (watching out for \\.) │ │ │ │ │ + value = value.replace(/(\\)?\\\./g, function($0, $1) { │ │ │ │ │ + return $1 ? $0 : "!."; │ │ │ │ │ + }); │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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, │ │ │ │ │ + // replace \* with #* (watching out for \\*) │ │ │ │ │ + value = value.replace(/(\\)?\\\*/g, function($0, $1) { │ │ │ │ │ + return $1 ? $0 : "!*"; │ │ │ │ │ + }); │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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, │ │ │ │ │ + // replace \\ with \ │ │ │ │ │ + value = value.replace(/\\\\/g, "\\"); │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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, │ │ │ │ │ + // convert .* to * (the sequence #.* is not allowed) │ │ │ │ │ + value = value.replace(/\.\*/g, "*"); │ │ │ │ │ + │ │ │ │ │ + return value; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * APIMethod: clone │ │ │ │ │ + * Clones this filter. │ │ │ │ │ * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {} Clone of this filter. │ │ │ │ │ */ │ │ │ │ │ - projection: null, │ │ │ │ │ + clone: function() { │ │ │ │ │ + return OpenLayers.Util.extend(new OpenLayers.Filter.Comparison(), this); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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, │ │ │ │ │ + CLASS_NAME: "OpenLayers.Filter.Comparison" │ │ │ │ │ +}); │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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, │ │ │ │ │ +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 │ │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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, │ │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ + * full text of the license. */ │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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, │ │ │ │ │ +/** │ │ │ │ │ + * @requires OpenLayers/Format/XML/VersionedOGC.js │ │ │ │ │ + * @requires OpenLayers/Filter/FeatureId.js │ │ │ │ │ + * @requires OpenLayers/Filter/Logical.js │ │ │ │ │ + * @requires OpenLayers/Filter/Comparison.js │ │ │ │ │ + */ │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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, │ │ │ │ │ +/** │ │ │ │ │ + * 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: minResolution │ │ │ │ │ - * {Float} │ │ │ │ │ + * APIProperty: defaultVersion │ │ │ │ │ + * {String} Version number to assume if none found. Default is "1.0.0". │ │ │ │ │ */ │ │ │ │ │ - minResolution: null, │ │ │ │ │ + defaultVersion: "1.0.0", │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: numZoomLevels │ │ │ │ │ - * {Integer} │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - numZoomLevels: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: minScale │ │ │ │ │ - * {Float} │ │ │ │ │ + * APIMethod: read │ │ │ │ │ + * Read and Filter doc and return an object representing the Filter. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * data - {String | DOMElement} Data to read. │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {} A filter object. │ │ │ │ │ */ │ │ │ │ │ - minScale: null, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIProperty: maxScale │ │ │ │ │ - * {Float} │ │ │ │ │ - */ │ │ │ │ │ - maxScale: null, │ │ │ │ │ + CLASS_NAME: "OpenLayers.Format.Filter" │ │ │ │ │ +}); │ │ │ │ │ +/* ====================================================================== │ │ │ │ │ + OpenLayers/Filter/Function.js │ │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIProperty: displayOutsideMaxExtent │ │ │ │ │ - * {Boolean} Request map tiles that are completely outside of the max │ │ │ │ │ - * extent for this layer. Defaults to false. │ │ │ │ │ - */ │ │ │ │ │ - displayOutsideMaxExtent: false, │ │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ + * full list of contributors). Published 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: 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. │ │ │ │ │ + * APIProperty: name │ │ │ │ │ + * {String} Name of the function. │ │ │ │ │ */ │ │ │ │ │ - wrapDateLine: false, │ │ │ │ │ + name: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: metadata │ │ │ │ │ - * {Object} This object can be used to store additional information on a │ │ │ │ │ - * layer object. │ │ │ │ │ + * APIProperty: params │ │ │ │ │ + * {Array( || String || Number)} Function parameters │ │ │ │ │ + * For now support only other Functions, String or Number │ │ │ │ │ */ │ │ │ │ │ - metadata: null, │ │ │ │ │ + params: null, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Constructor: OpenLayers.Layer │ │ │ │ │ + /** │ │ │ │ │ + * Constructor: OpenLayers.Filter.Function │ │ │ │ │ + * Creates a filter function. │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * name - {String} The layer name │ │ │ │ │ - * options - {Object} Hashtable of extra options to tag onto the layer │ │ │ │ │ + * options - {Object} An optional object with properties to set on the │ │ │ │ │ + * function. │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {} │ │ │ │ │ */ │ │ │ │ │ - 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; │ │ │ │ │ + CLASS_NAME: "OpenLayers.Filter.Function" │ │ │ │ │ +}); │ │ │ │ │ │ │ │ │ │ - if (this.id == null) { │ │ │ │ │ +/* ====================================================================== │ │ │ │ │ + OpenLayers/BaseTypes/Date.js │ │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ │ │ - this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_"); │ │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ + * full text of the license. */ │ │ │ │ │ │ │ │ │ │ - this.div = OpenLayers.Util.createDiv(this.id); │ │ │ │ │ - this.div.style.width = "100%"; │ │ │ │ │ - this.div.style.height = "100%"; │ │ │ │ │ - this.div.dir = "ltr"; │ │ │ │ │ +/** │ │ │ │ │ + * @requires OpenLayers/SingleFile.js │ │ │ │ │ + */ │ │ │ │ │ │ │ │ │ │ - this.events = new OpenLayers.Events(this, this.div); │ │ │ │ │ - if (this.eventListeners instanceof Object) { │ │ │ │ │ - this.events.on(this.eventListeners); │ │ │ │ │ - } │ │ │ │ │ +/** │ │ │ │ │ + * 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)?$/, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * 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: │ │ │ │ │ - * setNewBaseLayer - {Boolean} Set a new base layer when this layer has │ │ │ │ │ - * been destroyed. Default is true. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - destroy: function(setNewBaseLayer) { │ │ │ │ │ - if (setNewBaseLayer == null) { │ │ │ │ │ - setNewBaseLayer = true; │ │ │ │ │ - } │ │ │ │ │ - if (this.map != null) { │ │ │ │ │ - this.map.removeLayer(this, setNewBaseLayer); │ │ │ │ │ + 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; │ │ │ │ │ + }; │ │ │ │ │ } │ │ │ │ │ - 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 │ │ │ │ │ + * 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: │ │ │ │ │ - * obj - {} The layer to be cloned │ │ │ │ │ + * 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: │ │ │ │ │ - * {} An exact clone of this │ │ │ │ │ + * {Date} A date object. If the string could not be parsed, an invalid │ │ │ │ │ + * date is returned (i.e. isNaN(date.getTime())). │ │ │ │ │ */ │ │ │ │ │ - clone: function(obj) { │ │ │ │ │ - │ │ │ │ │ - if (obj == null) { │ │ │ │ │ - obj = new OpenLayers.Layer(this.name, this.getOptions()); │ │ │ │ │ + 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 │ │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ │ │ - // catch any randomly tagged-on properties │ │ │ │ │ - OpenLayers.Util.applyDefaults(obj, this); │ │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ + * full list of contributors). Published 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 │ │ │ │ │ + */ │ │ │ │ │ │ │ │ │ │ - // a cloned layer should never have its map property set │ │ │ │ │ - // because it has not been added to a map yet. │ │ │ │ │ - obj.map = null; │ │ │ │ │ +/** │ │ │ │ │ + * Class: OpenLayers.Format.Filter.v1 │ │ │ │ │ + * Superclass for Filter version 1 parsers. │ │ │ │ │ + * │ │ │ │ │ + * Inherits from: │ │ │ │ │ + * - │ │ │ │ │ + */ │ │ │ │ │ +OpenLayers.Format.Filter.v1 = OpenLayers.Class(OpenLayers.Format.XML, { │ │ │ │ │ │ │ │ │ │ - return obj; │ │ │ │ │ + /** │ │ │ │ │ + * 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" │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * Property: defaultPrefix │ │ │ │ │ */ │ │ │ │ │ - getOptions: function() { │ │ │ │ │ - var options = {}; │ │ │ │ │ - for (var o in this.options) { │ │ │ │ │ - options[o] = this[o]; │ │ │ │ │ - } │ │ │ │ │ - return options; │ │ │ │ │ - }, │ │ │ │ │ + defaultPrefix: "ogc", │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIMethod: setName │ │ │ │ │ - * Sets the new layer name for this layer. Can trigger a changelayer event │ │ │ │ │ - * on the map. │ │ │ │ │ + /** │ │ │ │ │ + * 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: │ │ │ │ │ - * newName - {String} The new name. │ │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ │ + * this instance. │ │ │ │ │ */ │ │ │ │ │ - setName: function(newName) { │ │ │ │ │ - if (newName != this.name) { │ │ │ │ │ - this.name = newName; │ │ │ │ │ - if (this.map != null) { │ │ │ │ │ - this.map.events.triggerEvent("changelayer", { │ │ │ │ │ - layer: this, │ │ │ │ │ - property: "name" │ │ │ │ │ - }); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ + initialize: function(options) { │ │ │ │ │ + OpenLayers.Format.XML.prototype.initialize.apply(this, [options]); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: addOptions │ │ │ │ │ - * │ │ │ │ │ + * Method: read │ │ │ │ │ + * │ │ │ │ │ * 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. │ │ │ │ │ + * data - {DOMElement} A Filter document element. │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {} A filter object. │ │ │ │ │ */ │ │ │ │ │ - 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) { │ │ │ │ │ + read: function(data) { │ │ │ │ │ + var obj = {}; │ │ │ │ │ + this.readers.ogc["Filter"].apply(this, [data, obj]); │ │ │ │ │ + return obj.filter; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - 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 │ │ │ │ │ - }); │ │ │ │ │ + /** │ │ │ │ │ + * 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; │ │ │ │ │ } │ │ │ │ │ - break; │ │ │ │ │ } │ │ │ │ │ + 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); │ │ │ │ │ } │ │ │ │ │ } │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: onMapResize │ │ │ │ │ - * This function can be implemented by subclasses │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - onMapResize: function() { │ │ │ │ │ - //this function can be implemented by subclasses │ │ │ │ │ + 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: redraw │ │ │ │ │ - * Redraws the layer. Returns true if the layer was redrawn, false if not. │ │ │ │ │ + * 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: │ │ │ │ │ - * {Boolean} The layer was redrawn. │ │ │ │ │ + * {String} String representation of the provided value. │ │ │ │ │ */ │ │ │ │ │ - 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; │ │ │ │ │ - } │ │ │ │ │ + encodeLiteral: function(value) { │ │ │ │ │ + if (value instanceof Date) { │ │ │ │ │ + value = OpenLayers.Date.toISOString(value); │ │ │ │ │ } │ │ │ │ │ - return redrawn; │ │ │ │ │ + return value; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: moveTo │ │ │ │ │ - * │ │ │ │ │ + * Method: writeOgcExpression │ │ │ │ │ + * Limited support for writing OGC expressions. Currently it supports │ │ │ │ │ + * ( || String || Number) │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * bounds - {} │ │ │ │ │ - * zoomChanged - {Boolean} Tells when zoom has changed, as layers have to │ │ │ │ │ - * do some init work in that case. │ │ │ │ │ - * dragging - {Boolean} │ │ │ │ │ + * value - ( || String || Number) │ │ │ │ │ + * node - {DOMElement} A parent DOM element │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {DOMElement} Updated node element. │ │ │ │ │ */ │ │ │ │ │ - moveTo: function(bounds, zoomChanged, dragging) { │ │ │ │ │ - var display = this.visibility; │ │ │ │ │ - if (!this.isBaseLayer) { │ │ │ │ │ - display = display && this.inRange; │ │ │ │ │ + writeOgcExpression: function(value, node) { │ │ │ │ │ + if (value instanceof OpenLayers.Filter.Function) { │ │ │ │ │ + this.writeNode("Function", value, node); │ │ │ │ │ + } else { │ │ │ │ │ + this.writeNode("Literal", value, node); │ │ │ │ │ } │ │ │ │ │ - this.display(display); │ │ │ │ │ + return node; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: moveByPx │ │ │ │ │ - * Move the layer based on pixel vector. To be implemented by subclasses. │ │ │ │ │ + * Method: write │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * dx - {Number} The x coord of the displacement vector. │ │ │ │ │ - * dy - {Number} The y coord of the displacement vector. │ │ │ │ │ + * filter - {} A filter object. │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {DOMElement} An ogc:Filter element. │ │ │ │ │ */ │ │ │ │ │ - moveByPx: function(dx, dy) {}, │ │ │ │ │ + write: function(filter) { │ │ │ │ │ + return this.writers.ogc["Filter"].apply(this, [filter]); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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 - {} │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - setMap: function(map) { │ │ │ │ │ - if (this.map == null) { │ │ │ │ │ + writers: { │ │ │ │ │ + "ogc": { │ │ │ │ │ + "Filter": function(filter) { │ │ │ │ │ + var node = this.createElementNSPlus("ogc:Filter"); │ │ │ │ │ + this.writeNode(this.getFilterType(filter), filter, node); │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "_featureIds": function(filter) { │ │ │ │ │ + var node = this.createDocumentFragment(); │ │ │ │ │ + for (var i = 0, ii = filter.fids.length; i < ii; ++i) { │ │ │ │ │ + this.writeNode("ogc:FeatureId", filter.fids[i], node); │ │ │ │ │ + } │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "FeatureId": function(fid) { │ │ │ │ │ + return this.createElementNSPlus("ogc:FeatureId", { │ │ │ │ │ + attributes: { │ │ │ │ │ + fid: fid │ │ │ │ │ + } │ │ │ │ │ + }); │ │ │ │ │ + }, │ │ │ │ │ + "And": function(filter) { │ │ │ │ │ + var node = this.createElementNSPlus("ogc:And"); │ │ │ │ │ + var childFilter; │ │ │ │ │ + for (var i = 0, ii = filter.filters.length; i < ii; ++i) { │ │ │ │ │ + childFilter = filter.filters[i]; │ │ │ │ │ + this.writeNode( │ │ │ │ │ + this.getFilterType(childFilter), childFilter, node │ │ │ │ │ + ); │ │ │ │ │ + } │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "Or": function(filter) { │ │ │ │ │ + var node = this.createElementNSPlus("ogc:Or"); │ │ │ │ │ + var childFilter; │ │ │ │ │ + for (var i = 0, ii = filter.filters.length; i < ii; ++i) { │ │ │ │ │ + childFilter = filter.filters[i]; │ │ │ │ │ + this.writeNode( │ │ │ │ │ + this.getFilterType(childFilter), childFilter, node │ │ │ │ │ + ); │ │ │ │ │ + } │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "Not": function(filter) { │ │ │ │ │ + var node = this.createElementNSPlus("ogc:Not"); │ │ │ │ │ + var childFilter = filter.filters[0]; │ │ │ │ │ + this.writeNode( │ │ │ │ │ + this.getFilterType(childFilter), childFilter, node │ │ │ │ │ + ); │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "PropertyIsLessThan": function(filter) { │ │ │ │ │ + var node = this.createElementNSPlus("ogc:PropertyIsLessThan"); │ │ │ │ │ + // no ogc:expression handling for PropertyName for now │ │ │ │ │ + this.writeNode("PropertyName", filter, node); │ │ │ │ │ + // handle Literals or Functions for now │ │ │ │ │ + this.writeOgcExpression(filter.value, node); │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "PropertyIsGreaterThan": function(filter) { │ │ │ │ │ + var node = this.createElementNSPlus("ogc:PropertyIsGreaterThan"); │ │ │ │ │ + // no ogc:expression handling for PropertyName for now │ │ │ │ │ + this.writeNode("PropertyName", filter, node); │ │ │ │ │ + // handle Literals or Functions for now │ │ │ │ │ + this.writeOgcExpression(filter.value, node); │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "PropertyIsLessThanOrEqualTo": function(filter) { │ │ │ │ │ + var node = this.createElementNSPlus("ogc:PropertyIsLessThanOrEqualTo"); │ │ │ │ │ + // no ogc:expression handling for PropertyName for now │ │ │ │ │ + this.writeNode("PropertyName", filter, node); │ │ │ │ │ + // handle Literals or Functions for now │ │ │ │ │ + this.writeOgcExpression(filter.value, node); │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "PropertyIsGreaterThanOrEqualTo": function(filter) { │ │ │ │ │ + var node = this.createElementNSPlus("ogc:PropertyIsGreaterThanOrEqualTo"); │ │ │ │ │ + // no ogc:expression handling for PropertyName for now │ │ │ │ │ + this.writeNode("PropertyName", filter, node); │ │ │ │ │ + // handle Literals or Functions for now │ │ │ │ │ + this.writeOgcExpression(filter.value, node); │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "PropertyIsBetween": function(filter) { │ │ │ │ │ + var node = this.createElementNSPlus("ogc:PropertyIsBetween"); │ │ │ │ │ + // no ogc:expression handling for PropertyName for now │ │ │ │ │ + this.writeNode("PropertyName", filter, node); │ │ │ │ │ + this.writeNode("LowerBoundary", filter, node); │ │ │ │ │ + this.writeNode("UpperBoundary", filter, node); │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "PropertyName": function(filter) { │ │ │ │ │ + // no ogc:expression handling for now │ │ │ │ │ + return this.createElementNSPlus("ogc:PropertyName", { │ │ │ │ │ + value: filter.property │ │ │ │ │ + }); │ │ │ │ │ + }, │ │ │ │ │ + "Literal": function(value) { │ │ │ │ │ + var encode = this.encodeLiteral || │ │ │ │ │ + OpenLayers.Format.Filter.v1.prototype.encodeLiteral; │ │ │ │ │ + return this.createElementNSPlus("ogc:Literal", { │ │ │ │ │ + value: encode(value) │ │ │ │ │ + }); │ │ │ │ │ + }, │ │ │ │ │ + "LowerBoundary": function(filter) { │ │ │ │ │ + // handle Literals or Functions for now │ │ │ │ │ + var node = this.createElementNSPlus("ogc:LowerBoundary"); │ │ │ │ │ + this.writeOgcExpression(filter.lowerBoundary, node); │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "UpperBoundary": function(filter) { │ │ │ │ │ + // handle Literals or Functions for now │ │ │ │ │ + var node = this.createElementNSPlus("ogc:UpperBoundary"); │ │ │ │ │ + this.writeNode("Literal", filter.upperBoundary, node); │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "INTERSECTS": function(filter) { │ │ │ │ │ + return this.writeSpatial(filter, "Intersects"); │ │ │ │ │ + }, │ │ │ │ │ + "WITHIN": function(filter) { │ │ │ │ │ + return this.writeSpatial(filter, "Within"); │ │ │ │ │ + }, │ │ │ │ │ + "CONTAINS": function(filter) { │ │ │ │ │ + return this.writeSpatial(filter, "Contains"); │ │ │ │ │ + }, │ │ │ │ │ + "DWITHIN": function(filter) { │ │ │ │ │ + var node = this.writeSpatial(filter, "DWithin"); │ │ │ │ │ + this.writeNode("Distance", filter, node); │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "Distance": function(filter) { │ │ │ │ │ + return this.createElementNSPlus("ogc:Distance", { │ │ │ │ │ + attributes: { │ │ │ │ │ + units: filter.distanceUnits │ │ │ │ │ + }, │ │ │ │ │ + value: filter.distance │ │ │ │ │ + }); │ │ │ │ │ + }, │ │ │ │ │ + "Function": function(filter) { │ │ │ │ │ + var node = this.createElementNSPlus("ogc:Function", { │ │ │ │ │ + attributes: { │ │ │ │ │ + name: filter.name │ │ │ │ │ + } │ │ │ │ │ + }); │ │ │ │ │ + var params = filter.params; │ │ │ │ │ + for (var i = 0, len = params.length; i < len; i++) { │ │ │ │ │ + this.writeOgcExpression(params[i], node); │ │ │ │ │ + } │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "PropertyIsNull": function(filter) { │ │ │ │ │ + var node = this.createElementNSPlus("ogc:PropertyIsNull"); │ │ │ │ │ + this.writeNode("PropertyName", filter, node); │ │ │ │ │ + return node; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - this.map = map; │ │ │ │ │ + /** │ │ │ │ │ + * Method: getFilterType │ │ │ │ │ + */ │ │ │ │ │ + getFilterType: function(filter) { │ │ │ │ │ + var filterType = this.filterMap[filter.type]; │ │ │ │ │ + if (!filterType) { │ │ │ │ │ + throw "Filter writing not supported for rule type: " + filter.type; │ │ │ │ │ + } │ │ │ │ │ + return filterType; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - // 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; │ │ │ │ │ + /** │ │ │ │ │ + * Property: filterMap │ │ │ │ │ + * {Object} Contains a member for each filter type. Values are node names │ │ │ │ │ + * for corresponding OGC Filter child elements. │ │ │ │ │ + */ │ │ │ │ │ + filterMap: { │ │ │ │ │ + "&&": "And", │ │ │ │ │ + "||": "Or", │ │ │ │ │ + "!": "Not", │ │ │ │ │ + "==": "PropertyIsEqualTo", │ │ │ │ │ + "!=": "PropertyIsNotEqualTo", │ │ │ │ │ + "<": "PropertyIsLessThan", │ │ │ │ │ + ">": "PropertyIsGreaterThan", │ │ │ │ │ + "<=": "PropertyIsLessThanOrEqualTo", │ │ │ │ │ + ">=": "PropertyIsGreaterThanOrEqualTo", │ │ │ │ │ + "..": "PropertyIsBetween", │ │ │ │ │ + "~": "PropertyIsLike", │ │ │ │ │ + "NULL": "PropertyIsNull", │ │ │ │ │ + "BBOX": "BBOX", │ │ │ │ │ + "DWITHIN": "DWITHIN", │ │ │ │ │ + "WITHIN": "WITHIN", │ │ │ │ │ + "CONTAINS": "CONTAINS", │ │ │ │ │ + "INTERSECTS": "INTERSECTS", │ │ │ │ │ + "FID": "_featureIds" │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - this.projection = this.projection || this.map.projection; │ │ │ │ │ - if (typeof this.projection == "string") { │ │ │ │ │ - this.projection = new OpenLayers.Projection(this.projection); │ │ │ │ │ - } │ │ │ │ │ + CLASS_NAME: "OpenLayers.Format.Filter.v1" │ │ │ │ │ │ │ │ │ │ - // 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; │ │ │ │ │ +}); │ │ │ │ │ +/* ====================================================================== │ │ │ │ │ + OpenLayers/Format/GML.js │ │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ │ │ - this.initResolutions(); │ │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ + * full text of the license. */ │ │ │ │ │ │ │ │ │ │ - if (!this.isBaseLayer) { │ │ │ │ │ - this.inRange = this.calculateInRange(); │ │ │ │ │ - var show = ((this.visibility) && (this.inRange)); │ │ │ │ │ - this.div.style.display = show ? "" : "none"; │ │ │ │ │ - } │ │ │ │ │ +/** │ │ │ │ │ + * @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 │ │ │ │ │ + */ │ │ │ │ │ │ │ │ │ │ - // deal with gutters │ │ │ │ │ - this.setTileSize(); │ │ │ │ │ - } │ │ │ │ │ - }, │ │ │ │ │ +/** │ │ │ │ │ + * 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, { │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * APIProperty: featureNS │ │ │ │ │ + * {String} Namespace used for feature attributes. Default is │ │ │ │ │ + * "http://mapserver.gis.umn.edu/mapserver". │ │ │ │ │ */ │ │ │ │ │ - afterAdd: function() {}, │ │ │ │ │ + featureNS: "http://mapserver.gis.umn.edu/mapserver", │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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 - {} │ │ │ │ │ + * APIProperty: featurePrefix │ │ │ │ │ + * {String} Namespace alias (or prefix) for feature nodes. Default is │ │ │ │ │ + * "feature". │ │ │ │ │ */ │ │ │ │ │ - removeMap: function(map) { │ │ │ │ │ - //to be overridden by subclasses │ │ │ │ │ - }, │ │ │ │ │ + featurePrefix: "feature", │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * APIProperty: featureName │ │ │ │ │ + * {String} Element name for features. Default is "featureMember". │ │ │ │ │ */ │ │ │ │ │ - getImageSize: function(bounds) { │ │ │ │ │ - return (this.imageSize || this.tileSize); │ │ │ │ │ - }, │ │ │ │ │ + featureName: "featureMember", │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: setTileSize │ │ │ │ │ - * Set the tile size based on the map size. This also sets layer.imageSize │ │ │ │ │ - * or use by Tile.Image. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * size - {} │ │ │ │ │ + * APIProperty: layerName │ │ │ │ │ + * {String} Name of data layer. Default is "features". │ │ │ │ │ */ │ │ │ │ │ - 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)); │ │ │ │ │ - } │ │ │ │ │ - }, │ │ │ │ │ + layerName: "features", │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: getVisibility │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Boolean} The layer should be displayed (if in range). │ │ │ │ │ + * APIProperty: geometryName │ │ │ │ │ + * {String} Name of geometry element. Defaults to "geometry". │ │ │ │ │ */ │ │ │ │ │ - getVisibility: function() { │ │ │ │ │ - return this.visibility; │ │ │ │ │ - }, │ │ │ │ │ + geometryName: "geometry", │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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) │ │ │ │ │ + * APIProperty: collectionName │ │ │ │ │ + * {String} Name of featureCollection element. │ │ │ │ │ */ │ │ │ │ │ - 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"); │ │ │ │ │ - } │ │ │ │ │ - }, │ │ │ │ │ + collectionName: "FeatureCollection", │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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.. │ │ │ │ │ - * │ │ │ │ │ + /** │ │ │ │ │ + * 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: │ │ │ │ │ - * display - {Boolean} │ │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ │ + * this instance. │ │ │ │ │ */ │ │ │ │ │ - display: function(display) { │ │ │ │ │ - if (display != (this.div.style.display != "none")) { │ │ │ │ │ - this.div.style.display = (display && this.calculateInRange()) ? "block" : "none"; │ │ │ │ │ - } │ │ │ │ │ + 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: calculateInRange │ │ │ │ │ + * APIMethod: read │ │ │ │ │ + * Read data from a string, and return a list of features. │ │ │ │ │ * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * data - {String} or {DOMElement} data to read/parse. │ │ │ │ │ + * │ │ │ │ │ * 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. │ │ │ │ │ + * {Array()} An array of features. │ │ │ │ │ */ │ │ │ │ │ - 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)); │ │ │ │ │ + read: function(data) { │ │ │ │ │ + if (typeof data == "string") { │ │ │ │ │ + data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); │ │ │ │ │ + } │ │ │ │ │ + var featureNodes = this.getElementsByTagNameNS(data.documentElement, │ │ │ │ │ + this.gmlns, │ │ │ │ │ + this.featureName); │ │ │ │ │ + var features = []; │ │ │ │ │ + for (var i = 0; i < featureNodes.length; i++) { │ │ │ │ │ + var feature = this.parseFeature(featureNodes[i]); │ │ │ │ │ + if (feature) { │ │ │ │ │ + features.push(feature); │ │ │ │ │ } │ │ │ │ │ } │ │ │ │ │ - return inRange; │ │ │ │ │ + return features; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIMethod: setIsBaseLayer │ │ │ │ │ - * │ │ │ │ │ + /** │ │ │ │ │ + * Method: parseFeature │ │ │ │ │ + * This function is the core of the GML parsing code in OpenLayers. │ │ │ │ │ + * It creates the geometries that are then attached to the returned │ │ │ │ │ + * feature, and calls parseAttributes() to get attribute data out. │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * isBaseLayer - {Boolean} │ │ │ │ │ + * node - {DOMElement} A GML feature node. │ │ │ │ │ */ │ │ │ │ │ - setIsBaseLayer: function(isBaseLayer) { │ │ │ │ │ - if (isBaseLayer != this.isBaseLayer) { │ │ │ │ │ - this.isBaseLayer = isBaseLayer; │ │ │ │ │ - if (this.map != null) { │ │ │ │ │ - this.map.events.triggerEvent("changebaselayer", { │ │ │ │ │ - layer: this │ │ │ │ │ - }); │ │ │ │ │ + parseFeature: function(node) { │ │ │ │ │ + // only accept one geometry per feature - look for highest "order" │ │ │ │ │ + var order = ["MultiPolygon", "Polygon", │ │ │ │ │ + "MultiLineString", "LineString", │ │ │ │ │ + "MultiPoint", "Point", "Envelope" │ │ │ │ │ + ]; │ │ │ │ │ + // FIXME: In case we parse a feature with no geometry, but boundedBy an Envelope, │ │ │ │ │ + // this code creates a geometry derived from the Envelope. This is not correct. │ │ │ │ │ + var type, nodeList, geometry, parser; │ │ │ │ │ + for (var i = 0; i < order.length; ++i) { │ │ │ │ │ + type = order[i]; │ │ │ │ │ + nodeList = this.getElementsByTagNameNS(node, this.gmlns, type); │ │ │ │ │ + if (nodeList.length > 0) { │ │ │ │ │ + // only deal with first geometry of this type │ │ │ │ │ + parser = this.parseGeometry[type.toLowerCase()]; │ │ │ │ │ + if (parser) { │ │ │ │ │ + geometry = parser.apply(this, [nodeList[0]]); │ │ │ │ │ + if (this.internalProjection && this.externalProjection) { │ │ │ │ │ + geometry.transform(this.externalProjection, │ │ │ │ │ + this.internalProjection); │ │ │ │ │ + } │ │ │ │ │ + } else { │ │ │ │ │ + throw new TypeError("Unsupported geometry type: " + type); │ │ │ │ │ + } │ │ │ │ │ + // stop looking for different geometry types │ │ │ │ │ + break; │ │ │ │ │ } │ │ │ │ │ } │ │ │ │ │ - }, │ │ │ │ │ - │ │ │ │ │ - /********************************************************/ │ │ │ │ │ - /* */ │ │ │ │ │ - /* Baselayer Functions */ │ │ │ │ │ - /* */ │ │ │ │ │ - /********************************************************/ │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Method: initResolutions │ │ │ │ │ - * This method's responsibility is to set up the 'resolutions' array │ │ │ │ │ - * for the layer -- this array is what the layer will use to interface │ │ │ │ │ - * between the zoom levels of the map and the resolution display │ │ │ │ │ - * of the layer. │ │ │ │ │ - * │ │ │ │ │ - * The user has several options that determine how the array is set up. │ │ │ │ │ - * │ │ │ │ │ - * For a detailed explanation, see the following wiki from the │ │ │ │ │ - * openlayers.org homepage: │ │ │ │ │ - * http://trac.openlayers.org/wiki/SettingZoomLevels │ │ │ │ │ - */ │ │ │ │ │ - initResolutions: function() { │ │ │ │ │ - │ │ │ │ │ - // ok we want resolutions, here's our strategy: │ │ │ │ │ - // │ │ │ │ │ - // 1. if resolutions are defined in the layer config, use them │ │ │ │ │ - // 2. else, if scales are defined in the layer config then derive │ │ │ │ │ - // resolutions from these scales │ │ │ │ │ - // 3. else, attempt to calculate resolutions from maxResolution, │ │ │ │ │ - // minResolution, numZoomLevels, maxZoomLevel set in the │ │ │ │ │ - // layer config │ │ │ │ │ - // 4. if we still don't have resolutions, and if resolutions │ │ │ │ │ - // are defined in the same, use them │ │ │ │ │ - // 5. else, if scales are defined in the map then derive │ │ │ │ │ - // resolutions from these scales │ │ │ │ │ - // 6. else, attempt to calculate resolutions from maxResolution, │ │ │ │ │ - // minResolution, numZoomLevels, maxZoomLevel set in the │ │ │ │ │ - // map │ │ │ │ │ - // 7. hope for the best! │ │ │ │ │ │ │ │ │ │ - var i, len, p; │ │ │ │ │ - var props = {}, │ │ │ │ │ - alwaysInRange = true; │ │ │ │ │ - │ │ │ │ │ - // get resolution data from layer config │ │ │ │ │ - // (we also set alwaysInRange in the layer as appropriate) │ │ │ │ │ - for (i = 0, len = this.RESOLUTION_PROPERTIES.length; i < len; i++) { │ │ │ │ │ - p = this.RESOLUTION_PROPERTIES[i]; │ │ │ │ │ - props[p] = this.options[p]; │ │ │ │ │ - if (alwaysInRange && this.options[p]) { │ │ │ │ │ - alwaysInRange = false; │ │ │ │ │ + var bounds; │ │ │ │ │ + var boxNodes = this.getElementsByTagNameNS(node, this.gmlns, "Box"); │ │ │ │ │ + for (i = 0; i < boxNodes.length; ++i) { │ │ │ │ │ + var boxNode = boxNodes[i]; │ │ │ │ │ + var box = this.parseGeometry["box"].apply(this, [boxNode]); │ │ │ │ │ + var parentNode = boxNode.parentNode; │ │ │ │ │ + var parentName = parentNode.localName || │ │ │ │ │ + parentNode.nodeName.split(":").pop(); │ │ │ │ │ + if (parentName === "boundedBy") { │ │ │ │ │ + bounds = box; │ │ │ │ │ + } else { │ │ │ │ │ + geometry = box.toGeometry(); │ │ │ │ │ } │ │ │ │ │ } │ │ │ │ │ - if (this.options.alwaysInRange == null) { │ │ │ │ │ - this.alwaysInRange = alwaysInRange; │ │ │ │ │ - } │ │ │ │ │ │ │ │ │ │ - // if we don't have resolutions then attempt to derive them from scales │ │ │ │ │ - if (props.resolutions == null) { │ │ │ │ │ - props.resolutions = this.resolutionsFromScales(props.scales); │ │ │ │ │ + // construct feature (optionally with attributes) │ │ │ │ │ + var attributes; │ │ │ │ │ + if (this.extractAttributes) { │ │ │ │ │ + attributes = this.parseAttributes(node); │ │ │ │ │ } │ │ │ │ │ + var feature = new OpenLayers.Feature.Vector(geometry, attributes); │ │ │ │ │ + feature.bounds = bounds; │ │ │ │ │ │ │ │ │ │ - // if we still don't have resolutions then attempt to calculate them │ │ │ │ │ - if (props.resolutions == null) { │ │ │ │ │ - props.resolutions = this.calculateResolutions(props); │ │ │ │ │ - } │ │ │ │ │ + feature.gml = { │ │ │ │ │ + featureType: node.firstChild.nodeName.split(":")[1], │ │ │ │ │ + featureNS: node.firstChild.namespaceURI, │ │ │ │ │ + featureNSPrefix: node.firstChild.prefix │ │ │ │ │ + }; │ │ │ │ │ │ │ │ │ │ - // if we couldn't calculate resolutions then we look at we have │ │ │ │ │ - // in the map │ │ │ │ │ - if (props.resolutions == null) { │ │ │ │ │ - for (i = 0, len = this.RESOLUTION_PROPERTIES.length; i < len; i++) { │ │ │ │ │ - p = this.RESOLUTION_PROPERTIES[i]; │ │ │ │ │ - props[p] = this.options[p] != null ? │ │ │ │ │ - this.options[p] : this.map[p]; │ │ │ │ │ - } │ │ │ │ │ - if (props.resolutions == null) { │ │ │ │ │ - props.resolutions = this.resolutionsFromScales(props.scales); │ │ │ │ │ - } │ │ │ │ │ - if (props.resolutions == null) { │ │ │ │ │ - props.resolutions = this.calculateResolutions(props); │ │ │ │ │ + // assign fid - this can come from a "fid" or "id" attribute │ │ │ │ │ + var childNode = node.firstChild; │ │ │ │ │ + var fid; │ │ │ │ │ + while (childNode) { │ │ │ │ │ + if (childNode.nodeType == 1) { │ │ │ │ │ + fid = childNode.getAttribute("fid") || │ │ │ │ │ + childNode.getAttribute("id"); │ │ │ │ │ + if (fid) { │ │ │ │ │ + break; │ │ │ │ │ + } │ │ │ │ │ } │ │ │ │ │ + childNode = childNode.nextSibling; │ │ │ │ │ } │ │ │ │ │ + feature.fid = fid; │ │ │ │ │ + return feature; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - // ok, we new need to set properties in the instance │ │ │ │ │ + /** │ │ │ │ │ + * Property: parseGeometry │ │ │ │ │ + * Properties of this object are the functions that parse geometries based │ │ │ │ │ + * on their type. │ │ │ │ │ + */ │ │ │ │ │ + parseGeometry: { │ │ │ │ │ │ │ │ │ │ - // get maxResolution from the config if it's defined there │ │ │ │ │ - var maxResolution; │ │ │ │ │ - if (this.options.maxResolution && │ │ │ │ │ - this.options.maxResolution !== "auto") { │ │ │ │ │ - maxResolution = this.options.maxResolution; │ │ │ │ │ - } │ │ │ │ │ - if (this.options.minScale) { │ │ │ │ │ - maxResolution = OpenLayers.Util.getResolutionFromScale( │ │ │ │ │ - this.options.minScale, this.units); │ │ │ │ │ - } │ │ │ │ │ + /** │ │ │ │ │ + * Method: parseGeometry.point │ │ │ │ │ + * Given a GML node representing a point geometry, create an OpenLayers │ │ │ │ │ + * point geometry. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * node - {DOMElement} A GML node. │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {} 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 = []; │ │ │ │ │ │ │ │ │ │ - // get minResolution from the config if it's defined there │ │ │ │ │ - var minResolution; │ │ │ │ │ - if (this.options.minResolution && │ │ │ │ │ - this.options.minResolution !== "auto") { │ │ │ │ │ - minResolution = this.options.minResolution; │ │ │ │ │ - } │ │ │ │ │ - if (this.options.maxScale) { │ │ │ │ │ - minResolution = OpenLayers.Util.getResolutionFromScale( │ │ │ │ │ - this.options.maxScale, this.units); │ │ │ │ │ - } │ │ │ │ │ + // 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); │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ - if (props.resolutions) { │ │ │ │ │ + // 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(","); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ - //sort resolutions array descendingly │ │ │ │ │ - props.resolutions.sort(function(a, b) { │ │ │ │ │ - return (b - a); │ │ │ │ │ - }); │ │ │ │ │ + // 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 │ │ │ │ │ + ]; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ - // if we still don't have a maxResolution get it from the │ │ │ │ │ - // resolutions array │ │ │ │ │ - if (!maxResolution) { │ │ │ │ │ - maxResolution = props.resolutions[0]; │ │ │ │ │ + // preserve third dimension │ │ │ │ │ + if (coords.length == 2) { │ │ │ │ │ + coords[2] = null; │ │ │ │ │ } │ │ │ │ │ │ │ │ │ │ - // if we still don't have a minResolution get it from the │ │ │ │ │ - // resolutions array │ │ │ │ │ - if (!minResolution) { │ │ │ │ │ - var lastIdx = props.resolutions.length - 1; │ │ │ │ │ - minResolution = props.resolutions[lastIdx]; │ │ │ │ │ + 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]); │ │ │ │ │ } │ │ │ │ │ - } │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - this.resolutions = props.resolutions; │ │ │ │ │ - if (this.resolutions) { │ │ │ │ │ - len = this.resolutions.length; │ │ │ │ │ - this.scales = new Array(len); │ │ │ │ │ - for (i = 0; i < len; i++) { │ │ │ │ │ - this.scales[i] = OpenLayers.Util.getScaleFromResolution( │ │ │ │ │ - this.resolutions[i], this.units); │ │ │ │ │ + /** │ │ │ │ │ + * 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 < nodeList.length; ++i) { │ │ │ │ │ + point = this.parseGeometry.point.apply(this, [nodeList[i]]); │ │ │ │ │ + if (point) { │ │ │ │ │ + components.push(point); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ } │ │ │ │ │ - this.numZoomLevels = len; │ │ │ │ │ - } │ │ │ │ │ - this.minResolution = minResolution; │ │ │ │ │ - if (minResolution) { │ │ │ │ │ - this.maxScale = OpenLayers.Util.getScaleFromResolution( │ │ │ │ │ - minResolution, this.units); │ │ │ │ │ - } │ │ │ │ │ - this.maxResolution = maxResolution; │ │ │ │ │ - if (maxResolution) { │ │ │ │ │ - this.minScale = OpenLayers.Util.getScaleFromResolution( │ │ │ │ │ - maxResolution, this.units); │ │ │ │ │ - } │ │ │ │ │ - }, │ │ │ │ │ + return new OpenLayers.Geometry.MultiPoint(components); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: resolutionsFromScales │ │ │ │ │ - * Derive resolutions from scales. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * scales - {Array(Number)} Scales │ │ │ │ │ - * │ │ │ │ │ - * Returns │ │ │ │ │ - * {Array(Number)} Resolutions │ │ │ │ │ - */ │ │ │ │ │ - resolutionsFromScales: function(scales) { │ │ │ │ │ - if (scales == null) { │ │ │ │ │ - return; │ │ │ │ │ - } │ │ │ │ │ - var resolutions, i, len; │ │ │ │ │ - len = scales.length; │ │ │ │ │ - resolutions = new Array(len); │ │ │ │ │ - for (i = 0; i < len; i++) { │ │ │ │ │ - resolutions[i] = OpenLayers.Util.getResolutionFromScale( │ │ │ │ │ - scales[i], this.units); │ │ │ │ │ - } │ │ │ │ │ - return resolutions; │ │ │ │ │ - }, │ │ │ │ │ + /** │ │ │ │ │ + * Method: parseGeometry.linestring │ │ │ │ │ + * Given a GML node representing a linestring geometry, create an │ │ │ │ │ + * OpenLayers linestring geometry. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * node - {DOMElement} A GML node. │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {} 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 = []; │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: calculateResolutions │ │ │ │ │ - * Calculate resolutions based on the provided properties. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * props - {Object} Properties │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Array({Number})} Array of resolutions. │ │ │ │ │ - */ │ │ │ │ │ - calculateResolutions: function(props) { │ │ │ │ │ + // 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 < coords.length / dim; ++i) { │ │ │ │ │ + j = i * dim; │ │ │ │ │ + x = coords[j]; │ │ │ │ │ + y = coords[j + 1]; │ │ │ │ │ + z = (dim == 2) ? null : coords[j + 2]; │ │ │ │ │ + if (this.xy) { │ │ │ │ │ + points.push(new OpenLayers.Geometry.Point(x, y, z)); │ │ │ │ │ + } else { │ │ │ │ │ + points.push(new OpenLayers.Geometry.Point(y, x, z)); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ - var viewSize, wRes, hRes; │ │ │ │ │ + // look for │ │ │ │ │ + if (coords.length == 0) { │ │ │ │ │ + nodeList = this.getElementsByTagNameNS(node, this.gmlns, │ │ │ │ │ + "coordinates"); │ │ │ │ │ + if (nodeList.length > 0) { │ │ │ │ │ + coordString = this.getChildValue(nodeList[0]); │ │ │ │ │ + coordString = coordString.replace(this.regExes.trimSpace, │ │ │ │ │ + ""); │ │ │ │ │ + coordString = coordString.replace(this.regExes.trimComma, │ │ │ │ │ + ","); │ │ │ │ │ + var pointList = coordString.split(this.regExes.splitSpace); │ │ │ │ │ + for (var i = 0; i < pointList.length; ++i) { │ │ │ │ │ + coords = pointList[i].split(","); │ │ │ │ │ + if (coords.length == 2) { │ │ │ │ │ + coords[2] = null; │ │ │ │ │ + } │ │ │ │ │ + if (this.xy) { │ │ │ │ │ + points.push(new OpenLayers.Geometry.Point(coords[0], │ │ │ │ │ + coords[1], │ │ │ │ │ + coords[2])); │ │ │ │ │ + } else { │ │ │ │ │ + points.push(new OpenLayers.Geometry.Point(coords[1], │ │ │ │ │ + coords[0], │ │ │ │ │ + coords[2])); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ - // determine maxResolution │ │ │ │ │ - var maxResolution = props.maxResolution; │ │ │ │ │ - if (props.minScale != null) { │ │ │ │ │ - maxResolution = │ │ │ │ │ - OpenLayers.Util.getResolutionFromScale(props.minScale, │ │ │ │ │ - this.units); │ │ │ │ │ - } else if (maxResolution == "auto" && this.maxExtent != null) { │ │ │ │ │ - viewSize = this.map.getSize(); │ │ │ │ │ - wRes = this.maxExtent.getWidth() / viewSize.w; │ │ │ │ │ - hRes = this.maxExtent.getHeight() / viewSize.h; │ │ │ │ │ - maxResolution = Math.max(wRes, hRes); │ │ │ │ │ - } │ │ │ │ │ + var line = null; │ │ │ │ │ + if (points.length != 0) { │ │ │ │ │ + if (ring) { │ │ │ │ │ + line = new OpenLayers.Geometry.LinearRing(points); │ │ │ │ │ + } else { │ │ │ │ │ + line = new OpenLayers.Geometry.LineString(points); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + return line; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - // determine minResolution │ │ │ │ │ - var minResolution = props.minResolution; │ │ │ │ │ - if (props.maxScale != null) { │ │ │ │ │ - minResolution = │ │ │ │ │ - OpenLayers.Util.getResolutionFromScale(props.maxScale, │ │ │ │ │ - this.units); │ │ │ │ │ - } else if (props.minResolution == "auto" && this.minExtent != null) { │ │ │ │ │ - viewSize = this.map.getSize(); │ │ │ │ │ - wRes = this.minExtent.getWidth() / viewSize.w; │ │ │ │ │ - hRes = this.minExtent.getHeight() / viewSize.h; │ │ │ │ │ - minResolution = Math.max(wRes, hRes); │ │ │ │ │ - } │ │ │ │ │ + /** │ │ │ │ │ + * Method: parseGeometry.multilinestring │ │ │ │ │ + * Given a GML node representing a multilinestring geometry, create an │ │ │ │ │ + * OpenLayers multilinestring geometry. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * node - {DOMElement} A GML node. │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {} A multilinestring geometry. │ │ │ │ │ + */ │ │ │ │ │ + multilinestring: function(node) { │ │ │ │ │ + var nodeList = this.getElementsByTagNameNS(node, this.gmlns, │ │ │ │ │ + "LineString"); │ │ │ │ │ + var components = []; │ │ │ │ │ + if (nodeList.length > 0) { │ │ │ │ │ + var line; │ │ │ │ │ + for (var i = 0; i < nodeList.length; ++i) { │ │ │ │ │ + line = this.parseGeometry.linestring.apply(this, │ │ │ │ │ + [nodeList[i]]); │ │ │ │ │ + if (line) { │ │ │ │ │ + components.push(line); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + return new OpenLayers.Geometry.MultiLineString(components); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - if (typeof maxResolution !== "number" && │ │ │ │ │ - typeof minResolution !== "number" && │ │ │ │ │ - this.maxExtent != null) { │ │ │ │ │ - // maxResolution for default grid sets assumes that at zoom │ │ │ │ │ - // level zero, the whole world fits on one tile. │ │ │ │ │ - var tileSize = this.map.getTileSize(); │ │ │ │ │ - maxResolution = Math.max( │ │ │ │ │ - this.maxExtent.getWidth() / tileSize.w, │ │ │ │ │ - this.maxExtent.getHeight() / tileSize.h │ │ │ │ │ - ); │ │ │ │ │ - } │ │ │ │ │ + /** │ │ │ │ │ + * Method: parseGeometry.polygon │ │ │ │ │ + * Given a GML node representing a polygon geometry, create an │ │ │ │ │ + * OpenLayers polygon geometry. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * node - {DOMElement} A GML node. │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {} A polygon geometry. │ │ │ │ │ + */ │ │ │ │ │ + polygon: function(node) { │ │ │ │ │ + var nodeList = this.getElementsByTagNameNS(node, this.gmlns, │ │ │ │ │ + "LinearRing"); │ │ │ │ │ + var components = []; │ │ │ │ │ + if (nodeList.length > 0) { │ │ │ │ │ + // this assumes exterior ring first, inner rings after │ │ │ │ │ + var ring; │ │ │ │ │ + for (var i = 0; i < nodeList.length; ++i) { │ │ │ │ │ + ring = this.parseGeometry.linestring.apply(this, │ │ │ │ │ + [nodeList[i], true]); │ │ │ │ │ + if (ring) { │ │ │ │ │ + components.push(ring); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + return new OpenLayers.Geometry.Polygon(components); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - // determine numZoomLevels │ │ │ │ │ - var maxZoomLevel = props.maxZoomLevel; │ │ │ │ │ - var numZoomLevels = props.numZoomLevels; │ │ │ │ │ - if (typeof minResolution === "number" && │ │ │ │ │ - typeof maxResolution === "number" && numZoomLevels === undefined) { │ │ │ │ │ - var ratio = maxResolution / minResolution; │ │ │ │ │ - numZoomLevels = Math.floor(Math.log(ratio) / Math.log(2)) + 1; │ │ │ │ │ - } else if (numZoomLevels === undefined && maxZoomLevel != null) { │ │ │ │ │ - numZoomLevels = maxZoomLevel + 1; │ │ │ │ │ - } │ │ │ │ │ + /** │ │ │ │ │ + * Method: parseGeometry.multipolygon │ │ │ │ │ + * Given a GML node representing a multipolygon geometry, create an │ │ │ │ │ + * OpenLayers multipolygon geometry. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * node - {DOMElement} A GML node. │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {} A multipolygon geometry. │ │ │ │ │ + */ │ │ │ │ │ + multipolygon: function(node) { │ │ │ │ │ + var nodeList = this.getElementsByTagNameNS(node, this.gmlns, │ │ │ │ │ + "Polygon"); │ │ │ │ │ + var components = []; │ │ │ │ │ + if (nodeList.length > 0) { │ │ │ │ │ + var polygon; │ │ │ │ │ + for (var i = 0; i < nodeList.length; ++i) { │ │ │ │ │ + polygon = this.parseGeometry.polygon.apply(this, │ │ │ │ │ + [nodeList[i]]); │ │ │ │ │ + if (polygon) { │ │ │ │ │ + components.push(polygon); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + return new OpenLayers.Geometry.MultiPolygon(components); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - // are we able to calculate resolutions? │ │ │ │ │ - if (typeof numZoomLevels !== "number" || numZoomLevels <= 0 || │ │ │ │ │ - (typeof maxResolution !== "number" && │ │ │ │ │ - typeof minResolution !== "number")) { │ │ │ │ │ - return; │ │ │ │ │ - } │ │ │ │ │ + envelope: function(node) { │ │ │ │ │ + var components = []; │ │ │ │ │ + var coordString; │ │ │ │ │ + var envelope; │ │ │ │ │ │ │ │ │ │ - // now we have numZoomLevels and at least one of maxResolution │ │ │ │ │ - // or minResolution, we can populate the resolutions array │ │ │ │ │ + var lpoint = this.getElementsByTagNameNS(node, this.gmlns, "lowerCorner"); │ │ │ │ │ + if (lpoint.length > 0) { │ │ │ │ │ + var coords = []; │ │ │ │ │ │ │ │ │ │ - var resolutions = new Array(numZoomLevels); │ │ │ │ │ - var base = 2; │ │ │ │ │ - if (typeof minResolution == "number" && │ │ │ │ │ - typeof maxResolution == "number") { │ │ │ │ │ - // if maxResolution and minResolution are set, we calculate │ │ │ │ │ - // the base for exponential scaling that starts at │ │ │ │ │ - // maxResolution and ends at minResolution in numZoomLevels │ │ │ │ │ - // steps. │ │ │ │ │ - base = Math.pow( │ │ │ │ │ - (maxResolution / minResolution), │ │ │ │ │ - (1 / (numZoomLevels - 1)) │ │ │ │ │ - ); │ │ │ │ │ - } │ │ │ │ │ + if (lpoint.length > 0) { │ │ │ │ │ + coordString = lpoint[0].firstChild.nodeValue; │ │ │ │ │ + coordString = coordString.replace(this.regExes.trimSpace, ""); │ │ │ │ │ + coords = coordString.split(this.regExes.splitSpace); │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ - var i; │ │ │ │ │ - if (typeof maxResolution === "number") { │ │ │ │ │ - for (i = 0; i < numZoomLevels; i++) { │ │ │ │ │ - resolutions[i] = maxResolution / Math.pow(base, i); │ │ │ │ │ - } │ │ │ │ │ - } else { │ │ │ │ │ - for (i = 0; i < numZoomLevels; i++) { │ │ │ │ │ - resolutions[numZoomLevels - 1 - i] = │ │ │ │ │ - minResolution * Math.pow(base, i); │ │ │ │ │ + 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]); │ │ │ │ │ + } │ │ │ │ │ } │ │ │ │ │ - } │ │ │ │ │ - │ │ │ │ │ - return resolutions; │ │ │ │ │ - }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIMethod: getResolution │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Float} The currently selected resolution of the map, taken from the │ │ │ │ │ - * resolutions array, indexed by current zoom level. │ │ │ │ │ - */ │ │ │ │ │ - getResolution: function() { │ │ │ │ │ - var zoom = this.map.getZoom(); │ │ │ │ │ - return this.getResolutionForZoom(zoom); │ │ │ │ │ - }, │ │ │ │ │ + var upoint = this.getElementsByTagNameNS(node, this.gmlns, "upperCorner"); │ │ │ │ │ + if (upoint.length > 0) { │ │ │ │ │ + var coords = []; │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIMethod: getExtent │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {} 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(); │ │ │ │ │ - }, │ │ │ │ │ + if (upoint.length > 0) { │ │ │ │ │ + coordString = upoint[0].firstChild.nodeValue; │ │ │ │ │ + coordString = coordString.replace(this.regExes.trimSpace, ""); │ │ │ │ │ + coords = coordString.split(this.regExes.splitSpace); │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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); │ │ │ │ │ + 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]); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ - return this.getZoomForResolution(idealResolution, closest); │ │ │ │ │ - }, │ │ │ │ │ + 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)); │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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 │ │ │ │ │ - }, │ │ │ │ │ + var ring = new OpenLayers.Geometry.LinearRing(components); │ │ │ │ │ + envelope = new OpenLayers.Geometry.Polygon([ring]); │ │ │ │ │ + } │ │ │ │ │ + return envelope; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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)]; │ │ │ │ │ + /** │ │ │ │ │ + * 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])); │ │ │ │ │ + } │ │ │ │ │ } │ │ │ │ │ - return resolution; │ │ │ │ │ + │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: getZoomForResolution │ │ │ │ │ - * │ │ │ │ │ + * Method: parseAttributes │ │ │ │ │ + * │ │ │ │ │ * 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. │ │ │ │ │ - * │ │ │ │ │ + * node - {DOMElement} │ │ │ │ │ + * │ │ │ │ │ * 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. │ │ │ │ │ + * {Object} An attributes object. │ │ │ │ │ */ │ │ │ │ │ - getZoomForResolution: function(resolution, closest) { │ │ │ │ │ - var zoom, i, len; │ │ │ │ │ - if (this.map.fractionalZoom) { │ │ │ │ │ - var lowZoom = 0; │ │ │ │ │ - var highZoom = this.resolutions.length - 1; │ │ │ │ │ - var highRes = this.resolutions[lowZoom]; │ │ │ │ │ - var lowRes = this.resolutions[highZoom]; │ │ │ │ │ - var res; │ │ │ │ │ - for (i = 0, len = this.resolutions.length; i < len; ++i) { │ │ │ │ │ - res = this.resolutions[i]; │ │ │ │ │ - if (res >= resolution) { │ │ │ │ │ - highRes = res; │ │ │ │ │ - lowZoom = i; │ │ │ │ │ - } │ │ │ │ │ - if (res <= resolution) { │ │ │ │ │ - lowRes = res; │ │ │ │ │ - highZoom = i; │ │ │ │ │ - break; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - var dRes = highRes - lowRes; │ │ │ │ │ - if (dRes > 0) { │ │ │ │ │ - zoom = lowZoom + ((highRes - resolution) / dRes); │ │ │ │ │ - } else { │ │ │ │ │ - zoom = lowZoom; │ │ │ │ │ - } │ │ │ │ │ - } else { │ │ │ │ │ - var diff; │ │ │ │ │ - var minDiff = Number.POSITIVE_INFINITY; │ │ │ │ │ - for (i = 0, len = this.resolutions.length; i < len; i++) { │ │ │ │ │ - if (closest) { │ │ │ │ │ - diff = Math.abs(this.resolutions[i] - resolution); │ │ │ │ │ - if (diff > minDiff) { │ │ │ │ │ - break; │ │ │ │ │ - } │ │ │ │ │ - minDiff = diff; │ │ │ │ │ - } else { │ │ │ │ │ - if (this.resolutions[i] < resolution) { │ │ │ │ │ - break; │ │ │ │ │ + parseAttributes: function(node) { │ │ │ │ │ + var attributes = {}; │ │ │ │ │ + // assume attributes are children of the first type 1 child │ │ │ │ │ + var childNode = node.firstChild; │ │ │ │ │ + var children, i, child, grandchildren, grandchild, name, value; │ │ │ │ │ + while (childNode) { │ │ │ │ │ + if (childNode.nodeType == 1) { │ │ │ │ │ + // attributes are type 1 children with one type 3 child │ │ │ │ │ + children = childNode.childNodes; │ │ │ │ │ + for (i = 0; i < children.length; ++i) { │ │ │ │ │ + child = children[i]; │ │ │ │ │ + if (child.nodeType == 1) { │ │ │ │ │ + grandchildren = child.childNodes; │ │ │ │ │ + if (grandchildren.length == 1) { │ │ │ │ │ + grandchild = grandchildren[0]; │ │ │ │ │ + if (grandchild.nodeType == 3 || │ │ │ │ │ + grandchild.nodeType == 4) { │ │ │ │ │ + name = (child.prefix) ? │ │ │ │ │ + child.nodeName.split(":")[1] : │ │ │ │ │ + child.nodeName; │ │ │ │ │ + value = grandchild.nodeValue.replace( │ │ │ │ │ + this.regExes.trimSpace, ""); │ │ │ │ │ + attributes[name] = value; │ │ │ │ │ + } │ │ │ │ │ + } else { │ │ │ │ │ + // If child has no childNodes (grandchildren), │ │ │ │ │ + // set an attribute with null value. │ │ │ │ │ + // e.g. becomes │ │ │ │ │ + // {fieldname: null} │ │ │ │ │ + attributes[child.nodeName.split(":").pop()] = null; │ │ │ │ │ + } │ │ │ │ │ } │ │ │ │ │ } │ │ │ │ │ + break; │ │ │ │ │ } │ │ │ │ │ - zoom = Math.max(0, i - 1); │ │ │ │ │ + childNode = childNode.nextSibling; │ │ │ │ │ } │ │ │ │ │ - return zoom; │ │ │ │ │ + return attributes; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: getLonLatFromViewPortPx │ │ │ │ │ + * APIMethod: write │ │ │ │ │ + * Generate a GML document string given a list of features. │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * viewPortPx - {|Object} An OpenLayers.Pixel or │ │ │ │ │ - * an object with a 'x' │ │ │ │ │ - * and 'y' properties. │ │ │ │ │ + * features - {Array()} List of features to │ │ │ │ │ + * serialize into a string. │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {} An OpenLayers.LonLat which is the passed-in │ │ │ │ │ - * view port , translated into lon/lat by the layer. │ │ │ │ │ + * {String} A string representing the GML document. │ │ │ │ │ */ │ │ │ │ │ - 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); │ │ │ │ │ - } │ │ │ │ │ + write: function(features) { │ │ │ │ │ + if (!(OpenLayers.Util.isArray(features))) { │ │ │ │ │ + features = [features]; │ │ │ │ │ } │ │ │ │ │ - return lonlat; │ │ │ │ │ + var gml = this.createElementNS("http://www.opengis.net/wfs", │ │ │ │ │ + "wfs:" + this.collectionName); │ │ │ │ │ + for (var i = 0; i < features.length; i++) { │ │ │ │ │ + gml.appendChild(this.createFeatureXML(features[i])); │ │ │ │ │ + } │ │ │ │ │ + return OpenLayers.Format.XML.prototype.write.apply(this, [gml]); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIMethod: getViewPortPxFromLonLat │ │ │ │ │ - * Returns a pixel location given a map location. This method will return │ │ │ │ │ - * fractional pixel values. │ │ │ │ │ - * │ │ │ │ │ + /** │ │ │ │ │ + * Method: createFeatureXML │ │ │ │ │ + * Accept an OpenLayers.Feature.Vector, and build a GML node for it. │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * lonlat - {|Object} An OpenLayers.LonLat or │ │ │ │ │ - * an object with a 'lon' │ │ │ │ │ - * and 'lat' properties. │ │ │ │ │ + * feature - {} The feature to be built as GML. │ │ │ │ │ * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {} An which is the passed-in │ │ │ │ │ - * lonlat translated into view port pixels. │ │ │ │ │ + * Returns: │ │ │ │ │ + * {DOMElement} A node reprensting the feature in GML. │ │ │ │ │ */ │ │ │ │ │ - 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)) │ │ │ │ │ - ); │ │ │ │ │ + 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); │ │ │ │ │ } │ │ │ │ │ - return px; │ │ │ │ │ + featureNode.appendChild(featureContainer); │ │ │ │ │ + return featureNode; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: setOpacity │ │ │ │ │ - * Sets the opacity for the entire layer (all images) │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * opacity - {Float} │ │ │ │ │ + * APIMethod: buildGeometryNode │ │ │ │ │ */ │ │ │ │ │ - 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" │ │ │ │ │ - }); │ │ │ │ │ - } │ │ │ │ │ + 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]); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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 - {} │ │ │ │ │ + * Property: buildGeometry │ │ │ │ │ + * Object containing methods to do the actual geometry node building │ │ │ │ │ + * based on geometry type. │ │ │ │ │ */ │ │ │ │ │ - adjustBounds: function(bounds) { │ │ │ │ │ + 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"); │ │ │ │ │ │ │ │ │ │ - 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); │ │ │ │ │ - } │ │ │ │ │ + /** │ │ │ │ │ + * 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; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - 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); │ │ │ │ │ + /** │ │ │ │ │ + * 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 < points.length; i++) { │ │ │ │ │ + pointMember = this.createElementNS(this.gmlns, │ │ │ │ │ + "gml:pointMember"); │ │ │ │ │ + pointGeom = this.buildGeometry.point.apply(this, │ │ │ │ │ + [points[i]]); │ │ │ │ │ + pointMember.appendChild(pointGeom); │ │ │ │ │ + gml.appendChild(pointMember); │ │ │ │ │ + } │ │ │ │ │ + return gml; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - } │ │ │ │ │ - return bounds; │ │ │ │ │ - }, │ │ │ │ │ + /** │ │ │ │ │ + * Method: buildGeometry.linestring │ │ │ │ │ + * Given an OpenLayers linestring geometry, create a GML linestring. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * geometry - {} 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; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Layer" │ │ │ │ │ -}); │ │ │ │ │ -/* ====================================================================== │ │ │ │ │ - OpenLayers/Symbolizer.js │ │ │ │ │ - ====================================================================== */ │ │ │ │ │ + /** │ │ │ │ │ + * 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 < lines.length; ++i) { │ │ │ │ │ + lineMember = this.createElementNS(this.gmlns, │ │ │ │ │ + "gml:lineStringMember"); │ │ │ │ │ + lineGeom = this.buildGeometry.linestring.apply(this, │ │ │ │ │ + [lines[i]]); │ │ │ │ │ + lineMember.appendChild(lineGeom); │ │ │ │ │ + gml.appendChild(lineMember); │ │ │ │ │ + } │ │ │ │ │ + return gml; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ - * full text of the license. */ │ │ │ │ │ + /** │ │ │ │ │ + * Method: buildGeometry.linearring │ │ │ │ │ + * Given an OpenLayers linearring geometry, create a GML linearring. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * geometry - {} 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; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ │ - */ │ │ │ │ │ + /** │ │ │ │ │ + * 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 < rings.length; ++i) { │ │ │ │ │ + type = (i == 0) ? "outerBoundaryIs" : "innerBoundaryIs"; │ │ │ │ │ + ringMember = this.createElementNS(this.gmlns, │ │ │ │ │ + "gml:" + type); │ │ │ │ │ + ringGeom = this.buildGeometry.linearring.apply(this, │ │ │ │ │ + [rings[i]]); │ │ │ │ │ + ringMember.appendChild(ringGeom); │ │ │ │ │ + gml.appendChild(ringMember); │ │ │ │ │ + } │ │ │ │ │ + return gml; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * Class: OpenLayers.Symbolizer │ │ │ │ │ - * Base class representing a symbolizer used for feature rendering. │ │ │ │ │ - */ │ │ │ │ │ -OpenLayers.Symbolizer = OpenLayers.Class({ │ │ │ │ │ + /** │ │ │ │ │ + * Method: buildGeometry.multipolygon │ │ │ │ │ + * Given an OpenLayers multipolygon geometry, create a GML multipolygon. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * geometry - {} A multipolygon │ │ │ │ │ + * geometry. │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {DOMElement} A GML multipolygon node. │ │ │ │ │ + */ │ │ │ │ │ + multipolygon: function(geometry) { │ │ │ │ │ + var gml = this.createElementNS(this.gmlns, "gml:MultiPolygon"); │ │ │ │ │ + var polys = geometry.components; │ │ │ │ │ + var polyMember, polyGeom; │ │ │ │ │ + for (var i = 0; i < polys.length; ++i) { │ │ │ │ │ + polyMember = this.createElementNS(this.gmlns, │ │ │ │ │ + "gml:polygonMember"); │ │ │ │ │ + polyGeom = this.buildGeometry.polygon.apply(this, │ │ │ │ │ + [polys[i]]); │ │ │ │ │ + polyMember.appendChild(polyGeom); │ │ │ │ │ + gml.appendChild(polyMember); │ │ │ │ │ + } │ │ │ │ │ + return gml; │ │ │ │ │ │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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, │ │ │ │ │ + /** │ │ │ │ │ + * Method: buildGeometry.bounds │ │ │ │ │ + * Given an OpenLayers bounds, create a GML box. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * bounds - {} A bounds object. │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {DOMElement} A GML box node. │ │ │ │ │ + */ │ │ │ │ │ + bounds: function(bounds) { │ │ │ │ │ + var gml = this.createElementNS(this.gmlns, "gml:Box"); │ │ │ │ │ + gml.appendChild(this.buildCoordinatesNode(bounds)); │ │ │ │ │ + return gml; │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Constructor: OpenLayers.Symbolizer │ │ │ │ │ - * Instances of this class are not useful. See one of the subclasses. │ │ │ │ │ + * Method: buildCoordinates │ │ │ │ │ + * builds the coordinates XmlNode │ │ │ │ │ + * (code) │ │ │ │ │ + * ... │ │ │ │ │ + * (end) │ │ │ │ │ * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * config - {Object} An object containing properties to be set on the │ │ │ │ │ - * symbolizer. Any documented symbolizer property can be set at │ │ │ │ │ - * construction. │ │ │ │ │ + * Parameters: │ │ │ │ │ + * geometry - {} │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * A new symbolizer. │ │ │ │ │ + * {XmlNode} created xmlNode │ │ │ │ │ */ │ │ │ │ │ - initialize: function(config) { │ │ │ │ │ - OpenLayers.Util.extend(this, config); │ │ │ │ │ - }, │ │ │ │ │ + buildCoordinatesNode: function(geometry) { │ │ │ │ │ + var coordinatesNode = this.createElementNS(this.gmlns, │ │ │ │ │ + "gml:coordinates"); │ │ │ │ │ + coordinatesNode.setAttribute("decimal", "."); │ │ │ │ │ + coordinatesNode.setAttribute("cs", ","); │ │ │ │ │ + coordinatesNode.setAttribute("ts", " "); │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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)); │ │ │ │ │ - }, │ │ │ │ │ + var parts = []; │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Symbolizer" │ │ │ │ │ + if (geometry instanceof OpenLayers.Bounds) { │ │ │ │ │ + parts.push(geometry.left + "," + geometry.bottom); │ │ │ │ │ + parts.push(geometry.right + "," + geometry.top); │ │ │ │ │ + } else { │ │ │ │ │ + var points = (geometry.components) ? geometry.components : [geometry]; │ │ │ │ │ + for (var i = 0; i < points.length; i++) { │ │ │ │ │ + parts.push(points[i].x + "," + points[i].y); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ -}); │ │ │ │ │ + var txtNode = this.createTextNode(parts.join(" ")); │ │ │ │ │ + coordinatesNode.appendChild(txtNode); │ │ │ │ │ + │ │ │ │ │ + return coordinatesNode; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ + CLASS_NAME: "OpenLayers.Format.GML" │ │ │ │ │ +}); │ │ │ │ │ /* ====================================================================== │ │ │ │ │ - OpenLayers/Icon.js │ │ │ │ │ + OpenLayers/Format/GML/Base.js │ │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ │ + * @requires OpenLayers/Format/XML.js │ │ │ │ │ + * @requires OpenLayers/Format/GML.js │ │ │ │ │ */ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Class: OpenLayers.Icon │ │ │ │ │ - * │ │ │ │ │ - * The icon represents a graphical icon on the screen. Typically used in │ │ │ │ │ - * conjunction with a to represent markers on a screen. │ │ │ │ │ - * │ │ │ │ │ - * An icon has a url, size and position. It also contains an offset which │ │ │ │ │ - * allows the center point to be represented correctly. This can be │ │ │ │ │ - * provided either as a fixed offset or a function provided to calculate │ │ │ │ │ - * the desired offset. │ │ │ │ │ - * │ │ │ │ │ + * Though required in the full build, if the GML format is excluded, we set │ │ │ │ │ + * the namespace here. │ │ │ │ │ */ │ │ │ │ │ -OpenLayers.Icon = OpenLayers.Class({ │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Property: url │ │ │ │ │ - * {String} image url │ │ │ │ │ - */ │ │ │ │ │ - url: null, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Property: size │ │ │ │ │ - * {|Object} An OpenLayers.Size or │ │ │ │ │ - * an object with a 'w' and 'h' properties. │ │ │ │ │ - */ │ │ │ │ │ - size: null, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Property: offset │ │ │ │ │ - * {|Object} distance in pixels to offset the │ │ │ │ │ - * image when being rendered. An OpenLayers.Pixel or an object │ │ │ │ │ - * with a 'x' and 'y' properties. │ │ │ │ │ - */ │ │ │ │ │ - offset: null, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Property: calculateOffset │ │ │ │ │ - * {Function} Function to calculate the offset (based on the size) │ │ │ │ │ - */ │ │ │ │ │ - calculateOffset: null, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Property: imageDiv │ │ │ │ │ - * {DOMElement} │ │ │ │ │ - */ │ │ │ │ │ - imageDiv: null, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Property: px │ │ │ │ │ - * {|Object} An OpenLayers.Pixel or an object │ │ │ │ │ - * with a 'x' and 'y' properties. │ │ │ │ │ - */ │ │ │ │ │ - px: null, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Constructor: OpenLayers.Icon │ │ │ │ │ - * Creates an icon, which is an image tag in a div. │ │ │ │ │ - * │ │ │ │ │ - * url - {String} │ │ │ │ │ - * size - {|Object} An OpenLayers.Size or an │ │ │ │ │ - * object with a 'w' and 'h' │ │ │ │ │ - * properties. │ │ │ │ │ - * offset - {|Object} An OpenLayers.Pixel or an │ │ │ │ │ - * object with a 'x' and 'y' │ │ │ │ │ - * properties. │ │ │ │ │ - * calculateOffset - {Function} │ │ │ │ │ - */ │ │ │ │ │ - initialize: function(url, size, offset, calculateOffset) { │ │ │ │ │ - this.url = url; │ │ │ │ │ - this.size = size || { │ │ │ │ │ - w: 20, │ │ │ │ │ - h: 20 │ │ │ │ │ - }; │ │ │ │ │ - this.offset = offset || { │ │ │ │ │ - x: -(this.size.w / 2), │ │ │ │ │ - y: -(this.size.h / 2) │ │ │ │ │ - }; │ │ │ │ │ - this.calculateOffset = calculateOffset; │ │ │ │ │ - │ │ │ │ │ - var id = OpenLayers.Util.createUniqueID("OL_Icon_"); │ │ │ │ │ - this.imageDiv = OpenLayers.Util.createAlphaImageDiv(id); │ │ │ │ │ - }, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Method: destroy │ │ │ │ │ - * Nullify references and remove event listeners to prevent circular │ │ │ │ │ - * references and memory leaks │ │ │ │ │ - */ │ │ │ │ │ - destroy: function() { │ │ │ │ │ - // erase any drawn elements │ │ │ │ │ - this.erase(); │ │ │ │ │ - │ │ │ │ │ - OpenLayers.Event.stopObservingElement(this.imageDiv.firstChild); │ │ │ │ │ - this.imageDiv.innerHTML = ""; │ │ │ │ │ - this.imageDiv = null; │ │ │ │ │ - }, │ │ │ │ │ +if (!OpenLayers.Format.GML) { │ │ │ │ │ + OpenLayers.Format.GML = {}; │ │ │ │ │ +} │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: clone │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {} A fresh copy of the icon. │ │ │ │ │ - */ │ │ │ │ │ - clone: function() { │ │ │ │ │ - return new OpenLayers.Icon(this.url, │ │ │ │ │ - this.size, │ │ │ │ │ - this.offset, │ │ │ │ │ - this.calculateOffset); │ │ │ │ │ - }, │ │ │ │ │ +/** │ │ │ │ │ + * Class: OpenLayers.Format.GML.Base │ │ │ │ │ + * Superclass for GML parsers. │ │ │ │ │ + * │ │ │ │ │ + * Inherits from: │ │ │ │ │ + * - │ │ │ │ │ + */ │ │ │ │ │ +OpenLayers.Format.GML.Base = OpenLayers.Class(OpenLayers.Format.XML, { │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: setSize │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * size - {|Object} An OpenLayers.Size or │ │ │ │ │ - * an object with a 'w' and 'h' properties. │ │ │ │ │ + * Property: namespaces │ │ │ │ │ + * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ │ */ │ │ │ │ │ - setSize: function(size) { │ │ │ │ │ - if (size != null) { │ │ │ │ │ - this.size = size; │ │ │ │ │ - } │ │ │ │ │ - this.draw(); │ │ │ │ │ + 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 │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: setUrl │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * url - {String} │ │ │ │ │ - */ │ │ │ │ │ - setUrl: function(url) { │ │ │ │ │ - if (url != null) { │ │ │ │ │ - this.url = url; │ │ │ │ │ - } │ │ │ │ │ - this.draw(); │ │ │ │ │ - }, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Method: draw │ │ │ │ │ - * Move the div to the given pixel. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * px - {|Object} An OpenLayers.Pixel or an │ │ │ │ │ - * object with a 'x' and 'y' properties. │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {DOMElement} A new DOM Image of this icon set at the location passed-in │ │ │ │ │ + * Property: defaultPrefix │ │ │ │ │ */ │ │ │ │ │ - draw: function(px) { │ │ │ │ │ - OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, │ │ │ │ │ - null, │ │ │ │ │ - null, │ │ │ │ │ - this.size, │ │ │ │ │ - this.url, │ │ │ │ │ - "absolute"); │ │ │ │ │ - this.moveTo(px); │ │ │ │ │ - return this.imageDiv; │ │ │ │ │ - }, │ │ │ │ │ + defaultPrefix: "gml", │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: erase │ │ │ │ │ - * Erase the underlying image element. │ │ │ │ │ + /** │ │ │ │ │ + * Property: schemaLocation │ │ │ │ │ + * {String} Schema location for a particular minor version. │ │ │ │ │ */ │ │ │ │ │ - erase: function() { │ │ │ │ │ - if (this.imageDiv != null && this.imageDiv.parentNode != null) { │ │ │ │ │ - OpenLayers.Element.remove(this.imageDiv); │ │ │ │ │ - } │ │ │ │ │ - }, │ │ │ │ │ + schemaLocation: null, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: setOpacity │ │ │ │ │ - * Change the icon's opacity │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * opacity - {float} │ │ │ │ │ + /** │ │ │ │ │ + * APIProperty: featureType │ │ │ │ │ + * {Array(String) or String} The local (without prefix) feature typeName(s). │ │ │ │ │ */ │ │ │ │ │ - setOpacity: function(opacity) { │ │ │ │ │ - OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, null, null, │ │ │ │ │ - null, null, null, null, opacity); │ │ │ │ │ - │ │ │ │ │ - }, │ │ │ │ │ + featureType: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: moveTo │ │ │ │ │ - * move icon to passed in px. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * px - {|Object} the pixel position to move to. │ │ │ │ │ - * An OpenLayers.Pixel or an object with a 'x' and 'y' properties. │ │ │ │ │ + * APIProperty: featureNS │ │ │ │ │ + * {String} The feature namespace. Must be set in the options at │ │ │ │ │ + * construction. │ │ │ │ │ */ │ │ │ │ │ - moveTo: function(px) { │ │ │ │ │ - //if no px passed in, use stored location │ │ │ │ │ - if (px != null) { │ │ │ │ │ - this.px = px; │ │ │ │ │ - } │ │ │ │ │ - │ │ │ │ │ - if (this.imageDiv != null) { │ │ │ │ │ - if (this.px == null) { │ │ │ │ │ - this.display(false); │ │ │ │ │ - } else { │ │ │ │ │ - if (this.calculateOffset) { │ │ │ │ │ - this.offset = this.calculateOffset(this.size); │ │ │ │ │ - } │ │ │ │ │ - OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, { │ │ │ │ │ - x: this.px.x + this.offset.x, │ │ │ │ │ - y: this.px.y + this.offset.y │ │ │ │ │ - }); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - }, │ │ │ │ │ + featureNS: null, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: display │ │ │ │ │ - * Hide or show the icon │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * display - {Boolean} │ │ │ │ │ + /** │ │ │ │ │ + * APIProperty: geometry │ │ │ │ │ + * {String} Name of geometry element. Defaults to "geometry". If null, it │ │ │ │ │ + * will be set on when the first geometry is parsed. │ │ │ │ │ */ │ │ │ │ │ - display: function(display) { │ │ │ │ │ - this.imageDiv.style.display = (display) ? "" : "none"; │ │ │ │ │ - }, │ │ │ │ │ - │ │ │ │ │ + geometryName: "geometry", │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: isDrawn │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Boolean} Whether or not the icon is drawn. │ │ │ │ │ + * APIProperty: extractAttributes │ │ │ │ │ + * {Boolean} Extract attributes from GML. Default is true. │ │ │ │ │ */ │ │ │ │ │ - isDrawn: function() { │ │ │ │ │ - // nodeType 11 for ie, whose nodes *always* have a parentNode │ │ │ │ │ - // (of type document fragment) │ │ │ │ │ - var isDrawn = (this.imageDiv && this.imageDiv.parentNode && │ │ │ │ │ - (this.imageDiv.parentNode.nodeType != 11)); │ │ │ │ │ - │ │ │ │ │ - return isDrawn; │ │ │ │ │ - }, │ │ │ │ │ - │ │ │ │ │ - CLASS_NAME: "OpenLayers.Icon" │ │ │ │ │ -}); │ │ │ │ │ -/* ====================================================================== │ │ │ │ │ - OpenLayers/Kinetic.js │ │ │ │ │ - ====================================================================== */ │ │ │ │ │ - │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ - * full text of the license. */ │ │ │ │ │ - │ │ │ │ │ -/** │ │ │ │ │ - * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ │ - * @requires OpenLayers/Animation.js │ │ │ │ │ - */ │ │ │ │ │ - │ │ │ │ │ -OpenLayers.Kinetic = OpenLayers.Class({ │ │ │ │ │ + extractAttributes: true, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: threshold │ │ │ │ │ - * In most cases changing the threshold isn't needed. │ │ │ │ │ - * In px/ms, default to 0. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - threshold: 0, │ │ │ │ │ + srsName: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: deceleration │ │ │ │ │ - * {Float} the deseleration in px/ms², default to 0.0035. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - deceleration: 0.0035, │ │ │ │ │ + xy: true, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: nbPoints │ │ │ │ │ - * {Integer} the number of points we use to calculate the kinetic │ │ │ │ │ - * initial values. │ │ │ │ │ + * Property: geometryTypes │ │ │ │ │ + * {Object} Maps OpenLayers geometry class names to GML element names. │ │ │ │ │ + * Use before accessing this property. │ │ │ │ │ */ │ │ │ │ │ - nbPoints: 100, │ │ │ │ │ + geometryTypes: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: delay │ │ │ │ │ - * {Float} time to consider to calculate the kinetic initial values. │ │ │ │ │ - * In ms, default to 200. │ │ │ │ │ + * Property: singleFeatureType │ │ │ │ │ + * {Boolean} True if there is only 1 featureType, and not an array │ │ │ │ │ + * of featuretypes. │ │ │ │ │ */ │ │ │ │ │ - delay: 200, │ │ │ │ │ + singleFeatureType: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: points │ │ │ │ │ - * List of points use to calculate the kinetic initial values. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - points: undefined, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: timerId │ │ │ │ │ - * ID of the timer. │ │ │ │ │ + * Property: regExes │ │ │ │ │ + * Compiled regular expressions for manipulating strings. │ │ │ │ │ */ │ │ │ │ │ - timerId: undefined, │ │ │ │ │ + regExes: { │ │ │ │ │ + trimSpace: (/^\s*|\s*$/g), │ │ │ │ │ + removeSpace: (/\s*/g), │ │ │ │ │ + splitSpace: (/\s+/), │ │ │ │ │ + trimComma: (/\s*,\s*/g), │ │ │ │ │ + featureMember: (/^(.*:)?featureMembers?$/) │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Constructor: OpenLayers.Kinetic │ │ │ │ │ + * Constructor: OpenLayers.Format.GML.Base │ │ │ │ │ + * Instances of this class are not created directly. Use the │ │ │ │ │ + * or constructor │ │ │ │ │ + * instead. │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * options - {Object} │ │ │ │ │ + * 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.Util.extend(this, options); │ │ │ │ │ - }, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Method: begin │ │ │ │ │ - * Begins the dragging. │ │ │ │ │ - */ │ │ │ │ │ - begin: function() { │ │ │ │ │ - OpenLayers.Animation.stop(this.timerId); │ │ │ │ │ - this.timerId = undefined; │ │ │ │ │ - this.points = []; │ │ │ │ │ + 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: update │ │ │ │ │ - * Updates during the dragging. │ │ │ │ │ + * Method: read │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * xy - {} The new position. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - update: function(xy) { │ │ │ │ │ - this.points.unshift({ │ │ │ │ │ - xy: xy, │ │ │ │ │ - tick: new Date().getTime() │ │ │ │ │ - }); │ │ │ │ │ - if (this.points.length > this.nbPoints) { │ │ │ │ │ - this.points.pop(); │ │ │ │ │ + read: function(data) { │ │ │ │ │ + if (typeof data == "string") { │ │ │ │ │ + data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); │ │ │ │ │ + } │ │ │ │ │ + if (data && data.nodeType == 9) { │ │ │ │ │ + data = data.documentElement; │ │ │ │ │ + } │ │ │ │ │ + var features = []; │ │ │ │ │ + this.readNode(data, { │ │ │ │ │ + features: features │ │ │ │ │ + }, true); │ │ │ │ │ + if (features.length == 0) { │ │ │ │ │ + // look for gml:featureMember elements │ │ │ │ │ + var elements = this.getElementsByTagNameNS( │ │ │ │ │ + data, this.namespaces.gml, "featureMember" │ │ │ │ │ + ); │ │ │ │ │ + if (elements.length) { │ │ │ │ │ + for (var i = 0, len = elements.length; i < len; ++i) { │ │ │ │ │ + this.readNode(elements[i], { │ │ │ │ │ + features: features │ │ │ │ │ + }, true); │ │ │ │ │ + } │ │ │ │ │ + } else { │ │ │ │ │ + // look for gml:featureMembers elements (this is v3, but does no harm here) │ │ │ │ │ + var elements = this.getElementsByTagNameNS( │ │ │ │ │ + data, this.namespaces.gml, "featureMembers" │ │ │ │ │ + ); │ │ │ │ │ + if (elements.length) { │ │ │ │ │ + // there can be only one │ │ │ │ │ + this.readNode(elements[0], { │ │ │ │ │ + features: features │ │ │ │ │ + }, true); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ } │ │ │ │ │ + return features; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: end │ │ │ │ │ - * Ends the dragging, start the kinetic. │ │ │ │ │ + * 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: │ │ │ │ │ - * xy - {} The last position. │ │ │ │ │ + * 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} An object with two properties: "speed", and "theta". The │ │ │ │ │ - * "speed" and "theta" values are to be passed to the move │ │ │ │ │ - * function when starting the animation. │ │ │ │ │ + * {Object} The input object, modified (or a new one if none was provided). │ │ │ │ │ */ │ │ │ │ │ - end: function(xy) { │ │ │ │ │ - var last, now = new Date().getTime(); │ │ │ │ │ - for (var i = 0, l = this.points.length, point; i < l; i++) { │ │ │ │ │ - point = this.points[i]; │ │ │ │ │ - if (now - point.tick > this.delay) { │ │ │ │ │ - break; │ │ │ │ │ - } │ │ │ │ │ - last = point; │ │ │ │ │ - } │ │ │ │ │ - if (!last) { │ │ │ │ │ - return; │ │ │ │ │ + readNode: function(node, obj, first) { │ │ │ │ │ + // on subsequent calls of format.read(), we want to reset auto- │ │ │ │ │ + // configured properties and auto-configure again. │ │ │ │ │ + if (first === true && this.autoConfig === true) { │ │ │ │ │ + this.featureType = null; │ │ │ │ │ + delete this.namespaceAlias[this.featureNS]; │ │ │ │ │ + delete this.namespaces["feature"]; │ │ │ │ │ + this.featureNS = null; │ │ │ │ │ } │ │ │ │ │ - var time = new Date().getTime() - last.tick; │ │ │ │ │ - var dist = Math.sqrt(Math.pow(xy.x - last.xy.x, 2) + │ │ │ │ │ - Math.pow(xy.y - last.xy.y, 2)); │ │ │ │ │ - var speed = dist / time; │ │ │ │ │ - if (speed == 0 || speed < this.threshold) { │ │ │ │ │ - return; │ │ │ │ │ + // featureType auto-configuration │ │ │ │ │ + if (!this.featureNS && (!(node.prefix in this.namespaces) && │ │ │ │ │ + node.parentNode.namespaceURI == this.namespaces["gml"] && │ │ │ │ │ + this.regExes.featureMember.test(node.parentNode.nodeName))) { │ │ │ │ │ + this.featureType = node.nodeName.split(":").pop(); │ │ │ │ │ + this.setNamespace("feature", node.namespaceURI); │ │ │ │ │ + this.featureNS = node.namespaceURI; │ │ │ │ │ + this.autoConfig = true; │ │ │ │ │ } │ │ │ │ │ - var theta = Math.asin((xy.y - last.xy.y) / dist); │ │ │ │ │ - if (last.xy.x <= xy.x) { │ │ │ │ │ - theta = Math.PI - theta; │ │ │ │ │ + return OpenLayers.Format.XML.prototype.readNode.apply(this, [node, obj]); │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Property: readers │ │ │ │ │ + * Contains public functions, grouped by namespace prefix, that will │ │ │ │ │ + * be applied when a namespaced node is found matching the function │ │ │ │ │ + * name. The function will be applied in the scope of this parser │ │ │ │ │ + * with two arguments: the node being read and a context object passed │ │ │ │ │ + * from the parent. │ │ │ │ │ + */ │ │ │ │ │ + readers: { │ │ │ │ │ + "gml": { │ │ │ │ │ + "_inherit": function(node, obj, container) { │ │ │ │ │ + // To be implemented by version specific parsers │ │ │ │ │ + }, │ │ │ │ │ + "featureMember": function(node, obj) { │ │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ │ + }, │ │ │ │ │ + "featureMembers": function(node, obj) { │ │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ │ + }, │ │ │ │ │ + "name": function(node, obj) { │ │ │ │ │ + obj.name = this.getChildValue(node); │ │ │ │ │ + }, │ │ │ │ │ + "boundedBy": function(node, obj) { │ │ │ │ │ + var container = {}; │ │ │ │ │ + this.readChildNodes(node, container); │ │ │ │ │ + if (container.components && container.components.length > 0) { │ │ │ │ │ + obj.bounds = container.components[0]; │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ + "Point": function(node, container) { │ │ │ │ │ + var obj = { │ │ │ │ │ + points: [] │ │ │ │ │ + }; │ │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ │ + if (!container.components) { │ │ │ │ │ + container.components = []; │ │ │ │ │ + } │ │ │ │ │ + container.components.push(obj.points[0]); │ │ │ │ │ + }, │ │ │ │ │ + "coordinates": function(node, obj) { │ │ │ │ │ + var str = this.getChildValue(node).replace( │ │ │ │ │ + this.regExes.trimSpace, "" │ │ │ │ │ + ); │ │ │ │ │ + str = str.replace(this.regExes.trimComma, ","); │ │ │ │ │ + var pointList = str.split(this.regExes.splitSpace); │ │ │ │ │ + var coords; │ │ │ │ │ + var numPoints = pointList.length; │ │ │ │ │ + var points = new Array(numPoints); │ │ │ │ │ + for (var i = 0; i < numPoints; ++i) { │ │ │ │ │ + coords = pointList[i].split(","); │ │ │ │ │ + if (this.xy) { │ │ │ │ │ + points[i] = new OpenLayers.Geometry.Point( │ │ │ │ │ + coords[0], coords[1], coords[2] │ │ │ │ │ + ); │ │ │ │ │ + } else { │ │ │ │ │ + points[i] = new OpenLayers.Geometry.Point( │ │ │ │ │ + coords[1], coords[0], coords[2] │ │ │ │ │ + ); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + obj.points = points; │ │ │ │ │ + }, │ │ │ │ │ + "coord": function(node, obj) { │ │ │ │ │ + var coord = {}; │ │ │ │ │ + this.readChildNodes(node, coord); │ │ │ │ │ + if (!obj.points) { │ │ │ │ │ + obj.points = []; │ │ │ │ │ + } │ │ │ │ │ + obj.points.push(new OpenLayers.Geometry.Point( │ │ │ │ │ + coord.x, coord.y, coord.z │ │ │ │ │ + )); │ │ │ │ │ + }, │ │ │ │ │ + "X": function(node, coord) { │ │ │ │ │ + coord.x = this.getChildValue(node); │ │ │ │ │ + }, │ │ │ │ │ + "Y": function(node, coord) { │ │ │ │ │ + coord.y = this.getChildValue(node); │ │ │ │ │ + }, │ │ │ │ │ + "Z": function(node, coord) { │ │ │ │ │ + coord.z = this.getChildValue(node); │ │ │ │ │ + }, │ │ │ │ │ + "MultiPoint": function(node, container) { │ │ │ │ │ + var obj = { │ │ │ │ │ + components: [] │ │ │ │ │ + }; │ │ │ │ │ + this.readers.gml._inherit.apply(this, [node, obj, container]); │ │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ │ + container.components = [ │ │ │ │ │ + new OpenLayers.Geometry.MultiPoint(obj.components) │ │ │ │ │ + ]; │ │ │ │ │ + }, │ │ │ │ │ + "pointMember": function(node, obj) { │ │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ │ + }, │ │ │ │ │ + "LineString": function(node, container) { │ │ │ │ │ + var obj = {}; │ │ │ │ │ + this.readers.gml._inherit.apply(this, [node, obj, container]); │ │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ │ + if (!container.components) { │ │ │ │ │ + container.components = []; │ │ │ │ │ + } │ │ │ │ │ + container.components.push( │ │ │ │ │ + new OpenLayers.Geometry.LineString(obj.points) │ │ │ │ │ + ); │ │ │ │ │ + }, │ │ │ │ │ + "MultiLineString": function(node, container) { │ │ │ │ │ + var obj = { │ │ │ │ │ + components: [] │ │ │ │ │ + }; │ │ │ │ │ + this.readers.gml._inherit.apply(this, [node, obj, container]); │ │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ │ + container.components = [ │ │ │ │ │ + new OpenLayers.Geometry.MultiLineString(obj.components) │ │ │ │ │ + ]; │ │ │ │ │ + }, │ │ │ │ │ + "lineStringMember": function(node, obj) { │ │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ │ + }, │ │ │ │ │ + "Polygon": function(node, container) { │ │ │ │ │ + var obj = { │ │ │ │ │ + outer: null, │ │ │ │ │ + inner: [] │ │ │ │ │ + }; │ │ │ │ │ + this.readers.gml._inherit.apply(this, [node, obj, container]); │ │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ │ + obj.inner.unshift(obj.outer); │ │ │ │ │ + if (!container.components) { │ │ │ │ │ + container.components = []; │ │ │ │ │ + } │ │ │ │ │ + container.components.push( │ │ │ │ │ + new OpenLayers.Geometry.Polygon(obj.inner) │ │ │ │ │ + ); │ │ │ │ │ + }, │ │ │ │ │ + "LinearRing": function(node, obj) { │ │ │ │ │ + var container = {}; │ │ │ │ │ + this.readers.gml._inherit.apply(this, [node, container]); │ │ │ │ │ + this.readChildNodes(node, container); │ │ │ │ │ + obj.components = [new OpenLayers.Geometry.LinearRing( │ │ │ │ │ + container.points │ │ │ │ │ + )]; │ │ │ │ │ + }, │ │ │ │ │ + "MultiPolygon": function(node, container) { │ │ │ │ │ + var obj = { │ │ │ │ │ + components: [] │ │ │ │ │ + }; │ │ │ │ │ + this.readers.gml._inherit.apply(this, [node, obj, container]); │ │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ │ + container.components = [ │ │ │ │ │ + new OpenLayers.Geometry.MultiPolygon(obj.components) │ │ │ │ │ + ]; │ │ │ │ │ + }, │ │ │ │ │ + "polygonMember": function(node, obj) { │ │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ │ + }, │ │ │ │ │ + "GeometryCollection": function(node, container) { │ │ │ │ │ + var obj = { │ │ │ │ │ + components: [] │ │ │ │ │ + }; │ │ │ │ │ + this.readers.gml._inherit.apply(this, [node, obj, container]); │ │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ │ + container.components = [ │ │ │ │ │ + new OpenLayers.Geometry.Collection(obj.components) │ │ │ │ │ + ]; │ │ │ │ │ + }, │ │ │ │ │ + "geometryMember": function(node, obj) { │ │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ + "feature": { │ │ │ │ │ + "*": function(node, obj) { │ │ │ │ │ + // The node can either be named like the featureType, or it │ │ │ │ │ + // can be a child of the feature:featureType. Children can be │ │ │ │ │ + // geometry or attributes. │ │ │ │ │ + var name; │ │ │ │ │ + var local = node.localName || node.nodeName.split(":").pop(); │ │ │ │ │ + // Since an attribute can have the same name as the feature type │ │ │ │ │ + // we only want to read the node as a feature if the parent │ │ │ │ │ + // node can have feature nodes as children. In this case, the │ │ │ │ │ + // obj.features property is set. │ │ │ │ │ + if (obj.features) { │ │ │ │ │ + if (!this.singleFeatureType && │ │ │ │ │ + (OpenLayers.Util.indexOf(this.featureType, local) !== -1)) { │ │ │ │ │ + name = "_typeName"; │ │ │ │ │ + } else if (local === this.featureType) { │ │ │ │ │ + name = "_typeName"; │ │ │ │ │ + } │ │ │ │ │ + } else { │ │ │ │ │ + // Assume attribute elements have one child node and that the child │ │ │ │ │ + // is a text node. Otherwise assume it is a geometry node. │ │ │ │ │ + if (node.childNodes.length == 0 || │ │ │ │ │ + (node.childNodes.length == 1 && node.firstChild.nodeType == 3)) { │ │ │ │ │ + if (this.extractAttributes) { │ │ │ │ │ + name = "_attribute"; │ │ │ │ │ + } │ │ │ │ │ + } else { │ │ │ │ │ + name = "_geometry"; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + if (name) { │ │ │ │ │ + this.readers.feature[name].apply(this, [node, obj]); │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ + "_typeName": function(node, obj) { │ │ │ │ │ + var container = { │ │ │ │ │ + components: [], │ │ │ │ │ + attributes: {} │ │ │ │ │ + }; │ │ │ │ │ + this.readChildNodes(node, container); │ │ │ │ │ + // look for common gml namespaced elements │ │ │ │ │ + if (container.name) { │ │ │ │ │ + container.attributes.name = container.name; │ │ │ │ │ + } │ │ │ │ │ + var feature = new OpenLayers.Feature.Vector( │ │ │ │ │ + container.components[0], container.attributes │ │ │ │ │ + ); │ │ │ │ │ + if (!this.singleFeatureType) { │ │ │ │ │ + feature.type = node.nodeName.split(":").pop(); │ │ │ │ │ + feature.namespace = node.namespaceURI; │ │ │ │ │ + } │ │ │ │ │ + var fid = node.getAttribute("fid") || │ │ │ │ │ + this.getAttributeNS(node, this.namespaces["gml"], "id"); │ │ │ │ │ + if (fid) { │ │ │ │ │ + feature.fid = fid; │ │ │ │ │ + } │ │ │ │ │ + if (this.internalProjection && this.externalProjection && │ │ │ │ │ + feature.geometry) { │ │ │ │ │ + feature.geometry.transform( │ │ │ │ │ + this.externalProjection, this.internalProjection │ │ │ │ │ + ); │ │ │ │ │ + } │ │ │ │ │ + if (container.bounds) { │ │ │ │ │ + feature.bounds = container.bounds; │ │ │ │ │ + } │ │ │ │ │ + obj.features.push(feature); │ │ │ │ │ + }, │ │ │ │ │ + "_geometry": function(node, obj) { │ │ │ │ │ + if (!this.geometryName) { │ │ │ │ │ + this.geometryName = node.nodeName.split(":").pop(); │ │ │ │ │ + } │ │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ │ + }, │ │ │ │ │ + "_attribute": function(node, obj) { │ │ │ │ │ + var local = node.localName || node.nodeName.split(":").pop(); │ │ │ │ │ + var value = this.getChildValue(node); │ │ │ │ │ + obj.attributes[local] = value; │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ + "wfs": { │ │ │ │ │ + "FeatureCollection": function(node, obj) { │ │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ │ + } │ │ │ │ │ } │ │ │ │ │ - return { │ │ │ │ │ - speed: speed, │ │ │ │ │ - theta: theta │ │ │ │ │ - }; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: move │ │ │ │ │ - * Launch the kinetic move pan. │ │ │ │ │ + * Method: write │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * info - {Object} An object with two properties, "speed", and "theta". │ │ │ │ │ - * These values are those returned from the "end" call. │ │ │ │ │ - * callback - {Function} Function called on every step of the animation, │ │ │ │ │ - * receives x, y (values to pan), end (is the last point). │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - move: function(info, callback) { │ │ │ │ │ - var v0 = info.speed; │ │ │ │ │ - var fx = Math.cos(info.theta); │ │ │ │ │ - var fy = -Math.sin(info.theta); │ │ │ │ │ - │ │ │ │ │ - var initialTime = new Date().getTime(); │ │ │ │ │ + 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 │ │ │ │ │ + ); │ │ │ │ │ │ │ │ │ │ - var lastX = 0; │ │ │ │ │ - var lastY = 0; │ │ │ │ │ + return OpenLayers.Format.XML.prototype.write.apply(this, [root]); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - var timerCallback = function() { │ │ │ │ │ - if (this.timerId == null) { │ │ │ │ │ - return; │ │ │ │ │ + /** │ │ │ │ │ + * Property: writers │ │ │ │ │ + * As a compliment to the readers property, this structure contains public │ │ │ │ │ + * writing functions grouped by namespace alias and named like the │ │ │ │ │ + * node names they produce. │ │ │ │ │ + */ │ │ │ │ │ + writers: { │ │ │ │ │ + "gml": { │ │ │ │ │ + "featureMember": function(feature) { │ │ │ │ │ + var node = this.createElementNSPlus("gml:featureMember"); │ │ │ │ │ + this.writeNode("feature:_typeName", feature, node); │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "MultiPoint": function(geometry) { │ │ │ │ │ + var node = this.createElementNSPlus("gml:MultiPoint"); │ │ │ │ │ + var components = geometry.components || [geometry]; │ │ │ │ │ + for (var i = 0, ii = components.length; i < ii; ++i) { │ │ │ │ │ + this.writeNode("pointMember", components[i], node); │ │ │ │ │ + } │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "pointMember": function(geometry) { │ │ │ │ │ + var node = this.createElementNSPlus("gml:pointMember"); │ │ │ │ │ + this.writeNode("Point", geometry, node); │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "MultiLineString": function(geometry) { │ │ │ │ │ + var node = this.createElementNSPlus("gml:MultiLineString"); │ │ │ │ │ + var components = geometry.components || [geometry]; │ │ │ │ │ + for (var i = 0, ii = components.length; i < ii; ++i) { │ │ │ │ │ + this.writeNode("lineStringMember", components[i], node); │ │ │ │ │ + } │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "lineStringMember": function(geometry) { │ │ │ │ │ + var node = this.createElementNSPlus("gml:lineStringMember"); │ │ │ │ │ + this.writeNode("LineString", geometry, node); │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "MultiPolygon": function(geometry) { │ │ │ │ │ + var node = this.createElementNSPlus("gml:MultiPolygon"); │ │ │ │ │ + var components = geometry.components || [geometry]; │ │ │ │ │ + for (var i = 0, ii = components.length; i < ii; ++i) { │ │ │ │ │ + this.writeNode( │ │ │ │ │ + "polygonMember", components[i], node │ │ │ │ │ + ); │ │ │ │ │ + } │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "polygonMember": function(geometry) { │ │ │ │ │ + var node = this.createElementNSPlus("gml:polygonMember"); │ │ │ │ │ + this.writeNode("Polygon", geometry, node); │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "GeometryCollection": function(geometry) { │ │ │ │ │ + var node = this.createElementNSPlus("gml:GeometryCollection"); │ │ │ │ │ + for (var i = 0, len = geometry.components.length; i < len; ++i) { │ │ │ │ │ + this.writeNode("geometryMember", geometry.components[i], node); │ │ │ │ │ + } │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "geometryMember": function(geometry) { │ │ │ │ │ + var node = this.createElementNSPlus("gml:geometryMember"); │ │ │ │ │ + var child = this.writeNode("feature:_geometry", geometry); │ │ │ │ │ + node.appendChild(child.firstChild); │ │ │ │ │ + return node; │ │ │ │ │ } │ │ │ │ │ - │ │ │ │ │ - var t = new Date().getTime() - initialTime; │ │ │ │ │ - │ │ │ │ │ - var p = (-this.deceleration * Math.pow(t, 2)) / 2.0 + v0 * t; │ │ │ │ │ - var x = p * fx; │ │ │ │ │ - var y = p * fy; │ │ │ │ │ - │ │ │ │ │ - var args = {}; │ │ │ │ │ - args.end = false; │ │ │ │ │ - var v = -this.deceleration * t + v0; │ │ │ │ │ - │ │ │ │ │ - if (v <= 0) { │ │ │ │ │ - OpenLayers.Animation.stop(this.timerId); │ │ │ │ │ - this.timerId = null; │ │ │ │ │ - args.end = true; │ │ │ │ │ + }, │ │ │ │ │ + "feature": { │ │ │ │ │ + "_typeName": function(feature) { │ │ │ │ │ + var node = this.createElementNSPlus("feature:" + this.featureType, { │ │ │ │ │ + attributes: { │ │ │ │ │ + fid: feature.fid │ │ │ │ │ + } │ │ │ │ │ + }); │ │ │ │ │ + if (feature.geometry) { │ │ │ │ │ + this.writeNode("feature:_geometry", feature.geometry, node); │ │ │ │ │ + } │ │ │ │ │ + for (var name in feature.attributes) { │ │ │ │ │ + var value = feature.attributes[name]; │ │ │ │ │ + if (value != null) { │ │ │ │ │ + this.writeNode( │ │ │ │ │ + "feature:_attribute", { │ │ │ │ │ + name: name, │ │ │ │ │ + value: value │ │ │ │ │ + }, node │ │ │ │ │ + ); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "_geometry": function(geometry) { │ │ │ │ │ + if (this.externalProjection && this.internalProjection) { │ │ │ │ │ + geometry = geometry.clone().transform( │ │ │ │ │ + this.internalProjection, this.externalProjection │ │ │ │ │ + ); │ │ │ │ │ + } │ │ │ │ │ + var node = this.createElementNSPlus( │ │ │ │ │ + "feature:" + this.geometryName │ │ │ │ │ + ); │ │ │ │ │ + var type = this.geometryTypes[geometry.CLASS_NAME]; │ │ │ │ │ + var child = this.writeNode("gml:" + type, geometry, node); │ │ │ │ │ + if (this.srsName) { │ │ │ │ │ + child.setAttribute("srsName", this.srsName); │ │ │ │ │ + } │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "_attribute": function(obj) { │ │ │ │ │ + return this.createElementNSPlus("feature:" + obj.name, { │ │ │ │ │ + value: obj.value │ │ │ │ │ + }); │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ + "wfs": { │ │ │ │ │ + "FeatureCollection": function(features) { │ │ │ │ │ + /** │ │ │ │ │ + * This is only here because GML2 only describes abstract │ │ │ │ │ + * feature collections. Typically, you would not be using │ │ │ │ │ + * the GML format to write wfs elements. This just provides │ │ │ │ │ + * some way to write out lists of features. GML3 defines the │ │ │ │ │ + * featureMembers element, so that is used by default instead. │ │ │ │ │ + */ │ │ │ │ │ + var node = this.createElementNSPlus("wfs:FeatureCollection"); │ │ │ │ │ + for (var i = 0, len = features.length; i < len; ++i) { │ │ │ │ │ + this.writeNode("gml:featureMember", features[i], node); │ │ │ │ │ + } │ │ │ │ │ + return node; │ │ │ │ │ } │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - args.x = x - lastX; │ │ │ │ │ - args.y = y - lastY; │ │ │ │ │ - lastX = x; │ │ │ │ │ - lastY = y; │ │ │ │ │ - callback(args.x, args.y, args.end); │ │ │ │ │ + /** │ │ │ │ │ + * Method: setGeometryTypes │ │ │ │ │ + * Sets the 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" │ │ │ │ │ }; │ │ │ │ │ - │ │ │ │ │ - this.timerId = OpenLayers.Animation.start( │ │ │ │ │ - OpenLayers.Function.bind(timerCallback, this) │ │ │ │ │ - ); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Kinetic" │ │ │ │ │ + CLASS_NAME: "OpenLayers.Format.GML.Base" │ │ │ │ │ + │ │ │ │ │ }); │ │ │ │ │ /* ====================================================================== │ │ │ │ │ - OpenLayers/Layer/HTTPRequest.js │ │ │ │ │ + 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/Layer.js │ │ │ │ │ + * @requires OpenLayers/Format/GML/Base.js │ │ │ │ │ */ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Class: OpenLayers.Layer.HTTPRequest │ │ │ │ │ - * │ │ │ │ │ - * Inherits from: │ │ │ │ │ - * - │ │ │ │ │ + * Class: OpenLayers.Format.GML.v3 │ │ │ │ │ + * Parses GML version 3. │ │ │ │ │ + * │ │ │ │ │ + * 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, │ │ │ │ │ +OpenLayers.Format.GML.v3 = OpenLayers.Class(OpenLayers.Format.GML.Base, { │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: params │ │ │ │ │ - * {Object} Hashtable of key/value parameters │ │ │ │ │ + /** │ │ │ │ │ + * Property: schemaLocation │ │ │ │ │ + * {String} Schema location for a particular minor version. The writers │ │ │ │ │ + * conform with the Simple Features Profile for GML. │ │ │ │ │ */ │ │ │ │ │ - params: null, │ │ │ │ │ + schemaLocation: "http://www.opengis.net/gml http://schemas.opengis.net/gml/3.1.1/profiles/gmlsfProfile/1.0.0/gmlsf.xsd", │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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. │ │ │ │ │ - * │ │ │ │ │ + /** │ │ │ │ │ + * 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). │ │ │ │ │ */ │ │ │ │ │ - reproject: false, │ │ │ │ │ + curve: 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 │ │ │ │ │ + * 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). │ │ │ │ │ */ │ │ │ │ │ - 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); │ │ │ │ │ - } │ │ │ │ │ - }, │ │ │ │ │ + multiCurve: true, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: destroy │ │ │ │ │ + * 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). │ │ │ │ │ */ │ │ │ │ │ - destroy: function() { │ │ │ │ │ - this.url = null; │ │ │ │ │ - this.params = null; │ │ │ │ │ - OpenLayers.Layer.prototype.destroy.apply(this, arguments); │ │ │ │ │ - }, │ │ │ │ │ + surface: false, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: clone │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * obj - {Object} │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {} An exact clone of this │ │ │ │ │ - * │ │ │ │ │ + * 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). │ │ │ │ │ */ │ │ │ │ │ - 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; │ │ │ │ │ - }, │ │ │ │ │ + multiSurface: true, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIMethod: setUrl │ │ │ │ │ - * │ │ │ │ │ + /** │ │ │ │ │ + * Constructor: OpenLayers.Format.GML.v3 │ │ │ │ │ + * Create a parser for GML v3. │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * newUrl - {String} │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - setUrl: function(newUrl) { │ │ │ │ │ - this.url = newUrl; │ │ │ │ │ + initialize: function(options) { │ │ │ │ │ + OpenLayers.Format.GML.Base.prototype.initialize.apply(this, [options]); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: mergeNewParams │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * newParams - {Object} │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * redrawn: {Boolean} whether the layer was actually redrawn. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - 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; │ │ │ │ │ + readers: { │ │ │ │ │ + "gml": OpenLayers.Util.applyDefaults({ │ │ │ │ │ + "_inherit": function(node, obj, container) { │ │ │ │ │ + // SRSReferenceGroup attributes │ │ │ │ │ + var dim = parseInt(node.getAttribute("srsDimension"), 10) || │ │ │ │ │ + (container && container.srsDimension); │ │ │ │ │ + if (dim) { │ │ │ │ │ + obj.srsDimension = dim; │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ + "featureMembers": function(node, obj) { │ │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ │ + }, │ │ │ │ │ + "Curve": function(node, container) { │ │ │ │ │ + var obj = { │ │ │ │ │ + points: [] │ │ │ │ │ + }; │ │ │ │ │ + this.readers.gml._inherit.apply(this, [node, obj, container]); │ │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ │ + if (!container.components) { │ │ │ │ │ + container.components = []; │ │ │ │ │ + } │ │ │ │ │ + container.components.push( │ │ │ │ │ + new OpenLayers.Geometry.LineString(obj.points) │ │ │ │ │ + ); │ │ │ │ │ + }, │ │ │ │ │ + "segments": function(node, obj) { │ │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ │ + }, │ │ │ │ │ + "LineStringSegment": function(node, container) { │ │ │ │ │ + var obj = {}; │ │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ │ + if (obj.points) { │ │ │ │ │ + Array.prototype.push.apply(container.points, obj.points); │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ + "pos": function(node, obj) { │ │ │ │ │ + var str = this.getChildValue(node).replace( │ │ │ │ │ + this.regExes.trimSpace, "" │ │ │ │ │ + ); │ │ │ │ │ + var coords = str.split(this.regExes.splitSpace); │ │ │ │ │ + var point; │ │ │ │ │ + if (this.xy) { │ │ │ │ │ + point = new OpenLayers.Geometry.Point( │ │ │ │ │ + coords[0], coords[1], coords[2] │ │ │ │ │ + ); │ │ │ │ │ + } else { │ │ │ │ │ + point = new OpenLayers.Geometry.Point( │ │ │ │ │ + coords[1], coords[0], coords[2] │ │ │ │ │ + ); │ │ │ │ │ + } │ │ │ │ │ + obj.points = [point]; │ │ │ │ │ + }, │ │ │ │ │ + "posList": function(node, obj) { │ │ │ │ │ + var str = this.getChildValue(node).replace( │ │ │ │ │ + this.regExes.trimSpace, "" │ │ │ │ │ + ); │ │ │ │ │ + var coords = str.split(this.regExes.splitSpace); │ │ │ │ │ + // The "dimension" attribute is from the GML 3.0.1 spec. │ │ │ │ │ + var dim = obj.srsDimension || │ │ │ │ │ + parseInt(node.getAttribute("srsDimension") || node.getAttribute("dimension"), 10) || 2; │ │ │ │ │ + var j, x, y, z; │ │ │ │ │ + var numPoints = coords.length / dim; │ │ │ │ │ + var points = new Array(numPoints); │ │ │ │ │ + for (var i = 0, len = coords.length; i < len; i += dim) { │ │ │ │ │ + x = coords[i]; │ │ │ │ │ + y = coords[i + 1]; │ │ │ │ │ + z = (dim == 2) ? undefined : coords[i + 2]; │ │ │ │ │ + if (this.xy) { │ │ │ │ │ + points[i / dim] = new OpenLayers.Geometry.Point(x, y, z); │ │ │ │ │ + } else { │ │ │ │ │ + points[i / dim] = new OpenLayers.Geometry.Point(y, x, z); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + obj.points = points; │ │ │ │ │ + }, │ │ │ │ │ + "Surface": function(node, obj) { │ │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ │ + }, │ │ │ │ │ + "patches": function(node, obj) { │ │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ │ + }, │ │ │ │ │ + "PolygonPatch": function(node, obj) { │ │ │ │ │ + this.readers.gml.Polygon.apply(this, [node, obj]); │ │ │ │ │ + }, │ │ │ │ │ + "exterior": function(node, container) { │ │ │ │ │ + var obj = {}; │ │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ │ + container.outer = obj.components[0]; │ │ │ │ │ + }, │ │ │ │ │ + "interior": function(node, container) { │ │ │ │ │ + var obj = {}; │ │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ │ + container.inner.push(obj.components[0]); │ │ │ │ │ + }, │ │ │ │ │ + "MultiCurve": function(node, container) { │ │ │ │ │ + var obj = { │ │ │ │ │ + components: [] │ │ │ │ │ + }; │ │ │ │ │ + this.readers.gml._inherit.apply(this, [node, obj, container]); │ │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ │ + if (obj.components.length > 0) { │ │ │ │ │ + container.components = [ │ │ │ │ │ + new OpenLayers.Geometry.MultiLineString(obj.components) │ │ │ │ │ + ]; │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ + "curveMember": function(node, obj) { │ │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ │ + }, │ │ │ │ │ + "MultiSurface": function(node, container) { │ │ │ │ │ + var obj = { │ │ │ │ │ + components: [] │ │ │ │ │ + }; │ │ │ │ │ + this.readers.gml._inherit.apply(this, [node, obj, container]); │ │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ │ + if (obj.components.length > 0) { │ │ │ │ │ + container.components = [ │ │ │ │ │ + new OpenLayers.Geometry.MultiPolygon(obj.components) │ │ │ │ │ + ]; │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ + "surfaceMember": function(node, obj) { │ │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ │ + }, │ │ │ │ │ + "surfaceMembers": function(node, obj) { │ │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ │ + }, │ │ │ │ │ + "pointMembers": function(node, obj) { │ │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ │ + }, │ │ │ │ │ + "lineStringMembers": function(node, obj) { │ │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ │ + }, │ │ │ │ │ + "polygonMembers": function(node, obj) { │ │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ │ + }, │ │ │ │ │ + "geometryMembers": function(node, obj) { │ │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ │ + }, │ │ │ │ │ + "Envelope": function(node, container) { │ │ │ │ │ + var obj = { │ │ │ │ │ + points: new Array(2) │ │ │ │ │ + }; │ │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ │ + if (!container.components) { │ │ │ │ │ + container.components = []; │ │ │ │ │ + } │ │ │ │ │ + var min = obj.points[0]; │ │ │ │ │ + var max = obj.points[1]; │ │ │ │ │ + container.components.push( │ │ │ │ │ + new OpenLayers.Bounds(min.x, min.y, max.x, max.y) │ │ │ │ │ + ); │ │ │ │ │ + }, │ │ │ │ │ + "lowerCorner": function(node, container) { │ │ │ │ │ + var obj = {}; │ │ │ │ │ + this.readers.gml.pos.apply(this, [node, obj]); │ │ │ │ │ + container.points[0] = obj.points[0]; │ │ │ │ │ + }, │ │ │ │ │ + "upperCorner": function(node, container) { │ │ │ │ │ + var obj = {}; │ │ │ │ │ + this.readers.gml.pos.apply(this, [node, obj]); │ │ │ │ │ + container.points[1] = obj.points[0]; │ │ │ │ │ + } │ │ │ │ │ + }, OpenLayers.Format.GML.Base.prototype.readers["gml"]), │ │ │ │ │ + "feature": OpenLayers.Format.GML.Base.prototype.readers["feature"], │ │ │ │ │ + "wfs": OpenLayers.Format.GML.Base.prototype.readers["wfs"] │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: redraw │ │ │ │ │ - * Redraws the layer. Returns true if the layer was redrawn, false if not. │ │ │ │ │ + * Method: write │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * force - {Boolean} Force redraw by adding random parameter. │ │ │ │ │ + * features - {Array() | OpenLayers.Feature.Vector} │ │ │ │ │ + * An array of features or a single feature. │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {Boolean} The layer was redrawn. │ │ │ │ │ + * {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. │ │ │ │ │ */ │ │ │ │ │ - redraw: function(force) { │ │ │ │ │ - if (force) { │ │ │ │ │ - return this.mergeNewParams({ │ │ │ │ │ - "_olSalt": Math.random() │ │ │ │ │ - }); │ │ │ │ │ + write: function(features) { │ │ │ │ │ + var name; │ │ │ │ │ + if (OpenLayers.Util.isArray(features)) { │ │ │ │ │ + name = "featureMembers"; │ │ │ │ │ } else { │ │ │ │ │ - return OpenLayers.Layer.prototype.redraw.apply(this, []); │ │ │ │ │ + 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]); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - selectUrl: function(paramString, urls) { │ │ │ │ │ - var product = 1; │ │ │ │ │ - for (var i = 0, len = paramString.length; i < len; i++) { │ │ │ │ │ - product *= paramString.charCodeAt(i) * this.URL_HASH_FACTOR; │ │ │ │ │ - product -= Math.floor(product); │ │ │ │ │ - } │ │ │ │ │ - return urls[Math.floor(product * urls.length)]; │ │ │ │ │ + writers: { │ │ │ │ │ + "gml": OpenLayers.Util.applyDefaults({ │ │ │ │ │ + "featureMembers": function(features) { │ │ │ │ │ + var node = this.createElementNSPlus("gml:featureMembers"); │ │ │ │ │ + for (var i = 0, len = features.length; i < len; ++i) { │ │ │ │ │ + this.writeNode("feature:_typeName", features[i], node); │ │ │ │ │ + } │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "Point": function(geometry) { │ │ │ │ │ + var node = this.createElementNSPlus("gml:Point"); │ │ │ │ │ + this.writeNode("pos", geometry, node); │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "pos": function(point) { │ │ │ │ │ + // only 2d for simple features profile │ │ │ │ │ + var pos = (this.xy) ? │ │ │ │ │ + (point.x + " " + point.y) : (point.y + " " + point.x); │ │ │ │ │ + return this.createElementNSPlus("gml:pos", { │ │ │ │ │ + value: pos │ │ │ │ │ + }); │ │ │ │ │ + }, │ │ │ │ │ + "LineString": function(geometry) { │ │ │ │ │ + var node = this.createElementNSPlus("gml:LineString"); │ │ │ │ │ + this.writeNode("posList", geometry.components, node); │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "Curve": function(geometry) { │ │ │ │ │ + var node = this.createElementNSPlus("gml:Curve"); │ │ │ │ │ + this.writeNode("segments", geometry, node); │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "segments": function(geometry) { │ │ │ │ │ + var node = this.createElementNSPlus("gml:segments"); │ │ │ │ │ + this.writeNode("LineStringSegment", geometry, node); │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "LineStringSegment": function(geometry) { │ │ │ │ │ + var node = this.createElementNSPlus("gml:LineStringSegment"); │ │ │ │ │ + this.writeNode("posList", geometry.components, node); │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "posList": function(points) { │ │ │ │ │ + // only 2d for simple features profile │ │ │ │ │ + var len = points.length; │ │ │ │ │ + var parts = new Array(len); │ │ │ │ │ + var point; │ │ │ │ │ + for (var i = 0; i < len; ++i) { │ │ │ │ │ + point = points[i]; │ │ │ │ │ + if (this.xy) { │ │ │ │ │ + parts[i] = point.x + " " + point.y; │ │ │ │ │ + } else { │ │ │ │ │ + parts[i] = point.y + " " + point.x; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + return this.createElementNSPlus("gml:posList", { │ │ │ │ │ + value: parts.join(" ") │ │ │ │ │ + }); │ │ │ │ │ + }, │ │ │ │ │ + "Surface": function(geometry) { │ │ │ │ │ + var node = this.createElementNSPlus("gml:Surface"); │ │ │ │ │ + this.writeNode("patches", geometry, node); │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "patches": function(geometry) { │ │ │ │ │ + var node = this.createElementNSPlus("gml:patches"); │ │ │ │ │ + this.writeNode("PolygonPatch", geometry, node); │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "PolygonPatch": function(geometry) { │ │ │ │ │ + var node = this.createElementNSPlus("gml:PolygonPatch", { │ │ │ │ │ + attributes: { │ │ │ │ │ + interpolation: "planar" │ │ │ │ │ + } │ │ │ │ │ + }); │ │ │ │ │ + this.writeNode("exterior", geometry.components[0], node); │ │ │ │ │ + for (var i = 1, len = geometry.components.length; i < len; ++i) { │ │ │ │ │ + this.writeNode( │ │ │ │ │ + "interior", geometry.components[i], node │ │ │ │ │ + ); │ │ │ │ │ + } │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "Polygon": function(geometry) { │ │ │ │ │ + var node = this.createElementNSPlus("gml:Polygon"); │ │ │ │ │ + this.writeNode("exterior", geometry.components[0], node); │ │ │ │ │ + for (var i = 1, len = geometry.components.length; i < len; ++i) { │ │ │ │ │ + this.writeNode( │ │ │ │ │ + "interior", geometry.components[i], node │ │ │ │ │ + ); │ │ │ │ │ + } │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "exterior": function(ring) { │ │ │ │ │ + var node = this.createElementNSPlus("gml:exterior"); │ │ │ │ │ + this.writeNode("LinearRing", ring, node); │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "interior": function(ring) { │ │ │ │ │ + var node = this.createElementNSPlus("gml:interior"); │ │ │ │ │ + this.writeNode("LinearRing", ring, node); │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "LinearRing": function(ring) { │ │ │ │ │ + var node = this.createElementNSPlus("gml:LinearRing"); │ │ │ │ │ + this.writeNode("posList", ring.components, node); │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "MultiCurve": function(geometry) { │ │ │ │ │ + var node = this.createElementNSPlus("gml:MultiCurve"); │ │ │ │ │ + var components = geometry.components || [geometry]; │ │ │ │ │ + for (var i = 0, len = components.length; i < len; ++i) { │ │ │ │ │ + this.writeNode("curveMember", components[i], node); │ │ │ │ │ + } │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "curveMember": function(geometry) { │ │ │ │ │ + var node = this.createElementNSPlus("gml:curveMember"); │ │ │ │ │ + if (this.curve) { │ │ │ │ │ + this.writeNode("Curve", geometry, node); │ │ │ │ │ + } else { │ │ │ │ │ + this.writeNode("LineString", geometry, node); │ │ │ │ │ + } │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "MultiSurface": function(geometry) { │ │ │ │ │ + var node = this.createElementNSPlus("gml:MultiSurface"); │ │ │ │ │ + var components = geometry.components || [geometry]; │ │ │ │ │ + for (var i = 0, len = components.length; i < len; ++i) { │ │ │ │ │ + this.writeNode("surfaceMember", components[i], node); │ │ │ │ │ + } │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "surfaceMember": function(polygon) { │ │ │ │ │ + var node = this.createElementNSPlus("gml:surfaceMember"); │ │ │ │ │ + if (this.surface) { │ │ │ │ │ + this.writeNode("Surface", polygon, node); │ │ │ │ │ + } else { │ │ │ │ │ + this.writeNode("Polygon", polygon, node); │ │ │ │ │ + } │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "Envelope": function(bounds) { │ │ │ │ │ + var node = this.createElementNSPlus("gml:Envelope"); │ │ │ │ │ + this.writeNode("lowerCorner", bounds, node); │ │ │ │ │ + this.writeNode("upperCorner", bounds, node); │ │ │ │ │ + // srsName attribute is required for gml:Envelope │ │ │ │ │ + if (this.srsName) { │ │ │ │ │ + node.setAttribute("srsName", this.srsName); │ │ │ │ │ + } │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "lowerCorner": function(bounds) { │ │ │ │ │ + // only 2d for simple features profile │ │ │ │ │ + var pos = (this.xy) ? │ │ │ │ │ + (bounds.left + " " + bounds.bottom) : │ │ │ │ │ + (bounds.bottom + " " + bounds.left); │ │ │ │ │ + return this.createElementNSPlus("gml:lowerCorner", { │ │ │ │ │ + value: pos │ │ │ │ │ + }); │ │ │ │ │ + }, │ │ │ │ │ + "upperCorner": function(bounds) { │ │ │ │ │ + // only 2d for simple features profile │ │ │ │ │ + var pos = (this.xy) ? │ │ │ │ │ + (bounds.right + " " + bounds.top) : │ │ │ │ │ + (bounds.top + " " + bounds.right); │ │ │ │ │ + return this.createElementNSPlus("gml:upperCorner", { │ │ │ │ │ + value: pos │ │ │ │ │ + }); │ │ │ │ │ + } │ │ │ │ │ + }, OpenLayers.Format.GML.Base.prototype.writers["gml"]), │ │ │ │ │ + "feature": OpenLayers.Format.GML.Base.prototype.writers["feature"], │ │ │ │ │ + "wfs": OpenLayers.Format.GML.Base.prototype.writers["wfs"] │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: getFullRequestString │ │ │ │ │ - * Combine url with layer's params and these newParams. │ │ │ │ │ - * │ │ │ │ │ - * does checking on the serverPath variable, allowing for cases when it │ │ │ │ │ - * is supplied with trailing ? or &, as well as cases where not. │ │ │ │ │ - * │ │ │ │ │ - * return in formatted string like this: │ │ │ │ │ - * "server?key1=value1&key2=value2&key3=value3" │ │ │ │ │ - * │ │ │ │ │ - * WARNING: The altUrl parameter is deprecated and will be removed in 3.0. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * newParams - {Object} │ │ │ │ │ - * altUrl - {String} Use this as the url instead of the layer's url │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {String} │ │ │ │ │ + /** │ │ │ │ │ + * Method: setGeometryTypes │ │ │ │ │ + * Sets the mapping. │ │ │ │ │ */ │ │ │ │ │ - getFullRequestString: function(newParams, altUrl) { │ │ │ │ │ - │ │ │ │ │ - // if not altUrl passed in, use layer's url │ │ │ │ │ - var url = altUrl || this.url; │ │ │ │ │ - │ │ │ │ │ - // create a new params hashtable with all the layer params and the │ │ │ │ │ - // new params together. then convert to string │ │ │ │ │ - var allParams = OpenLayers.Util.extend({}, this.params); │ │ │ │ │ - allParams = OpenLayers.Util.extend(allParams, newParams); │ │ │ │ │ - var paramsString = OpenLayers.Util.getParameterString(allParams); │ │ │ │ │ - │ │ │ │ │ - // if url is not a string, it should be an array of strings, │ │ │ │ │ - // in which case we will deterministically select one of them in │ │ │ │ │ - // order to evenly distribute requests to different urls. │ │ │ │ │ - // │ │ │ │ │ - if (OpenLayers.Util.isArray(url)) { │ │ │ │ │ - url = this.selectUrl(paramsString, url); │ │ │ │ │ - } │ │ │ │ │ - │ │ │ │ │ - // ignore parameters that are already in the url search string │ │ │ │ │ - var urlParams = │ │ │ │ │ - OpenLayers.Util.upperCaseObject(OpenLayers.Util.getParameters(url)); │ │ │ │ │ - for (var key in allParams) { │ │ │ │ │ - if (key.toUpperCase() in urlParams) { │ │ │ │ │ - delete allParams[key]; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - paramsString = OpenLayers.Util.getParameterString(allParams); │ │ │ │ │ - │ │ │ │ │ - return OpenLayers.Util.urlAppend(url, paramsString); │ │ │ │ │ + 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.Layer.HTTPRequest" │ │ │ │ │ + CLASS_NAME: "OpenLayers.Format.GML.v3" │ │ │ │ │ + │ │ │ │ │ }); │ │ │ │ │ /* ====================================================================== │ │ │ │ │ - OpenLayers/Tile.js │ │ │ │ │ + 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/BaseTypes/Class.js │ │ │ │ │ - * @requires OpenLayers/Util.js │ │ │ │ │ + * @requires OpenLayers/Format/Filter/v1.js │ │ │ │ │ + * @requires OpenLayers/Format/GML/v3.js │ │ │ │ │ */ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Class: OpenLayers.Tile │ │ │ │ │ - * This is a class designed to designate a single tile, however │ │ │ │ │ - * it is explicitly designed to do relatively little. Tiles store │ │ │ │ │ - * information about themselves -- such as the URL that they are related │ │ │ │ │ - * to, and their size - but do not add themselves to the layer div │ │ │ │ │ - * automatically, for example. Create a new tile with the │ │ │ │ │ - * constructor, or a subclass. │ │ │ │ │ - * │ │ │ │ │ - * TBD 3.0 - remove reference to url in above paragraph │ │ │ │ │ + * 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.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, │ │ │ │ │ +OpenLayers.Format.Filter.v1_1_0 = OpenLayers.Class( │ │ │ │ │ + OpenLayers.Format.GML.v3, OpenLayers.Format.Filter.v1, { │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: size │ │ │ │ │ - * {} null │ │ │ │ │ - */ │ │ │ │ │ - size: null, │ │ │ │ │ + /** │ │ │ │ │ + * Constant: VERSION │ │ │ │ │ + * {String} 1.1.0 │ │ │ │ │ + */ │ │ │ │ │ + VERSION: "1.1.0", │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: position │ │ │ │ │ - * {} Top Left pixel of the tile │ │ │ │ │ - */ │ │ │ │ │ - position: null, │ │ │ │ │ + /** │ │ │ │ │ + * 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", │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: isLoading │ │ │ │ │ - * {Boolean} Is the tile loading? │ │ │ │ │ - */ │ │ │ │ │ - isLoading: false, │ │ │ │ │ + /** │ │ │ │ │ + * 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] │ │ │ │ │ + ); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** 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. │ │ │ │ │ - */ │ │ │ │ │ + /** │ │ │ │ │ + * 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"] │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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(); │ │ │ │ │ - } │ │ │ │ │ + /** │ │ │ │ │ + * Property: writers │ │ │ │ │ + * As a compliment to the readers property, this structure contains public │ │ │ │ │ + * writing functions grouped by namespace alias and named like the │ │ │ │ │ + * node names they produce. │ │ │ │ │ + */ │ │ │ │ │ + writers: { │ │ │ │ │ + "ogc": OpenLayers.Util.applyDefaults({ │ │ │ │ │ + "PropertyIsEqualTo": function(filter) { │ │ │ │ │ + var node = this.createElementNSPlus("ogc:PropertyIsEqualTo", { │ │ │ │ │ + attributes: { │ │ │ │ │ + matchCase: filter.matchCase │ │ │ │ │ + } │ │ │ │ │ + }); │ │ │ │ │ + // no ogc:expression handling for PropertyName for now │ │ │ │ │ + this.writeNode("PropertyName", filter, node); │ │ │ │ │ + // handle Literals or Functions for now │ │ │ │ │ + this.writeOgcExpression(filter.value, node); │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "PropertyIsNotEqualTo": function(filter) { │ │ │ │ │ + var node = this.createElementNSPlus("ogc:PropertyIsNotEqualTo", { │ │ │ │ │ + attributes: { │ │ │ │ │ + matchCase: filter.matchCase │ │ │ │ │ + } │ │ │ │ │ + }); │ │ │ │ │ + // no ogc:expression handling for PropertyName for now │ │ │ │ │ + this.writeNode("PropertyName", filter, node); │ │ │ │ │ + // handle Literals or Functions for now │ │ │ │ │ + this.writeOgcExpression(filter.value, node); │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "PropertyIsLike": function(filter) { │ │ │ │ │ + var node = this.createElementNSPlus("ogc:PropertyIsLike", { │ │ │ │ │ + attributes: { │ │ │ │ │ + matchCase: filter.matchCase, │ │ │ │ │ + wildCard: "*", │ │ │ │ │ + singleChar: ".", │ │ │ │ │ + escapeChar: "!" │ │ │ │ │ + } │ │ │ │ │ + }); │ │ │ │ │ + // no ogc:expression handling for now │ │ │ │ │ + this.writeNode("PropertyName", filter, node); │ │ │ │ │ + // convert regex string to ogc string │ │ │ │ │ + this.writeNode("Literal", filter.regex2value(), node); │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "BBOX": function(filter) { │ │ │ │ │ + var node = this.createElementNSPlus("ogc:BBOX"); │ │ │ │ │ + // PropertyName is optional in 1.1.0 │ │ │ │ │ + filter.property && this.writeNode("PropertyName", filter, node); │ │ │ │ │ + var box = this.writeNode("gml:Envelope", filter.value); │ │ │ │ │ + if (filter.projection) { │ │ │ │ │ + box.setAttribute("srsName", filter.projection); │ │ │ │ │ + } │ │ │ │ │ + node.appendChild(box); │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "SortBy": function(sortProperties) { │ │ │ │ │ + var node = this.createElementNSPlus("ogc:SortBy"); │ │ │ │ │ + for (var i = 0, l = sortProperties.length; i < l; i++) { │ │ │ │ │ + this.writeNode( │ │ │ │ │ + "ogc:SortProperty", │ │ │ │ │ + sortProperties[i], │ │ │ │ │ + node │ │ │ │ │ + ); │ │ │ │ │ + } │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "SortProperty": function(sortProperty) { │ │ │ │ │ + var node = this.createElementNSPlus("ogc:SortProperty"); │ │ │ │ │ + this.writeNode( │ │ │ │ │ + "ogc:PropertyName", │ │ │ │ │ + sortProperty, │ │ │ │ │ + node │ │ │ │ │ + ); │ │ │ │ │ + this.writeNode( │ │ │ │ │ + "ogc:SortOrder", │ │ │ │ │ + (sortProperty.order == 'DESC') ? 'DESC' : 'ASC', │ │ │ │ │ + node │ │ │ │ │ + ); │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "SortOrder": function(value) { │ │ │ │ │ + var node = this.createElementNSPlus("ogc:SortOrder", { │ │ │ │ │ + value: value │ │ │ │ │ + }); │ │ │ │ │ + return node; │ │ │ │ │ + } │ │ │ │ │ + }, OpenLayers.Format.Filter.v1.prototype.writers["ogc"]), │ │ │ │ │ + "gml": OpenLayers.Format.GML.v3.prototype.writers["gml"], │ │ │ │ │ + "feature": OpenLayers.Format.GML.v3.prototype.writers["feature"] │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - //give the tile a unique id based on its BBOX. │ │ │ │ │ - this.id = OpenLayers.Util.createUniqueID("Tile_"); │ │ │ │ │ + /** │ │ │ │ │ + * 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:Envelope", filter.value); │ │ │ │ │ + } │ │ │ │ │ + if (filter.projection) { │ │ │ │ │ + child.setAttribute("srsName", filter.projection); │ │ │ │ │ + } │ │ │ │ │ + node.appendChild(child); │ │ │ │ │ + } │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - OpenLayers.Util.extend(this, options); │ │ │ │ │ + CLASS_NAME: "OpenLayers.Format.Filter.v1_1_0" │ │ │ │ │ │ │ │ │ │ - this.events = new OpenLayers.Events(this); │ │ │ │ │ - if (this.eventListeners instanceof Object) { │ │ │ │ │ - this.events.on(this.eventListeners); │ │ │ │ │ - } │ │ │ │ │ - }, │ │ │ │ │ + }); │ │ │ │ │ +/* ====================================================================== │ │ │ │ │ + OpenLayers/Format/OWSCommon/v1_0_0.js │ │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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"); │ │ │ │ │ - } │ │ │ │ │ - }, │ │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ + * full text of the license. */ │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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; │ │ │ │ │ +/** │ │ │ │ │ + * @requires OpenLayers/Format/OWSCommon/v1.js │ │ │ │ │ + */ │ │ │ │ │ │ │ │ │ │ - if (this.eventListeners) { │ │ │ │ │ - this.events.un(this.eventListeners); │ │ │ │ │ - } │ │ │ │ │ - this.events.destroy(); │ │ │ │ │ - this.eventListeners = null; │ │ │ │ │ - this.events = null; │ │ │ │ │ - }, │ │ │ │ │ +/** │ │ │ │ │ + * 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, { │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * Property: namespaces │ │ │ │ │ + * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ │ */ │ │ │ │ │ - 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; │ │ │ │ │ + namespaces: { │ │ │ │ │ + ows: "http://www.opengis.net/ows", │ │ │ │ │ + xlink: "http://www.w3.org/1999/xlink" │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - 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; │ │ │ │ │ + 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); │ │ │ │ │ } │ │ │ │ │ - } │ │ │ │ │ - │ │ │ │ │ - return withinMaxExtent || this.layer.displayOutsideMaxExtent; │ │ │ │ │ + }, OpenLayers.Format.OWSCommon.v1.prototype.readers.ows) │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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 │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - moveTo: function(bounds, position, redraw) { │ │ │ │ │ - if (redraw == null) { │ │ │ │ │ - redraw = true; │ │ │ │ │ - } │ │ │ │ │ - │ │ │ │ │ - this.setBounds(bounds); │ │ │ │ │ - this.position = position.clone(); │ │ │ │ │ - if (redraw) { │ │ │ │ │ - this.draw(); │ │ │ │ │ - } │ │ │ │ │ + writers: { │ │ │ │ │ + "ows": OpenLayers.Format.OWSCommon.v1.prototype.writers.ows │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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.Format.OWSCommon.v1_0_0" │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Tile" │ │ │ │ │ }); │ │ │ │ │ /* ====================================================================== │ │ │ │ │ - OpenLayers/Tile/Image.js │ │ │ │ │ + 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/Tile.js │ │ │ │ │ - * @requires OpenLayers/Animation.js │ │ │ │ │ - * @requires OpenLayers/Util.js │ │ │ │ │ + * @requires OpenLayers/Format/WFST/v1.js │ │ │ │ │ + * @requires OpenLayers/Format/Filter/v1_1_0.js │ │ │ │ │ + * @requires OpenLayers/Format/OWSCommon/v1_0_0.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. │ │ │ │ │ + * 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.Tile.Image = OpenLayers.Class(OpenLayers.Tile, { │ │ │ │ │ +OpenLayers.Format.WFST.v1_1_0 = OpenLayers.Class( │ │ │ │ │ + OpenLayers.Format.Filter.v1_1_0, OpenLayers.Format.WFST.v1, { │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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. │ │ │ │ │ - */ │ │ │ │ │ + /** │ │ │ │ │ + * Property: version │ │ │ │ │ + * {String} WFS version number. │ │ │ │ │ + */ │ │ │ │ │ + version: "1.1.0", │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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: schemaLocations │ │ │ │ │ + * {Object} Properties are namespace aliases, values are schema locations. │ │ │ │ │ + */ │ │ │ │ │ + schemaLocations: { │ │ │ │ │ + "wfs": "http://schemas.opengis.net/wfs/1.1.0/wfs.xsd" │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: imgDiv │ │ │ │ │ - * {HTMLImageElement} The image for this tile. │ │ │ │ │ - */ │ │ │ │ │ - imgDiv: null, │ │ │ │ │ + /** │ │ │ │ │ + * 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]); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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, │ │ │ │ │ + /** │ │ │ │ │ + * 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: imageReloadAttempts │ │ │ │ │ - * {Integer} Attempts to load the image. │ │ │ │ │ - */ │ │ │ │ │ - imageReloadAttempts: 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: { │ │ │ │ │ + "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: layerAlphaHack │ │ │ │ │ - * {Boolean} True if the png alpha hack needs to be applied on the layer's div. │ │ │ │ │ - */ │ │ │ │ │ - layerAlphaHack: null, │ │ │ │ │ + /** │ │ │ │ │ + * Property: writers │ │ │ │ │ + * As a compliment to the readers property, this structure contains public │ │ │ │ │ + * writing functions grouped by namespace alias and named like the │ │ │ │ │ + * node names they produce. │ │ │ │ │ + */ │ │ │ │ │ + writers: { │ │ │ │ │ + "wfs": OpenLayers.Util.applyDefaults({ │ │ │ │ │ + "GetFeature": function(options) { │ │ │ │ │ + var node = OpenLayers.Format.WFST.v1.prototype.writers["wfs"]["GetFeature"].apply(this, arguments); │ │ │ │ │ + options && this.setAttributes(node, { │ │ │ │ │ + resultType: options.resultType, │ │ │ │ │ + startIndex: options.startIndex, │ │ │ │ │ + count: options.count │ │ │ │ │ + }); │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "Query": function(options) { │ │ │ │ │ + options = OpenLayers.Util.extend({ │ │ │ │ │ + featureNS: this.featureNS, │ │ │ │ │ + featurePrefix: this.featurePrefix, │ │ │ │ │ + featureType: this.featureType, │ │ │ │ │ + srsName: this.srsName │ │ │ │ │ + }, options); │ │ │ │ │ + var prefix = options.featurePrefix; │ │ │ │ │ + var node = this.createElementNSPlus("wfs:Query", { │ │ │ │ │ + attributes: { │ │ │ │ │ + typeName: (prefix ? prefix + ":" : "") + │ │ │ │ │ + options.featureType, │ │ │ │ │ + srsName: options.srsName │ │ │ │ │ + } │ │ │ │ │ + }); │ │ │ │ │ + if (options.featureNS) { │ │ │ │ │ + node.setAttribute("xmlns:" + prefix, options.featureNS); │ │ │ │ │ + } │ │ │ │ │ + if (options.propertyNames) { │ │ │ │ │ + for (var i = 0, len = options.propertyNames.length; i < len; i++) { │ │ │ │ │ + this.writeNode( │ │ │ │ │ + "wfs:PropertyName", { │ │ │ │ │ + property: options.propertyNames[i] │ │ │ │ │ + }, │ │ │ │ │ + node │ │ │ │ │ + ); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + if (options.filter) { │ │ │ │ │ + OpenLayers.Format.WFST.v1_1_0.prototype.setFilterProperty.call(this, options.filter); │ │ │ │ │ + this.writeNode("ogc:Filter", options.filter, node); │ │ │ │ │ + } │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "PropertyName": function(obj) { │ │ │ │ │ + return this.createElementNSPlus("wfs:PropertyName", { │ │ │ │ │ + value: obj.property │ │ │ │ │ + }); │ │ │ │ │ + } │ │ │ │ │ + }, OpenLayers.Format.WFST.v1.prototype.writers["wfs"]), │ │ │ │ │ + "gml": OpenLayers.Format.GML.v3.prototype.writers["gml"], │ │ │ │ │ + "feature": OpenLayers.Format.GML.v3.prototype.writers["feature"], │ │ │ │ │ + "ogc": OpenLayers.Format.Filter.v1_1_0.prototype.writers["ogc"] │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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, │ │ │ │ │ + CLASS_NAME: "OpenLayers.Format.WFST.v1_1_0" │ │ │ │ │ + }); │ │ │ │ │ +/* ====================================================================== │ │ │ │ │ + OpenLayers/Format/WPSExecute.js │ │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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, │ │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ + * full text of the license. */ │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: canvasContext │ │ │ │ │ - * {CanvasRenderingContext2D} A canvas context associated with │ │ │ │ │ - * the tile image. │ │ │ │ │ - */ │ │ │ │ │ - canvasContext: null, │ │ │ │ │ +/** │ │ │ │ │ + * @requires OpenLayers/Format/XML.js │ │ │ │ │ + * @requires OpenLayers/Format/OWSCommon/v1_1_0.js │ │ │ │ │ + * @requires OpenLayers/Format/WCSGetCoverage.js │ │ │ │ │ + * @requires OpenLayers/Format/WFST/v1_1_0.js │ │ │ │ │ + */ │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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, │ │ │ │ │ +/** │ │ │ │ │ + * Class: OpenLayers.Format.WPSExecute version 1.0.0 │ │ │ │ │ + * │ │ │ │ │ + * Inherits from: │ │ │ │ │ + * - │ │ │ │ │ + */ │ │ │ │ │ +OpenLayers.Format.WPSExecute = OpenLayers.Class(OpenLayers.Format.XML, │ │ │ │ │ + OpenLayers.Format.Filter.v1_1_0, { │ │ │ │ │ │ │ │ │ │ - /** 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. │ │ │ │ │ - */ │ │ │ │ │ + /** │ │ │ │ │ + * Property: namespaces │ │ │ │ │ + * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ │ + */ │ │ │ │ │ + namespaces: { │ │ │ │ │ + ows: "http://www.opengis.net/ows/1.1", │ │ │ │ │ + gml: "http://www.opengis.net/gml", │ │ │ │ │ + wps: "http://www.opengis.net/wps/1.0.0", │ │ │ │ │ + wfs: "http://www.opengis.net/wfs", │ │ │ │ │ + ogc: "http://www.opengis.net/ogc", │ │ │ │ │ + wcs: "http://www.opengis.net/wcs", │ │ │ │ │ + xlink: "http://www.w3.org/1999/xlink", │ │ │ │ │ + xsi: "http://www.w3.org/2001/XMLSchema-instance" │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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); │ │ │ │ │ + /** │ │ │ │ │ + * Property: regExes │ │ │ │ │ + * Compiled regular expressions for manipulating strings. │ │ │ │ │ + */ │ │ │ │ │ + regExes: { │ │ │ │ │ + trimSpace: (/^\s*|\s*$/g), │ │ │ │ │ + removeSpace: (/\s*/g), │ │ │ │ │ + splitSpace: (/\s+/), │ │ │ │ │ + trimComma: (/\s*,\s*/g) │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - this.url = url; //deprecated remove me │ │ │ │ │ + /** │ │ │ │ │ + * Constant: VERSION │ │ │ │ │ + * {String} 1.0.0 │ │ │ │ │ + */ │ │ │ │ │ + VERSION: "1.0.0", │ │ │ │ │ │ │ │ │ │ - this.layerAlphaHack = this.layer.alpha && OpenLayers.Util.alphaHack(); │ │ │ │ │ + /** │ │ │ │ │ + * Property: schemaLocation │ │ │ │ │ + * {String} Schema location │ │ │ │ │ + */ │ │ │ │ │ + schemaLocation: "http://www.opengis.net/wps/1.0.0 http://schemas.opengis.net/wps/1.0.0/wpsAll.xsd", │ │ │ │ │ │ │ │ │ │ - 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); │ │ │ │ │ - } │ │ │ │ │ - }, │ │ │ │ │ + schemaLocationAttr: function(options) { │ │ │ │ │ + return undefined; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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); │ │ │ │ │ - }, │ │ │ │ │ + /** │ │ │ │ │ + * Constructor: OpenLayers.Format.WPSExecute │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ │ + * this instance. │ │ │ │ │ + */ │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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"; │ │ │ │ │ + /** │ │ │ │ │ + * Method: write │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * options - {Object} Optional object. │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {String} An WPS Execute request XML string. │ │ │ │ │ + */ │ │ │ │ │ + write: function(options) { │ │ │ │ │ + var doc; │ │ │ │ │ + if (window.ActiveXObject) { │ │ │ │ │ + doc = new ActiveXObject("Microsoft.XMLDOM"); │ │ │ │ │ + this.xmldom = doc; │ │ │ │ │ } else { │ │ │ │ │ - this.isLoading = true; │ │ │ │ │ - this._loadEvent = "loadstart"; │ │ │ │ │ + doc = document.implementation.createDocument("", "", null); │ │ │ │ │ } │ │ │ │ │ - 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"; │ │ │ │ │ - }, │ │ │ │ │ + var node = this.writeNode("wps:Execute", options, doc); │ │ │ │ │ + this.setAttributeNS( │ │ │ │ │ + node, this.namespaces.xsi, │ │ │ │ │ + "xsi:schemaLocation", this.schemaLocation │ │ │ │ │ + ); │ │ │ │ │ + return OpenLayers.Format.XML.prototype.write.apply(this, [node]); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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); │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: read │ │ │ │ │ + * Parse a WPS Execute and return an object with its information. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * data - {String} or {DOMElement} data to read/parse. │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Object} │ │ │ │ │ + */ │ │ │ │ │ + read: function(data) { │ │ │ │ │ + if (typeof data == "string") { │ │ │ │ │ + data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); │ │ │ │ │ } │ │ │ │ │ - this.setImgSrc(); │ │ │ │ │ - if (this.layerAlphaHack === true) { │ │ │ │ │ - img.style.filter = ""; │ │ │ │ │ + if (data && data.nodeType == 9) { │ │ │ │ │ + data = data.documentElement; │ │ │ │ │ } │ │ │ │ │ - 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 info = {}; │ │ │ │ │ + this.readNode(data, info); │ │ │ │ │ + return info; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - 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; │ │ │ │ │ + /** │ │ │ │ │ + * Property: writers │ │ │ │ │ + * As a compliment to the readers property, this structure contains public │ │ │ │ │ + * writing functions grouped by namespace alias and named like the │ │ │ │ │ + * node names they produce. │ │ │ │ │ + */ │ │ │ │ │ + writers: { │ │ │ │ │ + "wps": { │ │ │ │ │ + "Execute": function(options) { │ │ │ │ │ + var node = this.createElementNSPlus("wps:Execute", { │ │ │ │ │ + attributes: { │ │ │ │ │ + version: this.VERSION, │ │ │ │ │ + service: 'WPS' │ │ │ │ │ + } │ │ │ │ │ + }); │ │ │ │ │ + this.writeNode("ows:Identifier", options.identifier, node); │ │ │ │ │ + this.writeNode("wps:DataInputs", options.dataInputs, node); │ │ │ │ │ + this.writeNode("wps:ResponseForm", options.responseForm, node); │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "ResponseForm": function(responseForm) { │ │ │ │ │ + var node = this.createElementNSPlus("wps:ResponseForm", {}); │ │ │ │ │ + if (responseForm.rawDataOutput) { │ │ │ │ │ + this.writeNode("wps:RawDataOutput", responseForm.rawDataOutput, node); │ │ │ │ │ + } │ │ │ │ │ + if (responseForm.responseDocument) { │ │ │ │ │ + this.writeNode("wps:ResponseDocument", responseForm.responseDocument, node); │ │ │ │ │ + } │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "ResponseDocument": function(responseDocument) { │ │ │ │ │ + var node = this.createElementNSPlus("wps:ResponseDocument", { │ │ │ │ │ + attributes: { │ │ │ │ │ + storeExecuteResponse: responseDocument.storeExecuteResponse, │ │ │ │ │ + lineage: responseDocument.lineage, │ │ │ │ │ + status: responseDocument.status │ │ │ │ │ + } │ │ │ │ │ + }); │ │ │ │ │ + if (responseDocument.outputs) { │ │ │ │ │ + for (var i = 0, len = responseDocument.outputs.length; i < len; i++) { │ │ │ │ │ + this.writeNode("wps:Output", responseDocument.outputs[i], node); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "Output": function(output) { │ │ │ │ │ + var node = this.createElementNSPlus("wps:Output", { │ │ │ │ │ + attributes: { │ │ │ │ │ + asReference: output.asReference, │ │ │ │ │ + mimeType: output.mimeType, │ │ │ │ │ + encoding: output.encoding, │ │ │ │ │ + schema: output.schema │ │ │ │ │ + } │ │ │ │ │ + }); │ │ │ │ │ + this.writeNode("ows:Identifier", output.identifier, node); │ │ │ │ │ + this.writeNode("ows:Title", output.title, node); │ │ │ │ │ + this.writeNode("ows:Abstract", output["abstract"], node); │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "RawDataOutput": function(rawDataOutput) { │ │ │ │ │ + var node = this.createElementNSPlus("wps:RawDataOutput", { │ │ │ │ │ + attributes: { │ │ │ │ │ + mimeType: rawDataOutput.mimeType, │ │ │ │ │ + encoding: rawDataOutput.encoding, │ │ │ │ │ + schema: rawDataOutput.schema │ │ │ │ │ + } │ │ │ │ │ + }); │ │ │ │ │ + this.writeNode("ows:Identifier", rawDataOutput.identifier, node); │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "DataInputs": function(dataInputs) { │ │ │ │ │ + var node = this.createElementNSPlus("wps:DataInputs", {}); │ │ │ │ │ + for (var i = 0, ii = dataInputs.length; i < ii; ++i) { │ │ │ │ │ + this.writeNode("wps:Input", dataInputs[i], node); │ │ │ │ │ + } │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "Input": function(input) { │ │ │ │ │ + var node = this.createElementNSPlus("wps:Input", {}); │ │ │ │ │ + this.writeNode("ows:Identifier", input.identifier, node); │ │ │ │ │ + if (input.title) { │ │ │ │ │ + this.writeNode("ows:Title", input.title, node); │ │ │ │ │ + } │ │ │ │ │ + if (input.data) { │ │ │ │ │ + this.writeNode("wps:Data", input.data, node); │ │ │ │ │ + } │ │ │ │ │ + if (input.reference) { │ │ │ │ │ + this.writeNode("wps:Reference", input.reference, node); │ │ │ │ │ + } │ │ │ │ │ + if (input.boundingBoxData) { │ │ │ │ │ + this.writeNode("wps:BoundingBoxData", input.boundingBoxData, node); │ │ │ │ │ + } │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "Data": function(data) { │ │ │ │ │ + var node = this.createElementNSPlus("wps:Data", {}); │ │ │ │ │ + if (data.literalData) { │ │ │ │ │ + this.writeNode("wps:LiteralData", data.literalData, node); │ │ │ │ │ + } else if (data.complexData) { │ │ │ │ │ + this.writeNode("wps:ComplexData", data.complexData, node); │ │ │ │ │ + } else if (data.boundingBoxData) { │ │ │ │ │ + this.writeNode("ows:BoundingBox", data.boundingBoxData, node); │ │ │ │ │ + } │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "LiteralData": function(literalData) { │ │ │ │ │ + var node = this.createElementNSPlus("wps:LiteralData", { │ │ │ │ │ + attributes: { │ │ │ │ │ + uom: literalData.uom │ │ │ │ │ + }, │ │ │ │ │ + value: literalData.value │ │ │ │ │ + }); │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "ComplexData": function(complexData) { │ │ │ │ │ + var node = this.createElementNSPlus("wps:ComplexData", { │ │ │ │ │ + attributes: { │ │ │ │ │ + mimeType: complexData.mimeType, │ │ │ │ │ + encoding: complexData.encoding, │ │ │ │ │ + schema: complexData.schema │ │ │ │ │ + } │ │ │ │ │ + }); │ │ │ │ │ + var data = complexData.value; │ │ │ │ │ + if (typeof data === "string") { │ │ │ │ │ + node.appendChild( │ │ │ │ │ + this.getXMLDoc().createCDATASection(complexData.value) │ │ │ │ │ + ); │ │ │ │ │ + } else { │ │ │ │ │ + node.appendChild(data); │ │ │ │ │ + } │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "Reference": function(reference) { │ │ │ │ │ + var node = this.createElementNSPlus("wps:Reference", { │ │ │ │ │ + attributes: { │ │ │ │ │ + mimeType: reference.mimeType, │ │ │ │ │ + "xlink:href": reference.href, │ │ │ │ │ + method: reference.method, │ │ │ │ │ + encoding: reference.encoding, │ │ │ │ │ + schema: reference.schema │ │ │ │ │ + } │ │ │ │ │ + }); │ │ │ │ │ + if (reference.body) { │ │ │ │ │ + this.writeNode("wps:Body", reference.body, node); │ │ │ │ │ + } │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + "BoundingBoxData": function(node, obj) { │ │ │ │ │ + this.writers['ows']['BoundingBox'].apply(this, [node, obj, "wps:BoundingBoxData"]); │ │ │ │ │ + }, │ │ │ │ │ + "Body": function(body) { │ │ │ │ │ + var node = this.createElementNSPlus("wps:Body", {}); │ │ │ │ │ + if (body.wcs) { │ │ │ │ │ + this.writeNode("wcs:GetCoverage", body.wcs, node); │ │ │ │ │ + } else if (body.wfs) { │ │ │ │ │ + // OpenLayers.Format.WFST expects these to be on the │ │ │ │ │ + // instance and not in the options │ │ │ │ │ + this.featureType = body.wfs.featureType; │ │ │ │ │ + this.version = body.wfs.version; │ │ │ │ │ + this.writeNode("wfs:GetFeature", body.wfs, node); │ │ │ │ │ + } else { │ │ │ │ │ + this.writeNode("wps:Execute", body, node); │ │ │ │ │ + } │ │ │ │ │ + return node; │ │ │ │ │ } │ │ │ │ │ - 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; │ │ │ │ │ - }, │ │ │ │ │ + }, │ │ │ │ │ + "wcs": OpenLayers.Format.WCSGetCoverage.prototype.writers.wcs, │ │ │ │ │ + "wfs": OpenLayers.Format.WFST.v1_1_0.prototype.writers.wfs, │ │ │ │ │ + "ogc": OpenLayers.Format.Filter.v1_1_0.prototype.writers.ogc, │ │ │ │ │ + "ows": OpenLayers.Format.OWSCommon.v1_1_0.prototype.writers.ows │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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; │ │ │ │ │ - }, │ │ │ │ │ + /** │ │ │ │ │ + * Property: readers │ │ │ │ │ + * Contains public functions, grouped by namespace prefix, that will │ │ │ │ │ + * be applied when a namespaced node is found matching the function │ │ │ │ │ + * name. The function will be applied in the scope of this parser │ │ │ │ │ + * with two arguments: the node being read and a context object passed │ │ │ │ │ + * from the parent. │ │ │ │ │ + */ │ │ │ │ │ + readers: { │ │ │ │ │ + "wps": { │ │ │ │ │ + "ExecuteResponse": function(node, obj) { │ │ │ │ │ + obj.executeResponse = { │ │ │ │ │ + lang: node.getAttribute("lang"), │ │ │ │ │ + statusLocation: node.getAttribute("statusLocation"), │ │ │ │ │ + serviceInstance: node.getAttribute("serviceInstance"), │ │ │ │ │ + service: node.getAttribute("service") │ │ │ │ │ + }; │ │ │ │ │ + this.readChildNodes(node, obj.executeResponse); │ │ │ │ │ + }, │ │ │ │ │ + "Process": function(node, obj) { │ │ │ │ │ + obj.process = {}; │ │ │ │ │ + this.readChildNodes(node, obj.process); │ │ │ │ │ + }, │ │ │ │ │ + "Status": function(node, obj) { │ │ │ │ │ + obj.status = { │ │ │ │ │ + creationTime: node.getAttribute("creationTime") │ │ │ │ │ + }; │ │ │ │ │ + this.readChildNodes(node, obj.status); │ │ │ │ │ + }, │ │ │ │ │ + "ProcessSucceeded": function(node, obj) { │ │ │ │ │ + obj.processSucceeded = true; │ │ │ │ │ + }, │ │ │ │ │ + "ProcessOutputs": function(node, processDescription) { │ │ │ │ │ + processDescription.processOutputs = []; │ │ │ │ │ + this.readChildNodes(node, processDescription.processOutputs); │ │ │ │ │ + }, │ │ │ │ │ + "Output": function(node, processOutputs) { │ │ │ │ │ + var output = {}; │ │ │ │ │ + this.readChildNodes(node, output); │ │ │ │ │ + processOutputs.push(output); │ │ │ │ │ + }, │ │ │ │ │ + "Reference": function(node, output) { │ │ │ │ │ + output.reference = { │ │ │ │ │ + href: node.getAttribute("href"), │ │ │ │ │ + mimeType: node.getAttribute("mimeType"), │ │ │ │ │ + encoding: node.getAttribute("encoding"), │ │ │ │ │ + schema: node.getAttribute("schema") │ │ │ │ │ + }; │ │ │ │ │ + }, │ │ │ │ │ + "Data": function(node, output) { │ │ │ │ │ + output.data = {}; │ │ │ │ │ + this.readChildNodes(node, output); │ │ │ │ │ + }, │ │ │ │ │ + "LiteralData": function(node, output) { │ │ │ │ │ + output.literalData = { │ │ │ │ │ + dataType: node.getAttribute("dataType"), │ │ │ │ │ + uom: node.getAttribute("uom"), │ │ │ │ │ + value: this.getChildValue(node) │ │ │ │ │ + }; │ │ │ │ │ + }, │ │ │ │ │ + "ComplexData": function(node, output) { │ │ │ │ │ + output.complexData = { │ │ │ │ │ + mimeType: node.getAttribute("mimeType"), │ │ │ │ │ + schema: node.getAttribute("schema"), │ │ │ │ │ + encoding: node.getAttribute("encoding"), │ │ │ │ │ + value: "" │ │ │ │ │ + }; │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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); │ │ │ │ │ - } │ │ │ │ │ - }, │ │ │ │ │ + // try to get *some* value, ignore the empty text values │ │ │ │ │ + if (this.isSimpleContent(node)) { │ │ │ │ │ + var child; │ │ │ │ │ + for (child = node.firstChild; child; child = child.nextSibling) { │ │ │ │ │ + switch (child.nodeType) { │ │ │ │ │ + case 3: // text node │ │ │ │ │ + case 4: // cdata section │ │ │ │ │ + output.complexData.value += child.nodeValue; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } else { │ │ │ │ │ + for (child = node.firstChild; child; child = child.nextSibling) { │ │ │ │ │ + if (child.nodeType == 1) { │ │ │ │ │ + output.complexData.value = child; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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"); │ │ │ │ │ + }, │ │ │ │ │ + "BoundingBox": function(node, output) { │ │ │ │ │ + output.boundingBoxData = { │ │ │ │ │ + dimensions: node.getAttribute("dimensions"), │ │ │ │ │ + crs: node.getAttribute("crs") │ │ │ │ │ + }; │ │ │ │ │ + this.readChildNodes(node, output.boundingBoxData); │ │ │ │ │ } │ │ │ │ │ - } │ │ │ │ │ - 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" │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ -}); │ │ │ │ │ + // TODO: we should add Exception parsing here │ │ │ │ │ + "ows": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers["ows"] │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * 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; │ │ │ │ │ -}()); │ │ │ │ │ + CLASS_NAME: "OpenLayers.Format.WPSExecute" │ │ │ │ │ │ │ │ │ │ + }); │ │ │ │ │ /* ====================================================================== │ │ │ │ │ - OpenLayers/Layer/Grid.js │ │ │ │ │ + OpenLayers/WPSProcess.js │ │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ │ │ +/** │ │ │ │ │ + * @requires OpenLayers/SingleFile.js │ │ │ │ │ + */ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * @requires OpenLayers/Layer/HTTPRequest.js │ │ │ │ │ - * @requires OpenLayers/Tile/Image.js │ │ │ │ │ + * @requires OpenLayers/Geometry.js │ │ │ │ │ + * @requires OpenLayers/Feature/Vector.js │ │ │ │ │ + * @requires OpenLayers/Format/WKT.js │ │ │ │ │ + * @requires OpenLayers/Format/GeoJSON.js │ │ │ │ │ + * @requires OpenLayers/Format/WPSExecute.js │ │ │ │ │ + * @requires OpenLayers/Request.js │ │ │ │ │ */ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Class: OpenLayers.Layer.Grid │ │ │ │ │ - * Base class for layers that use a lattice of tiles. Create a new grid │ │ │ │ │ - * layer with the constructor. │ │ │ │ │ + * Class: OpenLayers.WPSProcess │ │ │ │ │ + * Representation of a WPS process. Usually instances of │ │ │ │ │ + * are created by calling 'getProcess' on an │ │ │ │ │ + * instance. │ │ │ │ │ * │ │ │ │ │ - * Inherits from: │ │ │ │ │ - * - │ │ │ │ │ + * Currently supports processes that have geometries │ │ │ │ │ + * or features as output, using WKT or GeoJSON as output format. It also │ │ │ │ │ + * supports chaining of processes by using the method to create a │ │ │ │ │ + * handle that is used as process input instead of a static value. │ │ │ │ │ */ │ │ │ │ │ -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, │ │ │ │ │ +OpenLayers.WPSProcess = OpenLayers.Class({ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: loading │ │ │ │ │ - * {Boolean} Indicates if tiles are being loaded. │ │ │ │ │ + * Property: client │ │ │ │ │ + * {} The client that manages this process. │ │ │ │ │ */ │ │ │ │ │ - loading: false, │ │ │ │ │ + client: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: backBuffer │ │ │ │ │ - * {DOMElement} The back buffer. │ │ │ │ │ + * Property: server │ │ │ │ │ + * {String} Local client identifier for this process's server. │ │ │ │ │ */ │ │ │ │ │ - backBuffer: null, │ │ │ │ │ + server: 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. │ │ │ │ │ + * Property: identifier │ │ │ │ │ + * {String} Process identifier known to the server. │ │ │ │ │ */ │ │ │ │ │ - gridResolution: null, │ │ │ │ │ + identifier: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: backBufferResolution │ │ │ │ │ - * {Number} The resolution of the current back buffer. This property is │ │ │ │ │ - * updated each time a back buffer is created. │ │ │ │ │ + * Property: description │ │ │ │ │ + * {Object} DescribeProcess response for this process. │ │ │ │ │ */ │ │ │ │ │ - backBufferResolution: null, │ │ │ │ │ + description: 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. │ │ │ │ │ + * APIProperty: localWPS │ │ │ │ │ + * {String} Service endpoint for locally chained WPS processes. Default is │ │ │ │ │ + * 'http://geoserver/wps'. │ │ │ │ │ */ │ │ │ │ │ - backBufferLonLat: null, │ │ │ │ │ + localWPS: 'http://geoserver/wps', │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * Property: formats │ │ │ │ │ + * {Object} OpenLayers.Format instances keyed by mimetype. │ │ │ │ │ */ │ │ │ │ │ - backBufferTimerId: null, │ │ │ │ │ + formats: 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. │ │ │ │ │ + * Property: chained │ │ │ │ │ + * {Integer} Number of chained processes for pending execute requests that │ │ │ │ │ + * don't have a full configuration yet. │ │ │ │ │ */ │ │ │ │ │ - removeBackBufferDelay: null, │ │ │ │ │ + chained: 0, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * Property: executeCallbacks │ │ │ │ │ + * {Array} Callbacks waiting to be executed until all chained processes │ │ │ │ │ + * are configured; │ │ │ │ │ */ │ │ │ │ │ - className: null, │ │ │ │ │ + executeCallbacks: 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. │ │ │ │ │ + * Constructor: OpenLayers.WPSProcess │ │ │ │ │ * │ │ │ │ │ - * 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. │ │ │ │ │ + * Parameters: │ │ │ │ │ + * options - {Object} Object whose properties will be set on the instance. │ │ │ │ │ * │ │ │ │ │ - * 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 │ │ │ │ │ + * Avaliable options: │ │ │ │ │ + * client - {} Mandatory. Client that manages this │ │ │ │ │ + * process. │ │ │ │ │ + * server - {String} Mandatory. Local client identifier of this process's │ │ │ │ │ + * server. │ │ │ │ │ + * identifier - {String} Mandatory. Process identifier known to the server. │ │ │ │ │ */ │ │ │ │ │ - transitionendEvents: [ │ │ │ │ │ - 'transitionend', 'webkitTransitionEnd', 'otransitionend', │ │ │ │ │ - 'oTransitionEnd' │ │ │ │ │ - ], │ │ │ │ │ + initialize: function(options) { │ │ │ │ │ + OpenLayers.Util.extend(this, options); │ │ │ │ │ + this.executeCallbacks = []; │ │ │ │ │ + this.formats = { │ │ │ │ │ + 'application/wkt': new OpenLayers.Format.WKT(), │ │ │ │ │ + 'application/json': new OpenLayers.Format.GeoJSON() │ │ │ │ │ + }; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Constructor: OpenLayers.Layer.Grid │ │ │ │ │ - * Create a new grid layer │ │ │ │ │ + * Method: describe │ │ │ │ │ + * Makes the client issue a DescribeProcess request asynchronously. │ │ │ │ │ * │ │ │ │ │ * 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 │ │ │ │ │ + * options - {Object} Configuration for the method call │ │ │ │ │ + * │ │ │ │ │ + * Available options: │ │ │ │ │ + * callback - {Function} Callback to execute when the description is │ │ │ │ │ + * available. Will be called with the parsed description as argument. │ │ │ │ │ + * Optional. │ │ │ │ │ + * scope - {Object} The scope in which the callback will be executed. │ │ │ │ │ + * Default is the global object. │ │ │ │ │ */ │ │ │ │ │ - initProperties: function() { │ │ │ │ │ - if (this.options.removeBackBufferDelay === undefined) { │ │ │ │ │ - this.removeBackBufferDelay = this.singleTile ? 0 : 2500; │ │ │ │ │ - } │ │ │ │ │ - │ │ │ │ │ - if (this.options.className === undefined) { │ │ │ │ │ - this.className = this.singleTile ? 'olLayerGridSingleTile' : │ │ │ │ │ - 'olLayerGrid'; │ │ │ │ │ + describe: function(options) { │ │ │ │ │ + options = options || {}; │ │ │ │ │ + if (!this.description) { │ │ │ │ │ + this.client.describeProcess(this.server, this.identifier, function(description) { │ │ │ │ │ + if (!this.description) { │ │ │ │ │ + this.parseDescription(description); │ │ │ │ │ + } │ │ │ │ │ + if (options.callback) { │ │ │ │ │ + options.callback.call(options.scope, this.description); │ │ │ │ │ + } │ │ │ │ │ + }, this); │ │ │ │ │ + } else if (options.callback) { │ │ │ │ │ + var description = this.description; │ │ │ │ │ + window.setTimeout(function() { │ │ │ │ │ + options.callback.call(options.scope, description); │ │ │ │ │ + }, 0); │ │ │ │ │ } │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: setMap │ │ │ │ │ + * APIMethod: configure │ │ │ │ │ + * Configure the process, but do not execute it. Use this for processes │ │ │ │ │ + * that are chained as input of a different process by means of the │ │ │ │ │ + * method. │ │ │ │ │ * │ │ │ │ │ * 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. │ │ │ │ │ + * options - {Object} │ │ │ │ │ * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * map - {} The map. │ │ │ │ │ - */ │ │ │ │ │ - removeMap: function(map) { │ │ │ │ │ - this.removeBackBuffer(); │ │ │ │ │ - }, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * APIMethod: destroy │ │ │ │ │ - * Deconstruct the layer and clear the grid. │ │ │ │ │ + * Returns: │ │ │ │ │ + * {} this process. │ │ │ │ │ + * │ │ │ │ │ + * Available options: │ │ │ │ │ + * inputs - {Object} The inputs for the process, keyed by input identifier. │ │ │ │ │ + * For spatial data inputs, the value of an input is usually an │ │ │ │ │ + * , an or an array of │ │ │ │ │ + * geometries or features. │ │ │ │ │ + * callback - {Function} Callback to call when the configuration is │ │ │ │ │ + * complete. Optional. │ │ │ │ │ + * scope - {Object} Optional scope for the callback. │ │ │ │ │ */ │ │ │ │ │ - destroy: function() { │ │ │ │ │ - this.removeBackBuffer(); │ │ │ │ │ - this.clearGrid(); │ │ │ │ │ - │ │ │ │ │ - this.grid = null; │ │ │ │ │ - this.tileSize = null; │ │ │ │ │ - OpenLayers.Layer.HTTPRequest.prototype.destroy.apply(this, arguments); │ │ │ │ │ + configure: function(options) { │ │ │ │ │ + this.describe({ │ │ │ │ │ + callback: function() { │ │ │ │ │ + var description = this.description, │ │ │ │ │ + inputs = options.inputs, │ │ │ │ │ + input, i, ii; │ │ │ │ │ + for (i = 0, ii = description.dataInputs.length; i < ii; ++i) { │ │ │ │ │ + input = description.dataInputs[i]; │ │ │ │ │ + this.setInputData(input, inputs[input.identifier]); │ │ │ │ │ + } │ │ │ │ │ + if (options.callback) { │ │ │ │ │ + options.callback.call(options.scope); │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ + scope: this │ │ │ │ │ + }); │ │ │ │ │ + return this; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: 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. │ │ │ │ │ - * │ │ │ │ │ + * APIMethod: execute │ │ │ │ │ + * Configures and executes the process │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * newParams - {Object} │ │ │ │ │ + * options - {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 │ │ │ │ │ + * Available options: │ │ │ │ │ + * inputs - {Object} The inputs for the process, keyed by input identifier. │ │ │ │ │ + * For spatial data inputs, the value of an input is usually an │ │ │ │ │ + * , an or an array of │ │ │ │ │ + * geometries or features. │ │ │ │ │ + * output - {String} The identifier of the output to request and parse. │ │ │ │ │ + * Optional. If not provided, the first output will be requested. │ │ │ │ │ + * success - {Function} Callback to call when the process is complete. │ │ │ │ │ + * This function is called with an outputs object as argument, which │ │ │ │ │ + * will have a property with the identifier of the requested output │ │ │ │ │ + * (or 'result' if output was not configured). For processes that │ │ │ │ │ + * generate spatial output, the value will be an array of │ │ │ │ │ + * instances. │ │ │ │ │ + * scope - {Object} Optional scope for the success callback. │ │ │ │ │ */ │ │ │ │ │ - clearGrid: function() { │ │ │ │ │ - if (this.grid) { │ │ │ │ │ - for (var iRow = 0, len = this.grid.length; iRow < len; iRow++) { │ │ │ │ │ - var row = this.grid[iRow]; │ │ │ │ │ - for (var iCol = 0, clen = row.length; iCol < clen; iCol++) { │ │ │ │ │ - var tile = row[iCol]; │ │ │ │ │ - this.destroyTile(tile); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - this.grid = []; │ │ │ │ │ - this.gridResolution = null; │ │ │ │ │ - this.gridLayout = null; │ │ │ │ │ - } │ │ │ │ │ + execute: function(options) { │ │ │ │ │ + this.configure({ │ │ │ │ │ + inputs: options.inputs, │ │ │ │ │ + callback: function() { │ │ │ │ │ + var me = this; │ │ │ │ │ + //TODO For now we only deal with a single output │ │ │ │ │ + var outputIndex = this.getOutputIndex( │ │ │ │ │ + me.description.processOutputs, options.output │ │ │ │ │ + ); │ │ │ │ │ + me.setResponseForm({ │ │ │ │ │ + outputIndex: outputIndex │ │ │ │ │ + }); │ │ │ │ │ + (function callback() { │ │ │ │ │ + OpenLayers.Util.removeItem(me.executeCallbacks, callback); │ │ │ │ │ + if (me.chained !== 0) { │ │ │ │ │ + // need to wait until chained processes have a │ │ │ │ │ + // description and configuration - see chainProcess │ │ │ │ │ + me.executeCallbacks.push(callback); │ │ │ │ │ + return; │ │ │ │ │ + } │ │ │ │ │ + // all chained processes are added as references now, so │ │ │ │ │ + // let's proceed. │ │ │ │ │ + OpenLayers.Request.POST({ │ │ │ │ │ + url: me.client.servers[me.server].url, │ │ │ │ │ + data: new OpenLayers.Format.WPSExecute().write(me.description), │ │ │ │ │ + success: function(response) { │ │ │ │ │ + var output = me.description.processOutputs[outputIndex]; │ │ │ │ │ + var mimeType = me.findMimeType( │ │ │ │ │ + output.complexOutput.supported.formats │ │ │ │ │ + ); │ │ │ │ │ + //TODO For now we assume a spatial output │ │ │ │ │ + var features = me.formats[mimeType].read(response.responseText); │ │ │ │ │ + if (features instanceof OpenLayers.Feature.Vector) { │ │ │ │ │ + features = [features]; │ │ │ │ │ + } │ │ │ │ │ + if (options.success) { │ │ │ │ │ + var outputs = {}; │ │ │ │ │ + outputs[options.output || 'result'] = features; │ │ │ │ │ + options.success.call(options.scope, outputs); │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ + scope: me │ │ │ │ │ + }); │ │ │ │ │ + })(); │ │ │ │ │ + }, │ │ │ │ │ + scope: this │ │ │ │ │ + }); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: addOptions │ │ │ │ │ - * │ │ │ │ │ + * APIMethod: output │ │ │ │ │ + * Chain an output of a configured process (see ) as input to │ │ │ │ │ + * another process. │ │ │ │ │ + * │ │ │ │ │ + * (code) │ │ │ │ │ + * intersect = client.getProcess('opengeo', 'JTS:intersection'); │ │ │ │ │ + * intersect.configure({ │ │ │ │ │ + * // ... │ │ │ │ │ + * }); │ │ │ │ │ + * buffer = client.getProcess('opengeo', 'JTS:buffer'); │ │ │ │ │ + * buffer.execute({ │ │ │ │ │ + * inputs: { │ │ │ │ │ + * geom: intersect.output('result'), // <-- here we're chaining │ │ │ │ │ + * distance: 1 │ │ │ │ │ + * }, │ │ │ │ │ + * // ... │ │ │ │ │ + * }); │ │ │ │ │ + * (end) │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * 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. │ │ │ │ │ + * identifier - {String} Identifier of the output that we're chaining. If │ │ │ │ │ + * not provided, the first output will be used. │ │ │ │ │ */ │ │ │ │ │ - addOptions: function(newOptions, reinitialize) { │ │ │ │ │ - var singleTileChanged = newOptions.singleTile !== undefined && │ │ │ │ │ - newOptions.singleTile !== this.singleTile; │ │ │ │ │ - OpenLayers.Layer.HTTPRequest.prototype.addOptions.apply(this, arguments); │ │ │ │ │ - if (this.map && singleTileChanged) { │ │ │ │ │ - this.initProperties(); │ │ │ │ │ - this.clearGrid(); │ │ │ │ │ - this.tileSize = this.options.tileSize; │ │ │ │ │ - this.setTileSize(); │ │ │ │ │ - this.moveTo(null, true); │ │ │ │ │ - } │ │ │ │ │ + output: function(identifier) { │ │ │ │ │ + return new OpenLayers.WPSProcess.ChainLink({ │ │ │ │ │ + process: this, │ │ │ │ │ + output: identifier │ │ │ │ │ + }); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: clone │ │ │ │ │ - * Create a clone of this layer │ │ │ │ │ + * Method: parseDescription │ │ │ │ │ + * Parses the DescribeProcess response │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * obj - {Object} Is this ever used? │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {} An exact clone of this OpenLayers.Layer.Grid │ │ │ │ │ + * description - {Object} │ │ │ │ │ */ │ │ │ │ │ - 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; │ │ │ │ │ + parseDescription: function(description) { │ │ │ │ │ + var server = this.client.servers[this.server]; │ │ │ │ │ + this.description = new OpenLayers.Format.WPSDescribeProcess() │ │ │ │ │ + .read(server.processDescription[this.identifier]) │ │ │ │ │ + .processDescriptions[this.identifier]; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: 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. │ │ │ │ │ + * Method: setInputData │ │ │ │ │ + * Sets the data for a single input │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * bounds - {} │ │ │ │ │ - * zoomChanged - {Boolean} │ │ │ │ │ - * dragging - {Boolean} │ │ │ │ │ + * input - {Object} An entry from the dataInputs array of the process │ │ │ │ │ + * description. │ │ │ │ │ + * data - {Mixed} For spatial data inputs, this is usually an │ │ │ │ │ + * , an or an array of │ │ │ │ │ + * geometries or features. │ │ │ │ │ */ │ │ │ │ │ - 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); │ │ │ │ │ - } │ │ │ │ │ + setInputData: function(input, data) { │ │ │ │ │ + // clear any previous data │ │ │ │ │ + delete input.data; │ │ │ │ │ + delete input.reference; │ │ │ │ │ + if (data instanceof OpenLayers.WPSProcess.ChainLink) { │ │ │ │ │ + ++this.chained; │ │ │ │ │ + input.reference = { │ │ │ │ │ + method: 'POST', │ │ │ │ │ + href: data.process.server === this.server ? │ │ │ │ │ + this.localWPS : this.client.servers[data.process.server].url │ │ │ │ │ + }; │ │ │ │ │ + data.process.describe({ │ │ │ │ │ + callback: function() { │ │ │ │ │ + --this.chained; │ │ │ │ │ + this.chainProcess(input, data); │ │ │ │ │ + }, │ │ │ │ │ + scope: this │ │ │ │ │ + }); │ │ │ │ │ + } else { │ │ │ │ │ + input.data = {}; │ │ │ │ │ + var complexData = input.complexData; │ │ │ │ │ + if (complexData) { │ │ │ │ │ + var format = this.findMimeType(complexData.supported.formats); │ │ │ │ │ + input.data.complexData = { │ │ │ │ │ + mimeType: format, │ │ │ │ │ + value: this.formats[format].write(this.toFeatures(data)) │ │ │ │ │ + }; │ │ │ │ │ } else { │ │ │ │ │ - │ │ │ │ │ - // 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(); │ │ │ │ │ - } │ │ │ │ │ + input.data.literalData = { │ │ │ │ │ + value: data │ │ │ │ │ + }; │ │ │ │ │ } │ │ │ │ │ } │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * Method: setResponseForm │ │ │ │ │ + * Sets the responseForm property of the payload. │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * loc - {} map location │ │ │ │ │ + * options - {Object} See below. │ │ │ │ │ * │ │ │ │ │ - * 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). │ │ │ │ │ + * Available options: │ │ │ │ │ + * outputIndex - {Integer} The index of the output to use. Optional. │ │ │ │ │ + * supportedFormats - {Object} Object with supported mime types as key, │ │ │ │ │ + * and true as value for supported types. Optional. │ │ │ │ │ */ │ │ │ │ │ - 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) │ │ │ │ │ - }; │ │ │ │ │ - } │ │ │ │ │ + setResponseForm: function(options) { │ │ │ │ │ + options = options || {}; │ │ │ │ │ + var output = this.description.processOutputs[options.outputIndex || 0]; │ │ │ │ │ + this.description.responseForm = { │ │ │ │ │ + rawDataOutput: { │ │ │ │ │ + identifier: output.identifier, │ │ │ │ │ + mimeType: this.findMimeType(output.complexOutput.supported.formats, options.supportedFormats) │ │ │ │ │ } │ │ │ │ │ - } │ │ │ │ │ - return data; │ │ │ │ │ - }, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Method: destroyTile │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * tile - {} │ │ │ │ │ - */ │ │ │ │ │ - destroyTile: function(tile) { │ │ │ │ │ - this.removeTileMonitoringHooks(tile); │ │ │ │ │ - tile.destroy(); │ │ │ │ │ + }; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: getServerResolution │ │ │ │ │ - * Return the closest server-supported resolution. │ │ │ │ │ + * Method: getOutputIndex │ │ │ │ │ + * Gets the index of a processOutput by its identifier │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * resolution - {Number} The base resolution. If undefined the │ │ │ │ │ - * map resolution is used. │ │ │ │ │ + * outputs - {Array} The processOutputs array to look at │ │ │ │ │ + * identifier - {String} The identifier of the output │ │ │ │ │ * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Number} The closest server resolution value. │ │ │ │ │ + * Returns │ │ │ │ │ + * {Integer} The index of the processOutput with the provided identifier │ │ │ │ │ + * in the outputs array. │ │ │ │ │ */ │ │ │ │ │ - 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) { │ │ │ │ │ + getOutputIndex: function(outputs, identifier) { │ │ │ │ │ + var output; │ │ │ │ │ + if (identifier) { │ │ │ │ │ + for (var i = outputs.length - 1; i >= 0; --i) { │ │ │ │ │ + if (outputs[i].identifier === identifier) { │ │ │ │ │ + output = i; │ │ │ │ │ break; │ │ │ │ │ } │ │ │ │ │ - distance = newDistance; │ │ │ │ │ - serverResolution = newResolution; │ │ │ │ │ } │ │ │ │ │ - resolution = serverResolution; │ │ │ │ │ + } else { │ │ │ │ │ + output = 0; │ │ │ │ │ } │ │ │ │ │ - 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); │ │ │ │ │ + return output; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: applyBackBuffer │ │ │ │ │ - * Create, insert, scale and position a back buffer for the layer. │ │ │ │ │ + * Method: chainProcess │ │ │ │ │ + * Sets a fully configured chained process as input for this process. │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * resolution - {Number} The resolution to transition to. │ │ │ │ │ + * input - {Object} The dataInput that the chained process provides. │ │ │ │ │ + * chainLink - {} The process to chain. │ │ │ │ │ */ │ │ │ │ │ - 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'; │ │ │ │ │ + chainProcess: function(input, chainLink) { │ │ │ │ │ + var output = this.getOutputIndex( │ │ │ │ │ + chainLink.process.description.processOutputs, chainLink.output │ │ │ │ │ + ); │ │ │ │ │ + input.reference.mimeType = this.findMimeType( │ │ │ │ │ + input.complexData.supported.formats, │ │ │ │ │ + chainLink.process.description.processOutputs[output].complexOutput.supported.formats │ │ │ │ │ + ); │ │ │ │ │ + var formats = {}; │ │ │ │ │ + formats[input.reference.mimeType] = true; │ │ │ │ │ + chainLink.process.setResponseForm({ │ │ │ │ │ + outputIndex: output, │ │ │ │ │ + supportedFormats: formats │ │ │ │ │ + }); │ │ │ │ │ + input.reference.body = chainLink.process.description; │ │ │ │ │ + while (this.executeCallbacks.length > 0) { │ │ │ │ │ + this.executeCallbacks[0](); │ │ │ │ │ } │ │ │ │ │ - │ │ │ │ │ - // 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. │ │ │ │ │ + * Method: toFeatures │ │ │ │ │ + * Converts spatial input into features so it can be processed by │ │ │ │ │ + * instances. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * source - {Mixed} An , an │ │ │ │ │ + * , or an array of geometries or features │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {DOMElement} The DOM element for the back buffer, undefined if the │ │ │ │ │ - * grid isn't initialized yet. │ │ │ │ │ - */ │ │ │ │ │ - createBackBuffer: function() { │ │ │ │ │ - var backBuffer; │ │ │ │ │ - if (this.grid.length > 0) { │ │ │ │ │ - backBuffer = document.createElement('div'); │ │ │ │ │ - backBuffer.id = this.div.id + '_bb'; │ │ │ │ │ - backBuffer.className = 'olBackBuffer'; │ │ │ │ │ - backBuffer.style.position = 'absolute'; │ │ │ │ │ - var map = this.map; │ │ │ │ │ - backBuffer.style.zIndex = this.transitionEffect === 'resize' ? │ │ │ │ │ - this.getZIndex() - 1 : │ │ │ │ │ - // 'map-resize': │ │ │ │ │ - map.Z_INDEX_BASE.BaseLayer - │ │ │ │ │ - (map.getNumLayers() - map.getLayerIndex(this)); │ │ │ │ │ - for (var i = 0, lenI = this.grid.length; i < lenI; i++) { │ │ │ │ │ - for (var j = 0, lenJ = this.grid[i].length; j < lenJ; j++) { │ │ │ │ │ - var tile = this.grid[i][j], │ │ │ │ │ - markup = this.grid[i][j].createBackBuffer(); │ │ │ │ │ - if (markup) { │ │ │ │ │ - markup._i = i; │ │ │ │ │ - markup._j = j; │ │ │ │ │ - markup._w = tile.size.w; │ │ │ │ │ - markup._h = tile.size.h; │ │ │ │ │ - markup.id = tile.id + '_bb'; │ │ │ │ │ - backBuffer.appendChild(markup); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - return backBuffer; │ │ │ │ │ - }, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Method: removeBackBuffer │ │ │ │ │ - * Remove back buffer from DOM. │ │ │ │ │ + * {Array()} │ │ │ │ │ */ │ │ │ │ │ - removeBackBuffer: function() { │ │ │ │ │ - if (this._transitionElement) { │ │ │ │ │ - for (var i = this.transitionendEvents.length - 1; i >= 0; --i) { │ │ │ │ │ - OpenLayers.Event.stopObserving(this._transitionElement, │ │ │ │ │ - this.transitionendEvents[i], this._removeBackBuffer); │ │ │ │ │ - } │ │ │ │ │ - delete this._transitionElement; │ │ │ │ │ + toFeatures: function(source) { │ │ │ │ │ + var isArray = OpenLayers.Util.isArray(source); │ │ │ │ │ + if (!isArray) { │ │ │ │ │ + source = [source]; │ │ │ │ │ } │ │ │ │ │ - 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; │ │ │ │ │ - } │ │ │ │ │ + var target = new Array(source.length), │ │ │ │ │ + current; │ │ │ │ │ + for (var i = 0, ii = source.length; i < ii; ++i) { │ │ │ │ │ + current = source[i]; │ │ │ │ │ + target[i] = current instanceof OpenLayers.Feature.Vector ? │ │ │ │ │ + current : new OpenLayers.Feature.Vector(current); │ │ │ │ │ } │ │ │ │ │ + return isArray ? target : target[0]; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: moveByPx │ │ │ │ │ - * Move the layer based on pixel vector. │ │ │ │ │ + * Method: findMimeType │ │ │ │ │ + * Finds a supported mime type. │ │ │ │ │ * │ │ │ │ │ * 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. │ │ │ │ │ + * sourceFormats - {Object} An object literal with mime types as key and │ │ │ │ │ + * true as value for supported formats. │ │ │ │ │ + * targetFormats - {Object} Like , but optional to check for │ │ │ │ │ + * supported mime types on a different target than this process. │ │ │ │ │ + * Default is to check against this process's supported formats. │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {} A Bounds object representing the bounds of all the │ │ │ │ │ - * currently loaded tiles (including those partially or not at all seen │ │ │ │ │ - * onscreen). │ │ │ │ │ + * {String} A supported mime type. │ │ │ │ │ */ │ │ │ │ │ - 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); │ │ │ │ │ + findMimeType: function(sourceFormats, targetFormats) { │ │ │ │ │ + targetFormats = targetFormats || this.formats; │ │ │ │ │ + for (var f in sourceFormats) { │ │ │ │ │ + if (f in targetFormats) { │ │ │ │ │ + return f; │ │ │ │ │ + } │ │ │ │ │ } │ │ │ │ │ - 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); │ │ │ │ │ - } │ │ │ │ │ + CLASS_NAME: "OpenLayers.WPSProcess" │ │ │ │ │ │ │ │ │ │ - //remove all but our single tile │ │ │ │ │ - this.removeExcessTiles(1, 1); │ │ │ │ │ +}); │ │ │ │ │ │ │ │ │ │ - // store the resolution of the grid │ │ │ │ │ - this.gridResolution = this.getServerResolution(); │ │ │ │ │ - }, │ │ │ │ │ +/** │ │ │ │ │ + * Class: OpenLayers.WPSProcess.ChainLink │ │ │ │ │ + * Type for chaining processes. │ │ │ │ │ + */ │ │ │ │ │ +OpenLayers.WPSProcess.ChainLink = OpenLayers.Class({ │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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 │ │ │ │ │ + /** │ │ │ │ │ + * Property: process │ │ │ │ │ + * {} The process to chain │ │ │ │ │ */ │ │ │ │ │ - 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 │ │ │ │ │ - }; │ │ │ │ │ - │ │ │ │ │ - }, │ │ │ │ │ + process: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * Property: output │ │ │ │ │ + * {String} The output identifier of the output we are going to use as │ │ │ │ │ + * input for another process. │ │ │ │ │ */ │ │ │ │ │ - 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; │ │ │ │ │ - }, │ │ │ │ │ + output: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: getTileBoundsForGridIndex │ │ │ │ │ + * Constructor: OpenLayers.WPSProcess.ChainLink │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * row - {Number} The row of the grid │ │ │ │ │ - * col - {Number} The column of the grid │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {} The bounds for the tile at (row, col) │ │ │ │ │ + * options - {Object} Properties to set on the instance. │ │ │ │ │ */ │ │ │ │ │ - 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 │ │ │ │ │ - ); │ │ │ │ │ + initialize: function(options) { │ │ │ │ │ + OpenLayers.Util.extend(this, options); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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); │ │ │ │ │ + CLASS_NAME: "OpenLayers.WPSProcess.ChainLink" │ │ │ │ │ │ │ │ │ │ - rowidx += 1; │ │ │ │ │ - } while ((tileBounds.bottom >= bounds.bottom - tilelat * this.buffer) || │ │ │ │ │ - rowidx < minRows); │ │ │ │ │ +}); │ │ │ │ │ +/* ====================================================================== │ │ │ │ │ + OpenLayers/StyleMap.js │ │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ │ │ - //shave off exceess rows and colums │ │ │ │ │ - this.removeExcessTiles(rowidx, colidx); │ │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ + * full list of 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 resolution = this.getServerResolution(); │ │ │ │ │ - // store the resolution of the grid │ │ │ │ │ - this.gridResolution = resolution; │ │ │ │ │ +/** │ │ │ │ │ + * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ │ + * @requires OpenLayers/Style.js │ │ │ │ │ + * @requires OpenLayers/Feature/Vector.js │ │ │ │ │ + */ │ │ │ │ │ │ │ │ │ │ - //now actually draw the tiles │ │ │ │ │ - tileData.sort(function(a, b) { │ │ │ │ │ - return a.distance - b.distance; │ │ │ │ │ - }); │ │ │ │ │ - for (var i = 0, ii = tileData.length; i < ii; ++i) { │ │ │ │ │ - tileData[i].tile.draw(); │ │ │ │ │ - } │ │ │ │ │ - }, │ │ │ │ │ +/** │ │ │ │ │ + * Class: OpenLayers.StyleMap │ │ │ │ │ + */ │ │ │ │ │ +OpenLayers.StyleMap = OpenLayers.Class({ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: getMaxExtent │ │ │ │ │ - * Get this layer's maximum extent. (Implemented as a getter for │ │ │ │ │ - * potential specific implementations in sub-classes.) │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {} │ │ │ │ │ + * Property: styles │ │ │ │ │ + * {Object} Hash of {}, keyed by names of well known │ │ │ │ │ + * rendering intents (e.g. "default", "temporary", "select", "delete"). │ │ │ │ │ */ │ │ │ │ │ - getMaxExtent: function() { │ │ │ │ │ - return this.maxExtent; │ │ │ │ │ - }, │ │ │ │ │ + styles: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: addTile │ │ │ │ │ - * Create a tile, initialize it, and add it to the layer div. │ │ │ │ │ - * │ │ │ │ │ - * Parameters │ │ │ │ │ - * bounds - {} │ │ │ │ │ - * position - {} │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {} The added OpenLayers.Tile │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - 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; │ │ │ │ │ - }, │ │ │ │ │ + extendDefault: true, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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. │ │ │ │ │ + /** │ │ │ │ │ + * Constructor: OpenLayers.StyleMap │ │ │ │ │ * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * tile - {} │ │ │ │ │ + * 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 │ │ │ │ │ */ │ │ │ │ │ - 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); │ │ │ │ │ - } │ │ │ │ │ + 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"]) │ │ │ │ │ }; │ │ │ │ │ │ │ │ │ │ - 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 │ │ │ │ │ - ); │ │ │ │ │ - } │ │ │ │ │ + // 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; │ │ │ │ │ } │ │ │ │ │ - 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; │ │ │ │ │ } │ │ │ │ │ } │ │ │ │ │ + OpenLayers.Util.extend(this, options); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: shiftRow │ │ │ │ │ - * Shifty grid work │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * prepend - {Boolean} if true, prepend to beginning. │ │ │ │ │ - * if false, then append to end │ │ │ │ │ - * tileSize - {Object} rendered tile size; object with w and h properties │ │ │ │ │ - */ │ │ │ │ │ - shiftRow: function(prepend, tileSize) { │ │ │ │ │ - var grid = this.grid; │ │ │ │ │ - var rowIndex = prepend ? 0 : (grid.length - 1); │ │ │ │ │ - var sign = prepend ? -1 : 1; │ │ │ │ │ - var rowSign = this.rowSign; │ │ │ │ │ - var tileLayout = this.gridLayout; │ │ │ │ │ - tileLayout.startrow += sign * rowSign; │ │ │ │ │ - │ │ │ │ │ - var modelRow = grid[rowIndex]; │ │ │ │ │ - var row = grid[prepend ? 'pop' : 'shift'](); │ │ │ │ │ - for (var i = 0, len = row.length; i < len; i++) { │ │ │ │ │ - var tile = row[i]; │ │ │ │ │ - var position = modelRow[i].position.clone(); │ │ │ │ │ - position.y += tileSize.h * sign; │ │ │ │ │ - tile.moveTo(this.getTileBoundsForGridIndex(rowIndex, i), position); │ │ │ │ │ - } │ │ │ │ │ - grid[prepend ? 'unshift' : 'push'](row); │ │ │ │ │ - }, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Method: shiftColumn │ │ │ │ │ - * Shift grid work in the other dimension │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * prepend - {Boolean} if true, prepend to beginning. │ │ │ │ │ - * if false, then append to end │ │ │ │ │ - * tileSize - {Object} rendered tile size; object with w and h properties │ │ │ │ │ + * Method: destroy │ │ │ │ │ */ │ │ │ │ │ - shiftColumn: function(prepend, tileSize) { │ │ │ │ │ - var grid = this.grid; │ │ │ │ │ - var colIndex = prepend ? 0 : (grid[0].length - 1); │ │ │ │ │ - var sign = prepend ? -1 : 1; │ │ │ │ │ - var tileLayout = this.gridLayout; │ │ │ │ │ - tileLayout.startcol += sign; │ │ │ │ │ - │ │ │ │ │ - for (var i = 0, len = grid.length; i < len; i++) { │ │ │ │ │ - var row = grid[i]; │ │ │ │ │ - var position = row[colIndex].position.clone(); │ │ │ │ │ - var tile = row[prepend ? 'pop' : 'shift'](); │ │ │ │ │ - position.x += tileSize.w * sign; │ │ │ │ │ - tile.moveTo(this.getTileBoundsForGridIndex(i, colIndex), position); │ │ │ │ │ - row[prepend ? 'unshift' : 'push'](tile); │ │ │ │ │ + destroy: function() { │ │ │ │ │ + for (var key in this.styles) { │ │ │ │ │ + this.styles[key].destroy(); │ │ │ │ │ } │ │ │ │ │ + this.styles = null; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: removeExcessTiles │ │ │ │ │ - * When the size of the map or the buffer changes, we may need to │ │ │ │ │ - * remove some excess rows and columns. │ │ │ │ │ + * Method: createSymbolizer │ │ │ │ │ + * Creates the symbolizer for a feature for a render intent. │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * rows - {Integer} Maximum number of rows we want our grid to have. │ │ │ │ │ - * columns - {Integer} Maximum number of columns we want our grid to have. │ │ │ │ │ + * 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 │ │ │ │ │ */ │ │ │ │ │ - removeExcessTiles: function(rows, columns) { │ │ │ │ │ - var i, l; │ │ │ │ │ - │ │ │ │ │ - // remove extra rows │ │ │ │ │ - while (this.grid.length > rows) { │ │ │ │ │ - var row = this.grid.pop(); │ │ │ │ │ - for (i = 0, l = row.length; i < l; i++) { │ │ │ │ │ - var tile = row[i]; │ │ │ │ │ - this.destroyTile(tile); │ │ │ │ │ - } │ │ │ │ │ + createSymbolizer: function(feature, intent) { │ │ │ │ │ + if (!feature) { │ │ │ │ │ + feature = new OpenLayers.Feature.Vector(); │ │ │ │ │ } │ │ │ │ │ - │ │ │ │ │ - // remove extra columns │ │ │ │ │ - for (i = 0, l = this.grid.length; i < l; i++) { │ │ │ │ │ - while (this.grid[i].length > columns) { │ │ │ │ │ - var row = this.grid[i]; │ │ │ │ │ - var tile = row.pop(); │ │ │ │ │ - this.destroyTile(tile); │ │ │ │ │ - } │ │ │ │ │ + if (!this.styles[intent]) { │ │ │ │ │ + intent = "default"; │ │ │ │ │ } │ │ │ │ │ - }, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * 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(); │ │ │ │ │ + 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)); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: getTileBounds │ │ │ │ │ - * Returns The tile bounds for a layer given a pixel location. │ │ │ │ │ - * │ │ │ │ │ + * 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: │ │ │ │ │ - * viewPortPx - {} The location in the viewport. │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {} Bounds of the tile at the given pixel location. │ │ │ │ │ + * 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 │ │ │ │ │ */ │ │ │ │ │ - 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); │ │ │ │ │ + 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.Layer.Grid" │ │ │ │ │ + CLASS_NAME: "OpenLayers.StyleMap" │ │ │ │ │ }); │ │ │ │ │ /* ====================================================================== │ │ │ │ │ - OpenLayers/TileManager.js │ │ │ │ │ + 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/Util.js │ │ │ │ │ - * @requires OpenLayers/BaseTypes.js │ │ │ │ │ - * @requires OpenLayers/BaseTypes/Element.js │ │ │ │ │ - * @requires OpenLayers/Layer/Grid.js │ │ │ │ │ - * @requires OpenLayers/Tile/Image.js │ │ │ │ │ + * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ │ */ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Class: OpenLayers.TileManager │ │ │ │ │ - * Provides queueing of image requests and caching of image elements. │ │ │ │ │ - * │ │ │ │ │ - * Queueing avoids unnecessary image requests while changing zoom levels │ │ │ │ │ - * quickly, and helps improve dragging performance on mobile devices that show │ │ │ │ │ - * a lag in dragging when loading of new images starts. and │ │ │ │ │ - * are the configuration options to control this behavior. │ │ │ │ │ - * │ │ │ │ │ - * Caching avoids setting the src on image elements for images that have already │ │ │ │ │ - * been used. Several maps can share a TileManager instance, in which case each │ │ │ │ │ - * map gets its own tile queue, but all maps share the same tile cache. │ │ │ │ │ + * Class: OpenLayers.Strategy │ │ │ │ │ + * Abstract vector layer strategy class. Not to be instantiated directly. Use │ │ │ │ │ + * one of the strategy subclasses instead. │ │ │ │ │ */ │ │ │ │ │ -OpenLayers.TileManager = OpenLayers.Class({ │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * APIProperty: cacheSize │ │ │ │ │ - * {Number} Number of image elements to keep referenced in this instance's │ │ │ │ │ - * cache for fast reuse. Default is 256. │ │ │ │ │ - */ │ │ │ │ │ - cacheSize: 256, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * APIProperty: tilesPerFrame │ │ │ │ │ - * {Number} Number of queued tiles to load per frame (see ). │ │ │ │ │ - * Default is 2. │ │ │ │ │ - */ │ │ │ │ │ - tilesPerFrame: 2, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * APIProperty: frameDelay │ │ │ │ │ - * {Number} Delay between tile loading frames (see ) in │ │ │ │ │ - * milliseconds. Default is 16. │ │ │ │ │ - */ │ │ │ │ │ - frameDelay: 16, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * APIProperty: moveDelay │ │ │ │ │ - * {Number} Delay in milliseconds after a map's move event before loading │ │ │ │ │ - * tiles. Default is 100. │ │ │ │ │ - */ │ │ │ │ │ - moveDelay: 100, │ │ │ │ │ +OpenLayers.Strategy = OpenLayers.Class({ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: zoomDelay │ │ │ │ │ - * {Number} Delay in milliseconds after a map's zoomend event before loading │ │ │ │ │ - * tiles. Default is 200. │ │ │ │ │ + * Property: layer │ │ │ │ │ + * {} The layer this strategy belongs to. │ │ │ │ │ */ │ │ │ │ │ - zoomDelay: 200, │ │ │ │ │ + layer: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: maps │ │ │ │ │ - * {Array()} The maps to manage tiles on. │ │ │ │ │ + * Property: options │ │ │ │ │ + * {Object} Any options sent to the constructor. │ │ │ │ │ */ │ │ │ │ │ - maps: null, │ │ │ │ │ + options: null, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: tileQueueId │ │ │ │ │ - * {Object} The ids of the loop, keyed by map id. │ │ │ │ │ + /** │ │ │ │ │ + * Property: active │ │ │ │ │ + * {Boolean} The control is active. │ │ │ │ │ */ │ │ │ │ │ - tileQueueId: null, │ │ │ │ │ + active: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: tileQueue │ │ │ │ │ - * {Object(Array())} Tiles queued for drawing, keyed by │ │ │ │ │ - * map id. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - tileQueue: null, │ │ │ │ │ + autoActivate: true, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: tileCache │ │ │ │ │ - * {Object} Cached image elements, keyed by URL. │ │ │ │ │ + * Property: autoDestroy │ │ │ │ │ + * {Boolean} The creator of the strategy can set autoDestroy to false │ │ │ │ │ + * to fully control when the strategy is destroyed. Defaults to │ │ │ │ │ + * true. │ │ │ │ │ */ │ │ │ │ │ - tileCache: null, │ │ │ │ │ + autoDestroy: true, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: tileCacheIndex │ │ │ │ │ - * {Array(String)} URLs of cached tiles. First entry is the least recently │ │ │ │ │ - * used. │ │ │ │ │ - */ │ │ │ │ │ - tileCacheIndex: null, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Constructor: OpenLayers.TileManager │ │ │ │ │ - * Constructor for a new instance. │ │ │ │ │ - * │ │ │ │ │ + * Constructor: OpenLayers.Strategy │ │ │ │ │ + * Abstract class for vector strategies. Create instances of a subclass. │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * options - {Object} Configuration for this instance. │ │ │ │ │ + * options - {Object} Optional object whose properties will be set on the │ │ │ │ │ + * instance. │ │ │ │ │ */ │ │ │ │ │ initialize: function(options) { │ │ │ │ │ OpenLayers.Util.extend(this, options); │ │ │ │ │ - this.maps = []; │ │ │ │ │ - this.tileQueueId = {}; │ │ │ │ │ - this.tileQueue = {}; │ │ │ │ │ - this.tileCache = {}; │ │ │ │ │ - this.tileCacheIndex = []; │ │ │ │ │ + this.options = options; │ │ │ │ │ + // set the active property here, so that user cannot override it │ │ │ │ │ + this.active = false; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: addMap │ │ │ │ │ - * Binds this instance to a map │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * map - {} │ │ │ │ │ + * APIMethod: destroy │ │ │ │ │ + * Clean up the strategy. │ │ │ │ │ */ │ │ │ │ │ - addMap: function(map) { │ │ │ │ │ - if (this._destroyed || !OpenLayers.Layer.Grid) { │ │ │ │ │ - return; │ │ │ │ │ - } │ │ │ │ │ - this.maps.push(map); │ │ │ │ │ - this.tileQueue[map.id] = []; │ │ │ │ │ - for (var i = 0, ii = map.layers.length; i < ii; ++i) { │ │ │ │ │ - this.addLayer({ │ │ │ │ │ - layer: map.layers[i] │ │ │ │ │ - }); │ │ │ │ │ - } │ │ │ │ │ - map.events.on({ │ │ │ │ │ - move: this.move, │ │ │ │ │ - zoomend: this.zoomEnd, │ │ │ │ │ - changelayer: this.changeLayer, │ │ │ │ │ - addlayer: this.addLayer, │ │ │ │ │ - preremovelayer: this.removeLayer, │ │ │ │ │ - scope: this │ │ │ │ │ - }); │ │ │ │ │ + destroy: function() { │ │ │ │ │ + this.deactivate(); │ │ │ │ │ + this.layer = null; │ │ │ │ │ + this.options = null; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: removeMap │ │ │ │ │ - * Unbinds this instance from a map │ │ │ │ │ + * Method: setLayer │ │ │ │ │ + * Called to set the property. │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * map - {} │ │ │ │ │ + * layer - {} │ │ │ │ │ */ │ │ │ │ │ - removeMap: function(map) { │ │ │ │ │ - if (this._destroyed || !OpenLayers.Layer.Grid) { │ │ │ │ │ - return; │ │ │ │ │ - } │ │ │ │ │ - window.clearTimeout(this.tileQueueId[map.id]); │ │ │ │ │ - if (map.layers) { │ │ │ │ │ - for (var i = 0, ii = map.layers.length; i < ii; ++i) { │ │ │ │ │ - this.removeLayer({ │ │ │ │ │ - layer: map.layers[i] │ │ │ │ │ - }); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - if (map.events) { │ │ │ │ │ - map.events.un({ │ │ │ │ │ - move: this.move, │ │ │ │ │ - zoomend: this.zoomEnd, │ │ │ │ │ - changelayer: this.changeLayer, │ │ │ │ │ - addlayer: this.addLayer, │ │ │ │ │ - preremovelayer: this.removeLayer, │ │ │ │ │ - scope: this │ │ │ │ │ - }); │ │ │ │ │ - } │ │ │ │ │ - delete this.tileQueue[map.id]; │ │ │ │ │ - delete this.tileQueueId[map.id]; │ │ │ │ │ - OpenLayers.Util.removeItem(this.maps, map); │ │ │ │ │ + setLayer: function(layer) { │ │ │ │ │ + this.layer = layer; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: move │ │ │ │ │ - * Handles the map's move event │ │ │ │ │ + * Method: activate │ │ │ │ │ + * Activate the strategy. Register any listeners, do appropriate setup. │ │ │ │ │ * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * evt - {Object} Listener argument │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Boolean} True if the strategy was successfully activated or false if │ │ │ │ │ + * the strategy was already active. │ │ │ │ │ */ │ │ │ │ │ - move: function(evt) { │ │ │ │ │ - this.updateTimeout(evt.object, this.moveDelay, true); │ │ │ │ │ + activate: function() { │ │ │ │ │ + if (!this.active) { │ │ │ │ │ + this.active = true; │ │ │ │ │ + return true; │ │ │ │ │ + } │ │ │ │ │ + return false; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: zoomEnd │ │ │ │ │ - * Handles the map's zoomEnd event │ │ │ │ │ + * Method: deactivate │ │ │ │ │ + * Deactivate the strategy. Unregister any listeners, do appropriate │ │ │ │ │ + * tear-down. │ │ │ │ │ * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * evt - {Object} Listener argument │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Boolean} True if the strategy was successfully deactivated or false if │ │ │ │ │ + * the strategy was already inactive. │ │ │ │ │ */ │ │ │ │ │ - zoomEnd: function(evt) { │ │ │ │ │ - this.updateTimeout(evt.object, this.zoomDelay); │ │ │ │ │ + deactivate: function() { │ │ │ │ │ + if (this.active) { │ │ │ │ │ + this.active = false; │ │ │ │ │ + return true; │ │ │ │ │ + } │ │ │ │ │ + return false; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: changeLayer │ │ │ │ │ - * Handles the map's changeLayer event │ │ │ │ │ + CLASS_NAME: "OpenLayers.Strategy" │ │ │ │ │ +}); │ │ │ │ │ +/* ====================================================================== │ │ │ │ │ + OpenLayers/Marker.js │ │ │ │ │ + ====================================================================== */ │ │ │ │ │ + │ │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ + * full text of the license. */ │ │ │ │ │ + │ │ │ │ │ + │ │ │ │ │ +/** │ │ │ │ │ + * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ │ + * @requires OpenLayers/Events.js │ │ │ │ │ + * @requires OpenLayers/Icon.js │ │ │ │ │ + */ │ │ │ │ │ + │ │ │ │ │ +/** │ │ │ │ │ + * Class: OpenLayers.Marker │ │ │ │ │ + * Instances of OpenLayers.Marker are a combination of a │ │ │ │ │ + * and an . │ │ │ │ │ + * │ │ │ │ │ + * Markers are generally added to a special layer called │ │ │ │ │ + * . │ │ │ │ │ + * │ │ │ │ │ + * Example: │ │ │ │ │ + * (code) │ │ │ │ │ + * var markers = new OpenLayers.Layer.Markers( "Markers" ); │ │ │ │ │ + * map.addLayer(markers); │ │ │ │ │ + * │ │ │ │ │ + * var size = new OpenLayers.Size(21,25); │ │ │ │ │ + * var offset = new OpenLayers.Pixel(-(size.w/2), -size.h); │ │ │ │ │ + * var icon = new OpenLayers.Icon('http://www.openlayers.org/dev/img/marker.png', size, offset); │ │ │ │ │ + * markers.addMarker(new OpenLayers.Marker(new OpenLayers.LonLat(0,0),icon)); │ │ │ │ │ + * markers.addMarker(new OpenLayers.Marker(new OpenLayers.LonLat(0,0),icon.clone())); │ │ │ │ │ + * │ │ │ │ │ + * (end) │ │ │ │ │ + * │ │ │ │ │ + * Note that if you pass an icon into the Marker constructor, it will take │ │ │ │ │ + * that icon and use it. This means that you should not share icons between │ │ │ │ │ + * markers -- you use them once, but you should clone() for any additional │ │ │ │ │ + * markers using that same icon. │ │ │ │ │ + */ │ │ │ │ │ +OpenLayers.Marker = OpenLayers.Class({ │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Property: icon │ │ │ │ │ + * {} The icon used by this marker. │ │ │ │ │ + */ │ │ │ │ │ + icon: null, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Property: lonlat │ │ │ │ │ + * {} location of object │ │ │ │ │ + */ │ │ │ │ │ + lonlat: null, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Property: events │ │ │ │ │ + * {} the event handler. │ │ │ │ │ + */ │ │ │ │ │ + events: null, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Property: map │ │ │ │ │ + * {} the map this marker is attached to │ │ │ │ │ + */ │ │ │ │ │ + map: null, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Constructor: OpenLayers.Marker │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * evt - {Object} Listener argument │ │ │ │ │ + * lonlat - {} the position of this marker │ │ │ │ │ + * icon - {} the icon for this marker │ │ │ │ │ */ │ │ │ │ │ - changeLayer: function(evt) { │ │ │ │ │ - if (evt.property === 'visibility' || evt.property === 'params') { │ │ │ │ │ - this.updateTimeout(evt.object, 0); │ │ │ │ │ + initialize: function(lonlat, icon) { │ │ │ │ │ + this.lonlat = lonlat; │ │ │ │ │ + │ │ │ │ │ + var newIcon = (icon) ? icon : OpenLayers.Marker.defaultIcon(); │ │ │ │ │ + if (this.icon == null) { │ │ │ │ │ + this.icon = newIcon; │ │ │ │ │ + } else { │ │ │ │ │ + this.icon.url = newIcon.url; │ │ │ │ │ + this.icon.size = newIcon.size; │ │ │ │ │ + this.icon.offset = newIcon.offset; │ │ │ │ │ + this.icon.calculateOffset = newIcon.calculateOffset; │ │ │ │ │ } │ │ │ │ │ + this.events = new OpenLayers.Events(this, this.icon.imageDiv); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: addLayer │ │ │ │ │ - * Handles the map's addlayer event │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * evt - {Object} The listener argument │ │ │ │ │ + * APIMethod: destroy │ │ │ │ │ + * Destroy the marker. You must first remove the marker from any │ │ │ │ │ + * layer which it has been added to, or you will get buggy behavior. │ │ │ │ │ + * (This can not be done within the marker since the marker does not │ │ │ │ │ + * know which layer it is attached to.) │ │ │ │ │ */ │ │ │ │ │ - addLayer: function(evt) { │ │ │ │ │ - var layer = evt.layer; │ │ │ │ │ - if (layer instanceof OpenLayers.Layer.Grid) { │ │ │ │ │ - layer.events.on({ │ │ │ │ │ - addtile: this.addTile, │ │ │ │ │ - retile: this.clearTileQueue, │ │ │ │ │ - scope: this │ │ │ │ │ - }); │ │ │ │ │ - var i, j, tile; │ │ │ │ │ - for (i = layer.grid.length - 1; i >= 0; --i) { │ │ │ │ │ - for (j = layer.grid[i].length - 1; j >= 0; --j) { │ │ │ │ │ - tile = layer.grid[i][j]; │ │ │ │ │ - this.addTile({ │ │ │ │ │ - tile: tile │ │ │ │ │ - }); │ │ │ │ │ - if (tile.url && !tile.imgDiv) { │ │ │ │ │ - this.manageTileCache({ │ │ │ │ │ - object: tile │ │ │ │ │ - }); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ + destroy: function() { │ │ │ │ │ + // erase any drawn features │ │ │ │ │ + this.erase(); │ │ │ │ │ + │ │ │ │ │ + this.map = null; │ │ │ │ │ + │ │ │ │ │ + this.events.destroy(); │ │ │ │ │ + this.events = null; │ │ │ │ │ + │ │ │ │ │ + if (this.icon != null) { │ │ │ │ │ + this.icon.destroy(); │ │ │ │ │ + this.icon = null; │ │ │ │ │ } │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: removeLayer │ │ │ │ │ - * Handles the map's preremovelayer event │ │ │ │ │ - * │ │ │ │ │ + /** │ │ │ │ │ + * Method: draw │ │ │ │ │ + * Calls draw on the icon, and returns that output. │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * evt - {Object} The listener argument │ │ │ │ │ + * px - {} │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {DOMElement} A new DOM Image with this marker's icon set at the │ │ │ │ │ + * location passed-in │ │ │ │ │ */ │ │ │ │ │ - removeLayer: function(evt) { │ │ │ │ │ - var layer = evt.layer; │ │ │ │ │ - if (layer instanceof OpenLayers.Layer.Grid) { │ │ │ │ │ - this.clearTileQueue({ │ │ │ │ │ - object: layer │ │ │ │ │ - }); │ │ │ │ │ - if (layer.events) { │ │ │ │ │ - layer.events.un({ │ │ │ │ │ - addtile: this.addTile, │ │ │ │ │ - retile: this.clearTileQueue, │ │ │ │ │ - scope: this │ │ │ │ │ - }); │ │ │ │ │ - } │ │ │ │ │ - if (layer.grid) { │ │ │ │ │ - var i, j, tile; │ │ │ │ │ - for (i = layer.grid.length - 1; i >= 0; --i) { │ │ │ │ │ - for (j = layer.grid[i].length - 1; j >= 0; --j) { │ │ │ │ │ - tile = layer.grid[i][j]; │ │ │ │ │ - this.unloadTile({ │ │ │ │ │ - object: tile │ │ │ │ │ - }); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ + draw: function(px) { │ │ │ │ │ + return this.icon.draw(px); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: updateTimeout │ │ │ │ │ - * Applies the or to the loop, │ │ │ │ │ - * and schedules more queue processing after if there are still │ │ │ │ │ - * tiles in the queue. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * map - {} The map to update the timeout for │ │ │ │ │ - * delay - {Number} The delay to apply │ │ │ │ │ - * nice - {Boolean} If true, the timeout function will only be created if │ │ │ │ │ - * the tilequeue is not empty. This is used by the move handler to │ │ │ │ │ - * avoid impacts on dragging performance. For other events, the tile │ │ │ │ │ - * queue may not be populated yet, so we need to set the timer │ │ │ │ │ - * regardless of the queue size. │ │ │ │ │ + /** │ │ │ │ │ + * Method: erase │ │ │ │ │ + * Erases any drawn elements for this marker. │ │ │ │ │ */ │ │ │ │ │ - updateTimeout: function(map, delay, nice) { │ │ │ │ │ - window.clearTimeout(this.tileQueueId[map.id]); │ │ │ │ │ - var tileQueue = this.tileQueue[map.id]; │ │ │ │ │ - if (!nice || tileQueue.length) { │ │ │ │ │ - this.tileQueueId[map.id] = window.setTimeout( │ │ │ │ │ - OpenLayers.Function.bind(function() { │ │ │ │ │ - this.drawTilesFromQueue(map); │ │ │ │ │ - if (tileQueue.length) { │ │ │ │ │ - this.updateTimeout(map, this.frameDelay); │ │ │ │ │ - } │ │ │ │ │ - }, this), delay │ │ │ │ │ - ); │ │ │ │ │ + erase: function() { │ │ │ │ │ + if (this.icon != null) { │ │ │ │ │ + this.icon.erase(); │ │ │ │ │ } │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: addTile │ │ │ │ │ - * Listener for the layer's addtile event │ │ │ │ │ + * Method: moveTo │ │ │ │ │ + * Move the marker to the new location. │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * evt - {Object} The listener argument │ │ │ │ │ + * px - {|Object} the pixel position to move to. │ │ │ │ │ + * An OpenLayers.Pixel or an object with a 'x' and 'y' properties. │ │ │ │ │ */ │ │ │ │ │ - addTile: function(evt) { │ │ │ │ │ - if (evt.tile instanceof OpenLayers.Tile.Image) { │ │ │ │ │ - evt.tile.events.on({ │ │ │ │ │ - beforedraw: this.queueTileDraw, │ │ │ │ │ - beforeload: this.manageTileCache, │ │ │ │ │ - loadend: this.addToCache, │ │ │ │ │ - unload: this.unloadTile, │ │ │ │ │ - scope: this │ │ │ │ │ - }); │ │ │ │ │ - } else { │ │ │ │ │ - // Layer has the wrong tile type, so don't handle it any longer │ │ │ │ │ - this.removeLayer({ │ │ │ │ │ - layer: evt.tile.layer │ │ │ │ │ - }); │ │ │ │ │ + moveTo: function(px) { │ │ │ │ │ + if ((px != null) && (this.icon != null)) { │ │ │ │ │ + this.icon.moveTo(px); │ │ │ │ │ } │ │ │ │ │ + this.lonlat = this.map.getLonLatFromLayerPx(px); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: unloadTile │ │ │ │ │ - * Listener for the tile's unload event │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * evt - {Object} The listener argument │ │ │ │ │ + * APIMethod: isDrawn │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Boolean} Whether or not the marker is drawn. │ │ │ │ │ */ │ │ │ │ │ - unloadTile: function(evt) { │ │ │ │ │ - var tile = evt.object; │ │ │ │ │ - tile.events.un({ │ │ │ │ │ - beforedraw: this.queueTileDraw, │ │ │ │ │ - beforeload: this.manageTileCache, │ │ │ │ │ - loadend: this.addToCache, │ │ │ │ │ - unload: this.unloadTile, │ │ │ │ │ - scope: this │ │ │ │ │ - }); │ │ │ │ │ - OpenLayers.Util.removeItem(this.tileQueue[tile.layer.map.id], tile); │ │ │ │ │ + isDrawn: function() { │ │ │ │ │ + var isDrawn = (this.icon && this.icon.isDrawn()); │ │ │ │ │ + return isDrawn; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: queueTileDraw │ │ │ │ │ - * Adds a tile to the queue that will draw it. │ │ │ │ │ + * Method: onScreen │ │ │ │ │ * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * evt - {Object} Listener argument of the tile's beforedraw event │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Boolean} Whether or not the marker is currently visible on screen. │ │ │ │ │ */ │ │ │ │ │ - queueTileDraw: function(evt) { │ │ │ │ │ - var tile = evt.object; │ │ │ │ │ - var queued = false; │ │ │ │ │ - var layer = tile.layer; │ │ │ │ │ - var url = layer.getURL(tile.bounds); │ │ │ │ │ - var img = this.tileCache[url]; │ │ │ │ │ - if (img && img.className !== 'olTileImage') { │ │ │ │ │ - // cached image no longer valid, e.g. because we're olTileReplacing │ │ │ │ │ - delete this.tileCache[url]; │ │ │ │ │ - OpenLayers.Util.removeItem(this.tileCacheIndex, url); │ │ │ │ │ - img = null; │ │ │ │ │ - } │ │ │ │ │ - // queue only if image with same url not cached already │ │ │ │ │ - if (layer.url && (layer.async || !img)) { │ │ │ │ │ - // add to queue only if not in queue already │ │ │ │ │ - var tileQueue = this.tileQueue[layer.map.id]; │ │ │ │ │ - if (!~OpenLayers.Util.indexOf(tileQueue, tile)) { │ │ │ │ │ - tileQueue.push(tile); │ │ │ │ │ - } │ │ │ │ │ - queued = true; │ │ │ │ │ - } │ │ │ │ │ - return !queued; │ │ │ │ │ - }, │ │ │ │ │ + onScreen: function() { │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: drawTilesFromQueue │ │ │ │ │ - * Draws tiles from the tileQueue, and unqueues the tiles │ │ │ │ │ - */ │ │ │ │ │ - drawTilesFromQueue: function(map) { │ │ │ │ │ - var tileQueue = this.tileQueue[map.id]; │ │ │ │ │ - var limit = this.tilesPerFrame; │ │ │ │ │ - var animating = map.zoomTween && map.zoomTween.playing; │ │ │ │ │ - while (!animating && tileQueue.length && limit) { │ │ │ │ │ - tileQueue.shift().draw(true); │ │ │ │ │ - --limit; │ │ │ │ │ + var onScreen = false; │ │ │ │ │ + if (this.map) { │ │ │ │ │ + var screenBounds = this.map.getExtent(); │ │ │ │ │ + onScreen = screenBounds.containsLonLat(this.lonlat); │ │ │ │ │ } │ │ │ │ │ + return onScreen; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: manageTileCache │ │ │ │ │ - * Adds, updates, removes and fetches cache entries. │ │ │ │ │ + * Method: inflate │ │ │ │ │ + * Englarges the markers icon by the specified ratio. │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * evt - {Object} Listener argument of the tile's beforeload event │ │ │ │ │ + * inflate - {float} the ratio to enlarge the marker by (passing 2 │ │ │ │ │ + * will double the size). │ │ │ │ │ */ │ │ │ │ │ - manageTileCache: function(evt) { │ │ │ │ │ - var tile = evt.object; │ │ │ │ │ - var img = this.tileCache[tile.url]; │ │ │ │ │ - if (img) { │ │ │ │ │ - // if image is on its layer's backbuffer, remove it from backbuffer │ │ │ │ │ - if (img.parentNode && │ │ │ │ │ - OpenLayers.Element.hasClass(img.parentNode, 'olBackBuffer')) { │ │ │ │ │ - img.parentNode.removeChild(img); │ │ │ │ │ - img.id = null; │ │ │ │ │ - } │ │ │ │ │ - // only use image from cache if it is not on a layer already │ │ │ │ │ - if (!img.parentNode) { │ │ │ │ │ - img.style.visibility = 'hidden'; │ │ │ │ │ - img.style.opacity = 0; │ │ │ │ │ - tile.setImage(img); │ │ │ │ │ - // LRU - move tile to the end of the array to mark it as the most │ │ │ │ │ - // recently used │ │ │ │ │ - OpenLayers.Util.removeItem(this.tileCacheIndex, tile.url); │ │ │ │ │ - this.tileCacheIndex.push(tile.url); │ │ │ │ │ - } │ │ │ │ │ + inflate: function(inflate) { │ │ │ │ │ + if (this.icon) { │ │ │ │ │ + this.icon.setSize({ │ │ │ │ │ + w: this.icon.size.w * inflate, │ │ │ │ │ + h: this.icon.size.h * inflate │ │ │ │ │ + }); │ │ │ │ │ } │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: addToCache │ │ │ │ │ - * │ │ │ │ │ + /** │ │ │ │ │ + * Method: setOpacity │ │ │ │ │ + * Change the opacity of the marker by changin the opacity of │ │ │ │ │ + * its icon │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * evt - {Object} Listener argument for the tile's loadend event │ │ │ │ │ + * opacity - {float} Specified as fraction (0.4, etc) │ │ │ │ │ */ │ │ │ │ │ - addToCache: function(evt) { │ │ │ │ │ - var tile = evt.object; │ │ │ │ │ - if (!this.tileCache[tile.url]) { │ │ │ │ │ - if (!OpenLayers.Element.hasClass(tile.imgDiv, 'olImageLoadError')) { │ │ │ │ │ - if (this.tileCacheIndex.length >= this.cacheSize) { │ │ │ │ │ - delete this.tileCache[this.tileCacheIndex[0]]; │ │ │ │ │ - this.tileCacheIndex.shift(); │ │ │ │ │ - } │ │ │ │ │ - this.tileCache[tile.url] = tile.imgDiv; │ │ │ │ │ - this.tileCacheIndex.push(tile.url); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ + setOpacity: function(opacity) { │ │ │ │ │ + this.icon.setOpacity(opacity); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: clearTileQueue │ │ │ │ │ - * Clears the tile queue from tiles of a specific layer │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * evt - {Object} Listener argument of the layer's retile event │ │ │ │ │ + * Method: setUrl │ │ │ │ │ + * Change URL of the Icon Image. │ │ │ │ │ + * │ │ │ │ │ + * url - {String} │ │ │ │ │ */ │ │ │ │ │ - clearTileQueue: function(evt) { │ │ │ │ │ - var layer = evt.object; │ │ │ │ │ - var tileQueue = this.tileQueue[layer.map.id]; │ │ │ │ │ - for (var i = tileQueue.length - 1; i >= 0; --i) { │ │ │ │ │ - if (tileQueue[i].layer === layer) { │ │ │ │ │ - tileQueue.splice(i, 1); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ + setUrl: function(url) { │ │ │ │ │ + this.icon.setUrl(url); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: destroy │ │ │ │ │ + /** │ │ │ │ │ + * Method: display │ │ │ │ │ + * Hide or show the icon │ │ │ │ │ + * │ │ │ │ │ + * display - {Boolean} │ │ │ │ │ */ │ │ │ │ │ - destroy: function() { │ │ │ │ │ - for (var i = this.maps.length - 1; i >= 0; --i) { │ │ │ │ │ - this.removeMap(this.maps[i]); │ │ │ │ │ - } │ │ │ │ │ - this.maps = null; │ │ │ │ │ - this.tileQueue = null; │ │ │ │ │ - this.tileQueueId = null; │ │ │ │ │ - this.tileCache = null; │ │ │ │ │ - this.tileCacheIndex = null; │ │ │ │ │ - this._destroyed = true; │ │ │ │ │ - } │ │ │ │ │ + display: function(display) { │ │ │ │ │ + this.icon.display(display); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ + CLASS_NAME: "OpenLayers.Marker" │ │ │ │ │ }); │ │ │ │ │ + │ │ │ │ │ + │ │ │ │ │ +/** │ │ │ │ │ + * Function: defaultIcon │ │ │ │ │ + * Creates a default . │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {} A default OpenLayers.Icon to use for a marker │ │ │ │ │ + */ │ │ │ │ │ +OpenLayers.Marker.defaultIcon = function() { │ │ │ │ │ + return new OpenLayers.Icon(OpenLayers.Util.getImageLocation("marker.png"), { │ │ │ │ │ + w: 21, │ │ │ │ │ + h: 25 │ │ │ │ │ + }, { │ │ │ │ │ + x: -10.5, │ │ │ │ │ + y: -25 │ │ │ │ │ + }); │ │ │ │ │ +}; │ │ │ │ │ + │ │ │ │ │ + │ │ │ │ │ /* ====================================================================== │ │ │ │ │ - OpenLayers/Symbolizer/Point.js │ │ │ │ │ + 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/Symbolizer.js │ │ │ │ │ + * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ │ */ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Class: OpenLayers.Symbolizer.Point │ │ │ │ │ - * A symbolizer used to render point features. │ │ │ │ │ + * 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.Symbolizer.Point = OpenLayers.Class(OpenLayers.Symbolizer, { │ │ │ │ │ +OpenLayers.Renderer = OpenLayers.Class({ │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Property: container │ │ │ │ │ + * {DOMElement} │ │ │ │ │ + */ │ │ │ │ │ + container: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * Property: root │ │ │ │ │ + * {DOMElement} │ │ │ │ │ + */ │ │ │ │ │ + root: null, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Property: extent │ │ │ │ │ + * {} │ │ │ │ │ */ │ │ │ │ │ + extent: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: strokeOpacity │ │ │ │ │ - * {Number} Stroke opacity (0-1). │ │ │ │ │ - * │ │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ + * 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, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: strokeWidth │ │ │ │ │ - * {Number} Pixel stroke width. │ │ │ │ │ - * │ │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ + * Property: resolution │ │ │ │ │ + * {Float} cache of current map resolution │ │ │ │ │ */ │ │ │ │ │ + resolution: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: strokeLinecap │ │ │ │ │ - * {String} Stroke cap type ("butt", "round", or "square"). │ │ │ │ │ - * │ │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ + * Property: map │ │ │ │ │ + * {} Reference to the map -- this is set in Vector's setMap() │ │ │ │ │ */ │ │ │ │ │ + map: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * Property: featureDx │ │ │ │ │ + * {Number} Feature offset in x direction. Will be calculated for and │ │ │ │ │ + * applied to the current feature while rendering (see │ │ │ │ │ + * ). │ │ │ │ │ */ │ │ │ │ │ + featureDx: 0, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: fillColor │ │ │ │ │ - * {String} RGB hex fill color (e.g. "#ff0000" for red). │ │ │ │ │ + * 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 │ │ │ │ │ * │ │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Boolean} Whether or not the browser supports the renderer class │ │ │ │ │ */ │ │ │ │ │ + supported: function() { │ │ │ │ │ + return false; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: fillOpacity │ │ │ │ │ - * {Number} Fill opacity (0-1). │ │ │ │ │ + * 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. │ │ │ │ │ * │ │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ + * 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; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: pointRadius │ │ │ │ │ - * {Number} Pixel point radius. │ │ │ │ │ + * 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 - {} │ │ │ │ │ * │ │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ + * 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; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: externalGraphic │ │ │ │ │ - * {String} Url to an external graphic that will be used for rendering │ │ │ │ │ - * points. │ │ │ │ │ + * 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 │ │ │ │ │ * │ │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ + * 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) {}, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: graphicWidth │ │ │ │ │ - * {Number} Pixel width for sizing an external graphic. │ │ │ │ │ + * Method: drawText │ │ │ │ │ + * Function for drawing text labels. │ │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ │ * │ │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ + * Parameters: │ │ │ │ │ + * featureId - {String} │ │ │ │ │ + * style - │ │ │ │ │ + * location - {} │ │ │ │ │ */ │ │ │ │ │ + drawText: function(featureId, style, location) {}, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: graphicHeight │ │ │ │ │ - * {Number} Pixel height for sizing an external graphic. │ │ │ │ │ + * Method: removeText │ │ │ │ │ + * Function for removing text labels. │ │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ │ * │ │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ + * Parameters: │ │ │ │ │ + * featureId - {String} │ │ │ │ │ */ │ │ │ │ │ + removeText: function(featureId) {}, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: graphicOpacity │ │ │ │ │ - * {Number} Opacity (0-1) for an external graphic. │ │ │ │ │ + * 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. │ │ │ │ │ * │ │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ + * Parameters: │ │ │ │ │ + * evt - {} │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {String} A feature id or undefined. │ │ │ │ │ */ │ │ │ │ │ + getFeatureIdFromEvent: function(evt) {}, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: graphicXOffset │ │ │ │ │ - * {Number} Pixel offset along the positive x axis for displacing an │ │ │ │ │ - * external graphic. │ │ │ │ │ + * Method: eraseFeatures │ │ │ │ │ + * This is called by the layer to erase features │ │ │ │ │ * │ │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ + * Parameters: │ │ │ │ │ + * features - {Array()} │ │ │ │ │ */ │ │ │ │ │ + eraseFeatures: function(features) { │ │ │ │ │ + if (!(OpenLayers.Util.isArray(features))) { │ │ │ │ │ + features = [features]; │ │ │ │ │ + } │ │ │ │ │ + for (var i = 0, len = features.length; i < len; ++i) { │ │ │ │ │ + var feature = features[i]; │ │ │ │ │ + this.eraseGeometry(feature.geometry, feature.id); │ │ │ │ │ + this.removeText(feature.id); │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: graphicYOffset │ │ │ │ │ - * {Number} Pixel offset along the positive y axis for displacing an │ │ │ │ │ - * external graphic. │ │ │ │ │ + * Method: eraseGeometry │ │ │ │ │ + * Remove a geometry from the renderer (by id). │ │ │ │ │ + * virtual function. │ │ │ │ │ * │ │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ + * Parameters: │ │ │ │ │ + * geometry - {} │ │ │ │ │ + * featureId - {String} │ │ │ │ │ */ │ │ │ │ │ + eraseGeometry: function(geometry, featureId) {}, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: rotation │ │ │ │ │ - * {Number} The rotation of a graphic in the clockwise direction about its │ │ │ │ │ - * center point (or any point off center as specified by │ │ │ │ │ - * and ). │ │ │ │ │ + * 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. │ │ │ │ │ * │ │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ + * Parameters: │ │ │ │ │ + * renderer - {} target renderer for the moved root │ │ │ │ │ */ │ │ │ │ │ + moveRoot: function(renderer) {}, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: graphicName │ │ │ │ │ - * {String} Named graphic to use when rendering points. Supported values │ │ │ │ │ - * include "circle", "square", "star", "x", "cross", and "triangle". │ │ │ │ │ + * 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. │ │ │ │ │ * │ │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ + * Returns: │ │ │ │ │ + * {String} the id of the output layer. │ │ │ │ │ */ │ │ │ │ │ + getRenderLayerId: function() { │ │ │ │ │ + return this.container.id; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Constructor: OpenLayers.Symbolizer.Point │ │ │ │ │ - * Create a symbolizer for rendering points. │ │ │ │ │ - * │ │ │ │ │ + * Method: applyDefaultSymbolizer │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * config - {Object} An object containing properties to be set on the │ │ │ │ │ - * symbolizer. Any documented symbolizer property can be set at │ │ │ │ │ - * construction. │ │ │ │ │ - * │ │ │ │ │ + * symbolizer - {Object} │ │ │ │ │ + * │ │ │ │ │ * Returns: │ │ │ │ │ - * A new point symbolizer. │ │ │ │ │ + * {Object} │ │ │ │ │ */ │ │ │ │ │ - initialize: function(config) { │ │ │ │ │ - OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments); │ │ │ │ │ + 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.Symbolizer.Point" │ │ │ │ │ - │ │ │ │ │ + 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/Symbolizer/Line.js │ │ │ │ │ + OpenLayers/Format/WPSDescribeProcess.js │ │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * @requires OpenLayers/Symbolizer.js │ │ │ │ │ + * @requires OpenLayers/Format/XML.js │ │ │ │ │ + * @requires OpenLayers/Format/OWSCommon/v1_1_0.js │ │ │ │ │ */ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Class: OpenLayers.Symbolizer.Line │ │ │ │ │ - * A symbolizer used to render line features. │ │ │ │ │ + * Class: OpenLayers.Format.WPSDescribeProcess │ │ │ │ │ + * Read WPS DescribeProcess responses. │ │ │ │ │ + * │ │ │ │ │ + * Inherits from: │ │ │ │ │ + * - │ │ │ │ │ */ │ │ │ │ │ -OpenLayers.Symbolizer.Line = OpenLayers.Class(OpenLayers.Symbolizer, { │ │ │ │ │ +OpenLayers.Format.WPSDescribeProcess = OpenLayers.Class( │ │ │ │ │ + OpenLayers.Format.XML, { │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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. │ │ │ │ │ - */ │ │ │ │ │ + /** │ │ │ │ │ + * Constant: VERSION │ │ │ │ │ + * {String} 1.0.0 │ │ │ │ │ + */ │ │ │ │ │ + VERSION: "1.0.0", │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIProperty: strokeOpacity │ │ │ │ │ - * {Number} Stroke opacity (0-1). │ │ │ │ │ - * │ │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ - */ │ │ │ │ │ + /** │ │ │ │ │ + * Property: namespaces │ │ │ │ │ + * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ │ + */ │ │ │ │ │ + namespaces: { │ │ │ │ │ + wps: "http://www.opengis.net/wps/1.0.0", │ │ │ │ │ + ows: "http://www.opengis.net/ows/1.1", │ │ │ │ │ + xsi: "http://www.w3.org/2001/XMLSchema-instance" │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIProperty: strokeWidth │ │ │ │ │ - * {Number} Pixel stroke width. │ │ │ │ │ - * │ │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ - */ │ │ │ │ │ + /** │ │ │ │ │ + * Property: schemaLocation │ │ │ │ │ + * {String} Schema location │ │ │ │ │ + */ │ │ │ │ │ + schemaLocation: "http://www.opengis.net/wps/1.0.0 http://schemas.opengis.net/wps/1.0.0/wpsAll.xsd", │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIProperty: strokeLinecap │ │ │ │ │ - * {String} Stroke cap type ("butt", "round", or "square"). │ │ │ │ │ - * │ │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ - */ │ │ │ │ │ + /** │ │ │ │ │ + * Property: defaultPrefix │ │ │ │ │ + */ │ │ │ │ │ + defaultPrefix: "wps", │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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. │ │ │ │ │ - */ │ │ │ │ │ + /** │ │ │ │ │ + * Property: regExes │ │ │ │ │ + * Compiled regular expressions for manipulating strings. │ │ │ │ │ + */ │ │ │ │ │ + regExes: { │ │ │ │ │ + trimSpace: (/^\s*|\s*$/g), │ │ │ │ │ + removeSpace: (/\s*/g), │ │ │ │ │ + splitSpace: (/\s+/), │ │ │ │ │ + trimComma: (/\s*,\s*/g) │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Constructor: OpenLayers.Symbolizer.Line │ │ │ │ │ - * Create a symbolizer for rendering lines. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * config - {Object} An object containing properties to be set on the │ │ │ │ │ - * symbolizer. Any documented symbolizer property can be set at │ │ │ │ │ - * construction. │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * A new line symbolizer. │ │ │ │ │ - */ │ │ │ │ │ - initialize: function(config) { │ │ │ │ │ - OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments); │ │ │ │ │ - }, │ │ │ │ │ + /** │ │ │ │ │ + * Constructor: OpenLayers.Format.WPSDescribeProcess │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ │ + * this instance. │ │ │ │ │ + */ │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Symbolizer.Line" │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: read │ │ │ │ │ + * Parse a WPS DescribeProcess and return an object with its information. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * data - {String} or {DOMElement} data to read/parse. │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Object} │ │ │ │ │ + */ │ │ │ │ │ + read: function(data) { │ │ │ │ │ + if (typeof data == "string") { │ │ │ │ │ + data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); │ │ │ │ │ + } │ │ │ │ │ + if (data && data.nodeType == 9) { │ │ │ │ │ + data = data.documentElement; │ │ │ │ │ + } │ │ │ │ │ + var info = {}; │ │ │ │ │ + this.readNode(data, info); │ │ │ │ │ + return info; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ -}); │ │ │ │ │ + /** │ │ │ │ │ + * Property: readers │ │ │ │ │ + * Contains public functions, grouped by namespace prefix, that will │ │ │ │ │ + * be applied when a namespaced node is found matching the function │ │ │ │ │ + * name. The function will be applied in the scope of this parser │ │ │ │ │ + * with two arguments: the node being read and a context object passed │ │ │ │ │ + * from the parent. │ │ │ │ │ + */ │ │ │ │ │ + readers: { │ │ │ │ │ + "wps": { │ │ │ │ │ + "ProcessDescriptions": function(node, obj) { │ │ │ │ │ + obj.processDescriptions = {}; │ │ │ │ │ + this.readChildNodes(node, obj.processDescriptions); │ │ │ │ │ + }, │ │ │ │ │ + "ProcessDescription": function(node, processDescriptions) { │ │ │ │ │ + var processVersion = this.getAttributeNS(node, this.namespaces.wps, "processVersion"); │ │ │ │ │ + var processDescription = { │ │ │ │ │ + processVersion: processVersion, │ │ │ │ │ + statusSupported: (node.getAttribute("statusSupported") === "true"), │ │ │ │ │ + storeSupported: (node.getAttribute("storeSupported") === "true") │ │ │ │ │ + }; │ │ │ │ │ + this.readChildNodes(node, processDescription); │ │ │ │ │ + processDescriptions[processDescription.identifier] = processDescription; │ │ │ │ │ + }, │ │ │ │ │ + "DataInputs": function(node, processDescription) { │ │ │ │ │ + processDescription.dataInputs = []; │ │ │ │ │ + this.readChildNodes(node, processDescription.dataInputs); │ │ │ │ │ + }, │ │ │ │ │ + "ProcessOutputs": function(node, processDescription) { │ │ │ │ │ + processDescription.processOutputs = []; │ │ │ │ │ + this.readChildNodes(node, processDescription.processOutputs); │ │ │ │ │ + }, │ │ │ │ │ + "Output": function(node, processOutputs) { │ │ │ │ │ + var output = {}; │ │ │ │ │ + this.readChildNodes(node, output); │ │ │ │ │ + processOutputs.push(output); │ │ │ │ │ + }, │ │ │ │ │ + "ComplexOutput": function(node, output) { │ │ │ │ │ + output.complexOutput = {}; │ │ │ │ │ + this.readChildNodes(node, output.complexOutput); │ │ │ │ │ + }, │ │ │ │ │ + "LiteralOutput": function(node, output) { │ │ │ │ │ + output.literalOutput = {}; │ │ │ │ │ + this.readChildNodes(node, output.literalOutput); │ │ │ │ │ + }, │ │ │ │ │ + "Input": function(node, dataInputs) { │ │ │ │ │ + var input = { │ │ │ │ │ + maxOccurs: parseInt(node.getAttribute("maxOccurs")), │ │ │ │ │ + minOccurs: parseInt(node.getAttribute("minOccurs")) │ │ │ │ │ + }; │ │ │ │ │ + this.readChildNodes(node, input); │ │ │ │ │ + dataInputs.push(input); │ │ │ │ │ + }, │ │ │ │ │ + "BoundingBoxData": function(node, input) { │ │ │ │ │ + input.boundingBoxData = {}; │ │ │ │ │ + this.readChildNodes(node, input.boundingBoxData); │ │ │ │ │ + }, │ │ │ │ │ + "CRS": function(node, obj) { │ │ │ │ │ + if (!obj.CRSs) { │ │ │ │ │ + obj.CRSs = {}; │ │ │ │ │ + } │ │ │ │ │ + obj.CRSs[this.getChildValue(node)] = true; │ │ │ │ │ + }, │ │ │ │ │ + "LiteralData": function(node, input) { │ │ │ │ │ + input.literalData = {}; │ │ │ │ │ + this.readChildNodes(node, input.literalData); │ │ │ │ │ + }, │ │ │ │ │ + "ComplexData": function(node, input) { │ │ │ │ │ + input.complexData = {}; │ │ │ │ │ + this.readChildNodes(node, input.complexData); │ │ │ │ │ + }, │ │ │ │ │ + "Default": function(node, complexData) { │ │ │ │ │ + complexData["default"] = {}; │ │ │ │ │ + this.readChildNodes(node, complexData["default"]); │ │ │ │ │ + }, │ │ │ │ │ + "Supported": function(node, complexData) { │ │ │ │ │ + complexData["supported"] = {}; │ │ │ │ │ + this.readChildNodes(node, complexData["supported"]); │ │ │ │ │ + }, │ │ │ │ │ + "Format": function(node, obj) { │ │ │ │ │ + var format = {}; │ │ │ │ │ + this.readChildNodes(node, format); │ │ │ │ │ + if (!obj.formats) { │ │ │ │ │ + obj.formats = {}; │ │ │ │ │ + } │ │ │ │ │ + obj.formats[format.mimeType] = true; │ │ │ │ │ + }, │ │ │ │ │ + "MimeType": function(node, format) { │ │ │ │ │ + format.mimeType = this.getChildValue(node); │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ + "ows": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers["ows"] │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ + CLASS_NAME: "OpenLayers.Format.WPSDescribeProcess" │ │ │ │ │ + │ │ │ │ │ + }); │ │ │ │ │ /* ====================================================================== │ │ │ │ │ - OpenLayers/Symbolizer/Polygon.js │ │ │ │ │ + OpenLayers/WPSClient.js │ │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * @requires OpenLayers/Symbolizer.js │ │ │ │ │ + * @requires OpenLayers/SingleFile.js │ │ │ │ │ */ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Class: OpenLayers.Symbolizer.Polygon │ │ │ │ │ - * A symbolizer used to render line features. │ │ │ │ │ + * @requires OpenLayers/Events.js │ │ │ │ │ + * @requires OpenLayers/WPSProcess.js │ │ │ │ │ + * @requires OpenLayers/Format/WPSDescribeProcess.js │ │ │ │ │ + * @requires OpenLayers/Request.js │ │ │ │ │ */ │ │ │ │ │ -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. │ │ │ │ │ - */ │ │ │ │ │ +/** │ │ │ │ │ + * Class: OpenLayers.WPSClient │ │ │ │ │ + * High level API for interaction with Web Processing Services (WPS). │ │ │ │ │ + * An instance is used to create │ │ │ │ │ + * instances for servers known to the WPSClient. The WPSClient also caches │ │ │ │ │ + * DescribeProcess responses to reduce the number of requests sent to servers │ │ │ │ │ + * when processes are created. │ │ │ │ │ + */ │ │ │ │ │ +OpenLayers.WPSClient = OpenLayers.Class({ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: strokeOpacity │ │ │ │ │ - * {Number} Stroke opacity (0-1). │ │ │ │ │ - * │ │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ + * Property: servers │ │ │ │ │ + * {Object} Service metadata, keyed by a local identifier. │ │ │ │ │ + * │ │ │ │ │ + * Properties: │ │ │ │ │ + * url - {String} the url of the server │ │ │ │ │ + * version - {String} WPS version of the server │ │ │ │ │ + * processDescription - {Object} Cache of raw DescribeProcess │ │ │ │ │ + * responses, keyed by process identifier. │ │ │ │ │ */ │ │ │ │ │ + servers: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: strokeWidth │ │ │ │ │ - * {Number} Pixel stroke width. │ │ │ │ │ - * │ │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ + * Property: version │ │ │ │ │ + * {String} The default WPS version to use if none is configured. Default │ │ │ │ │ + * is '1.0.0'. │ │ │ │ │ */ │ │ │ │ │ + version: '1.0.0', │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: strokeLinecap │ │ │ │ │ - * {String} Stroke cap type ("butt", "round", or "square"). │ │ │ │ │ - * │ │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ + * Property: lazy │ │ │ │ │ + * {Boolean} Should the DescribeProcess be deferred until a process is │ │ │ │ │ + * fully configured? Default is false. │ │ │ │ │ */ │ │ │ │ │ + lazy: false, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * Property: events │ │ │ │ │ + * {} │ │ │ │ │ + * │ │ │ │ │ + * Supported event types: │ │ │ │ │ + * describeprocess - Fires when the process description is available. │ │ │ │ │ + * Listeners receive an object with a 'raw' property holding the raw │ │ │ │ │ + * DescribeProcess response, and an 'identifier' property holding the │ │ │ │ │ + * process identifier of the described process. │ │ │ │ │ */ │ │ │ │ │ + events: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: fillColor │ │ │ │ │ - * {String} RGB hex fill color (e.g. "#ff0000" for red). │ │ │ │ │ - * │ │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ + * Constructor: OpenLayers.WPSClient │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * options - {Object} Object whose properties will be set on the instance. │ │ │ │ │ + * │ │ │ │ │ + * Avaliable options: │ │ │ │ │ + * servers - {Object} Mandatory. Service metadata, keyed by a local │ │ │ │ │ + * identifier. Can either be a string with the service url or an │ │ │ │ │ + * object literal with additional metadata: │ │ │ │ │ + * │ │ │ │ │ + * (code) │ │ │ │ │ + * servers: { │ │ │ │ │ + * local: '/geoserver/wps' │ │ │ │ │ + * }, { │ │ │ │ │ + * opengeo: { │ │ │ │ │ + * url: 'http://demo.opengeo.org/geoserver/wps', │ │ │ │ │ + * version: '1.0.0' │ │ │ │ │ + * } │ │ │ │ │ + * } │ │ │ │ │ + * (end) │ │ │ │ │ + * │ │ │ │ │ + * lazy - {Boolean} Optional. Set to true if DescribeProcess should not be │ │ │ │ │ + * requested until a process is fully configured. Default is false. │ │ │ │ │ */ │ │ │ │ │ + initialize: function(options) { │ │ │ │ │ + OpenLayers.Util.extend(this, options); │ │ │ │ │ + this.events = new OpenLayers.Events(this); │ │ │ │ │ + this.servers = {}; │ │ │ │ │ + for (var s in options.servers) { │ │ │ │ │ + this.servers[s] = typeof options.servers[s] == 'string' ? { │ │ │ │ │ + url: options.servers[s], │ │ │ │ │ + version: this.version, │ │ │ │ │ + processDescription: {} │ │ │ │ │ + } : options.servers[s]; │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: fillOpacity │ │ │ │ │ - * {Number} Fill opacity (0-1). │ │ │ │ │ - * │ │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ + * APIMethod: execute │ │ │ │ │ + * Shortcut to execute a process with a single function call. This is │ │ │ │ │ + * equivalent to using and then calling execute on the │ │ │ │ │ + * process. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * options - {Object} Options for the execute operation. │ │ │ │ │ + * │ │ │ │ │ + * Available options: │ │ │ │ │ + * server - {String} Mandatory. One of the local identifiers of the │ │ │ │ │ + * configured servers. │ │ │ │ │ + * process - {String} Mandatory. A process identifier known to the │ │ │ │ │ + * server. │ │ │ │ │ + * inputs - {Object} The inputs for the process, keyed by input identifier. │ │ │ │ │ + * For spatial data inputs, the value of an input is usually an │ │ │ │ │ + * , an or an array of │ │ │ │ │ + * geometries or features. │ │ │ │ │ + * output - {String} The identifier of an output to parse. Optional. If not │ │ │ │ │ + * provided, the first output will be parsed. │ │ │ │ │ + * success - {Function} Callback to call when the process is complete. │ │ │ │ │ + * This function is called with an outputs object as argument, which │ │ │ │ │ + * will have a property with the identifier of the requested output │ │ │ │ │ + * (e.g. 'result'). For processes that generate spatial output, the │ │ │ │ │ + * value will either be a single or an │ │ │ │ │ + * array of features. │ │ │ │ │ + * scope - {Object} Optional scope for the success callback. │ │ │ │ │ */ │ │ │ │ │ + execute: function(options) { │ │ │ │ │ + var process = this.getProcess(options.server, options.process); │ │ │ │ │ + process.execute({ │ │ │ │ │ + inputs: options.inputs, │ │ │ │ │ + success: options.success, │ │ │ │ │ + scope: options.scope │ │ │ │ │ + }); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Constructor: OpenLayers.Symbolizer.Polygon │ │ │ │ │ - * Create a symbolizer for rendering polygons. │ │ │ │ │ + * APIMethod: getProcess │ │ │ │ │ + * Creates an . │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * config - {Object} An object containing properties to be set on the │ │ │ │ │ - * symbolizer. Any documented symbolizer property can be set at │ │ │ │ │ - * construction. │ │ │ │ │ + * serverID - {String} Local identifier from the servers that this instance │ │ │ │ │ + * was constructed with. │ │ │ │ │ + * processID - {String} Process identifier known to the server. │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * A new polygon symbolizer. │ │ │ │ │ + * {} │ │ │ │ │ */ │ │ │ │ │ - initialize: function(config) { │ │ │ │ │ - OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments); │ │ │ │ │ + getProcess: function(serverID, processID) { │ │ │ │ │ + var process = new OpenLayers.WPSProcess({ │ │ │ │ │ + client: this, │ │ │ │ │ + server: serverID, │ │ │ │ │ + identifier: processID │ │ │ │ │ + }); │ │ │ │ │ + if (!this.lazy) { │ │ │ │ │ + process.describe(); │ │ │ │ │ + } │ │ │ │ │ + return process; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Symbolizer.Polygon" │ │ │ │ │ + /** │ │ │ │ │ + * Method: describeProcess │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * serverID - {String} Identifier of the server │ │ │ │ │ + * processID - {String} Identifier of the requested process │ │ │ │ │ + * callback - {Function} Callback to call when the description is available │ │ │ │ │ + * scope - {Object} Optional execution scope for the callback function │ │ │ │ │ + */ │ │ │ │ │ + describeProcess: function(serverID, processID, callback, scope) { │ │ │ │ │ + var server = this.servers[serverID]; │ │ │ │ │ + if (!server.processDescription[processID]) { │ │ │ │ │ + if (!(processID in server.processDescription)) { │ │ │ │ │ + // set to null so we know a describeFeature request is pending │ │ │ │ │ + server.processDescription[processID] = null; │ │ │ │ │ + OpenLayers.Request.GET({ │ │ │ │ │ + url: server.url, │ │ │ │ │ + params: { │ │ │ │ │ + SERVICE: 'WPS', │ │ │ │ │ + VERSION: server.version, │ │ │ │ │ + REQUEST: 'DescribeProcess', │ │ │ │ │ + IDENTIFIER: processID │ │ │ │ │ + }, │ │ │ │ │ + success: function(response) { │ │ │ │ │ + server.processDescription[processID] = response.responseText; │ │ │ │ │ + this.events.triggerEvent('describeprocess', { │ │ │ │ │ + identifier: processID, │ │ │ │ │ + raw: response.responseText │ │ │ │ │ + }); │ │ │ │ │ + }, │ │ │ │ │ + scope: this │ │ │ │ │ + }); │ │ │ │ │ + } else { │ │ │ │ │ + // pending request │ │ │ │ │ + this.events.register('describeprocess', this, function describe(evt) { │ │ │ │ │ + if (evt.identifier === processID) { │ │ │ │ │ + this.events.unregister('describeprocess', this, describe); │ │ │ │ │ + callback.call(scope, evt.raw); │ │ │ │ │ + } │ │ │ │ │ + }); │ │ │ │ │ + } │ │ │ │ │ + } else { │ │ │ │ │ + window.setTimeout(function() { │ │ │ │ │ + callback.call(scope, server.processDescription[processID]); │ │ │ │ │ + }, 0); │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ -}); │ │ │ │ │ + /** │ │ │ │ │ + * Method: destroy │ │ │ │ │ + */ │ │ │ │ │ + destroy: function() { │ │ │ │ │ + this.events.destroy(); │ │ │ │ │ + this.events = null; │ │ │ │ │ + this.servers = null; │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + CLASS_NAME: 'OpenLayers.WPSClient' │ │ │ │ │ │ │ │ │ │ +}); │ │ │ │ │ /* ====================================================================== │ │ │ │ │ - OpenLayers/Symbolizer/Text.js │ │ │ │ │ + OpenLayers/Popup.js │ │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * @requires OpenLayers/Symbolizer.js │ │ │ │ │ + * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ │ */ │ │ │ │ │ │ │ │ │ │ + │ │ │ │ │ /** │ │ │ │ │ - * Class: OpenLayers.Symbolizer.Text │ │ │ │ │ - * A symbolizer used to render text labels for features. │ │ │ │ │ + * Class: OpenLayers.Popup │ │ │ │ │ + * A popup is a small div that can opened and closed on the map. │ │ │ │ │ + * Typically opened in response to clicking on a marker. │ │ │ │ │ + * See . Popup's don't require their own │ │ │ │ │ + * layer and are added the the map using the │ │ │ │ │ + * method. │ │ │ │ │ + * │ │ │ │ │ + * Example: │ │ │ │ │ + * (code) │ │ │ │ │ + * popup = new OpenLayers.Popup("chicken", │ │ │ │ │ + * new OpenLayers.LonLat(5,40), │ │ │ │ │ + * new OpenLayers.Size(200,200), │ │ │ │ │ + * "example popup", │ │ │ │ │ + * true); │ │ │ │ │ + * │ │ │ │ │ + * map.addPopup(popup); │ │ │ │ │ + * (end) │ │ │ │ │ */ │ │ │ │ │ -OpenLayers.Symbolizer.Text = OpenLayers.Class(OpenLayers.Symbolizer, { │ │ │ │ │ +OpenLayers.Popup = OpenLayers.Class({ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: label │ │ │ │ │ - * {String} The text for the label. │ │ │ │ │ - * │ │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ + * Property: events │ │ │ │ │ + * {} custom event manager │ │ │ │ │ + */ │ │ │ │ │ + events: null, │ │ │ │ │ + │ │ │ │ │ + /** Property: id │ │ │ │ │ + * {String} the unique identifier assigned to this popup. │ │ │ │ │ */ │ │ │ │ │ + id: "", │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: fontFamily │ │ │ │ │ - * {String} The font family for the label. │ │ │ │ │ - * │ │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ + * Property: lonlat │ │ │ │ │ + * {} the position of this popup on the map │ │ │ │ │ */ │ │ │ │ │ + lonlat: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: fontSize │ │ │ │ │ - * {String} The font size for the label. │ │ │ │ │ - * │ │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ + * Property: div │ │ │ │ │ + * {DOMElement} the div that contains this popup. │ │ │ │ │ */ │ │ │ │ │ + div: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: fontWeight │ │ │ │ │ - * {String} The font weight for the label. │ │ │ │ │ - * │ │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ + * Property: contentSize │ │ │ │ │ + * {} the width and height of the content. │ │ │ │ │ */ │ │ │ │ │ + contentSize: null, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: fontStyle │ │ │ │ │ - * {String} The font style for the label. │ │ │ │ │ - * │ │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ │ + /** │ │ │ │ │ + * Property: size │ │ │ │ │ + * {} the width and height of the popup. │ │ │ │ │ */ │ │ │ │ │ + size: null, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Constructor: OpenLayers.Symbolizer.Text │ │ │ │ │ - * Create a symbolizer for rendering text labels. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * config - {Object} An object containing properties to be set on the │ │ │ │ │ - * symbolizer. Any documented symbolizer property can be set at │ │ │ │ │ - * construction. │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * A new text symbolizer. │ │ │ │ │ + /** │ │ │ │ │ + * Property: contentHTML │ │ │ │ │ + * {String} An HTML string for this popup to display. │ │ │ │ │ */ │ │ │ │ │ - initialize: function(config) { │ │ │ │ │ - OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments); │ │ │ │ │ - }, │ │ │ │ │ + contentHTML: null, │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Symbolizer.Text" │ │ │ │ │ + /** │ │ │ │ │ + * Property: backgroundColor │ │ │ │ │ + * {String} the background color used by the popup. │ │ │ │ │ + */ │ │ │ │ │ + backgroundColor: "", │ │ │ │ │ │ │ │ │ │ -}); │ │ │ │ │ + /** │ │ │ │ │ + * Property: opacity │ │ │ │ │ + * {float} the opacity of this popup (between 0.0 and 1.0) │ │ │ │ │ + */ │ │ │ │ │ + opacity: "", │ │ │ │ │ │ │ │ │ │ -/* ====================================================================== │ │ │ │ │ - OpenLayers/Symbolizer/Raster.js │ │ │ │ │ - ====================================================================== */ │ │ │ │ │ + /** │ │ │ │ │ + * Property: border │ │ │ │ │ + * {String} the border size of the popup. (eg 2px) │ │ │ │ │ + */ │ │ │ │ │ + border: "", │ │ │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ - * full text of the license. */ │ │ │ │ │ + /** │ │ │ │ │ + * Property: contentDiv │ │ │ │ │ + * {DOMElement} a reference to the element that holds the content of │ │ │ │ │ + * the div. │ │ │ │ │ + */ │ │ │ │ │ + contentDiv: null, │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * @requires OpenLayers/Symbolizer.js │ │ │ │ │ - */ │ │ │ │ │ + /** │ │ │ │ │ + * Property: groupDiv │ │ │ │ │ + * {DOMElement} First and only child of 'div'. The group Div contains the │ │ │ │ │ + * 'contentDiv' and the 'closeDiv'. │ │ │ │ │ + */ │ │ │ │ │ + groupDiv: null, │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * Class: OpenLayers.Symbolizer.Raster │ │ │ │ │ - * A symbolizer used to render raster images. │ │ │ │ │ - */ │ │ │ │ │ -OpenLayers.Symbolizer.Raster = OpenLayers.Class(OpenLayers.Symbolizer, { │ │ │ │ │ + /** │ │ │ │ │ + * Property: closeDiv │ │ │ │ │ + * {DOMElement} the optional closer image │ │ │ │ │ + */ │ │ │ │ │ + closeDiv: null, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * APIProperty: autoSize │ │ │ │ │ + * {Boolean} Resize the popup to auto-fit the contents. │ │ │ │ │ + * Default is false. │ │ │ │ │ + */ │ │ │ │ │ + autoSize: false, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * APIProperty: minSize │ │ │ │ │ + * {} Minimum size allowed for the popup's contents. │ │ │ │ │ */ │ │ │ │ │ - initialize: function(config) { │ │ │ │ │ - OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments); │ │ │ │ │ - }, │ │ │ │ │ + minSize: null, │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Symbolizer.Raster" │ │ │ │ │ + /** │ │ │ │ │ + * APIProperty: maxSize │ │ │ │ │ + * {} Maximum size allowed for the popup's contents. │ │ │ │ │ + */ │ │ │ │ │ + maxSize: null, │ │ │ │ │ │ │ │ │ │ -}); │ │ │ │ │ -/* ====================================================================== │ │ │ │ │ - OpenLayers/Style2.js │ │ │ │ │ - ====================================================================== */ │ │ │ │ │ + /** │ │ │ │ │ + * Property: displayClass │ │ │ │ │ + * {String} The CSS class of the popup. │ │ │ │ │ + */ │ │ │ │ │ + displayClass: "olPopup", │ │ │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ - * full text of the license. */ │ │ │ │ │ + /** │ │ │ │ │ + * Property: contentDisplayClass │ │ │ │ │ + * {String} The CSS class of the popup content div. │ │ │ │ │ + */ │ │ │ │ │ + contentDisplayClass: "olPopupContent", │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ │ - * @requires OpenLayers/Rule.js │ │ │ │ │ - * @requires OpenLayers/Symbolizer/Point.js │ │ │ │ │ - * @requires OpenLayers/Symbolizer/Line.js │ │ │ │ │ - * @requires OpenLayers/Symbolizer/Polygon.js │ │ │ │ │ - * @requires OpenLayers/Symbolizer/Text.js │ │ │ │ │ - * @requires OpenLayers/Symbolizer/Raster.js │ │ │ │ │ - */ │ │ │ │ │ + /** │ │ │ │ │ + * Property: padding │ │ │ │ │ + * {int or } An extra opportunity to specify internal │ │ │ │ │ + * padding of the content div inside the popup. This was originally │ │ │ │ │ + * confused with the css padding as specified in style.css's │ │ │ │ │ + * 'olPopupContent' class. We would like to get rid of this altogether, │ │ │ │ │ + * except that it does come in handy for the framed and anchoredbubble │ │ │ │ │ + * popups, who need to maintain yet another barrier between their │ │ │ │ │ + * content and the outer border of the popup itself. │ │ │ │ │ + * │ │ │ │ │ + * Note that in order to not break API, we must continue to support │ │ │ │ │ + * this property being set as an integer. Really, though, we'd like to │ │ │ │ │ + * have this specified as a Bounds object so that user can specify │ │ │ │ │ + * distinct left, top, right, bottom paddings. With the 3.0 release │ │ │ │ │ + * we can make this only a bounds. │ │ │ │ │ + */ │ │ │ │ │ + padding: 0, │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * Class: OpenLayers.Style2 │ │ │ │ │ - * This class represents a collection of rules for rendering features. │ │ │ │ │ - */ │ │ │ │ │ -OpenLayers.Style2 = OpenLayers.Class({ │ │ │ │ │ + /** │ │ │ │ │ + * Property: disableFirefoxOverflowHack │ │ │ │ │ + * {Boolean} The hack for overflow in Firefox causes all elements │ │ │ │ │ + * to be re-drawn, which causes Flash elements to be │ │ │ │ │ + * re-initialized, which is troublesome. │ │ │ │ │ + * With this property the hack can be disabled. │ │ │ │ │ + */ │ │ │ │ │ + disableFirefoxOverflowHack: false, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: id │ │ │ │ │ - * {String} A unique id for this session. │ │ │ │ │ + * Method: fixPadding │ │ │ │ │ + * To be removed in 3.0, this function merely helps us to deal with the │ │ │ │ │ + * case where the user may have set an integer value for padding, │ │ │ │ │ + * instead of an object. │ │ │ │ │ */ │ │ │ │ │ - id: null, │ │ │ │ │ + fixPadding: function() { │ │ │ │ │ + if (typeof this.padding == "number") { │ │ │ │ │ + this.padding = new OpenLayers.Bounds( │ │ │ │ │ + this.padding, this.padding, this.padding, this.padding │ │ │ │ │ + ); │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: name │ │ │ │ │ - * {String} Style identifier. │ │ │ │ │ + * APIProperty: panMapIfOutOfView │ │ │ │ │ + * {Boolean} When drawn, pan map such that the entire popup is visible in │ │ │ │ │ + * the current viewport (if necessary). │ │ │ │ │ + * Default is false. │ │ │ │ │ */ │ │ │ │ │ - name: null, │ │ │ │ │ + panMapIfOutOfView: false, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: title │ │ │ │ │ - * {String} Title of this style. │ │ │ │ │ + * APIProperty: keepInMap │ │ │ │ │ + * {Boolean} If panMapIfOutOfView is false, and this property is true, │ │ │ │ │ + * contrain the popup such that it always fits in the available map │ │ │ │ │ + * space. By default, this is not set on the base class. If you are │ │ │ │ │ + * creating popups that are near map edges and not allowing pannning, │ │ │ │ │ + * and especially if you have a popup which has a │ │ │ │ │ + * fixedRelativePosition, setting this to false may be a smart thing to │ │ │ │ │ + * do. Subclasses may want to override this setting. │ │ │ │ │ + * │ │ │ │ │ + * Default is false. │ │ │ │ │ */ │ │ │ │ │ - title: null, │ │ │ │ │ + keepInMap: false, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: description │ │ │ │ │ - * {String} Description of this style. │ │ │ │ │ + * APIProperty: closeOnMove │ │ │ │ │ + * {Boolean} When map pans, close the popup. │ │ │ │ │ + * Default is false. │ │ │ │ │ */ │ │ │ │ │ - description: null, │ │ │ │ │ + closeOnMove: false, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIProperty: layerName │ │ │ │ │ - * {} Name of the layer that this style belongs to, usually │ │ │ │ │ - * according to the NamedLayer attribute of an SLD document. │ │ │ │ │ + /** │ │ │ │ │ + * Property: map │ │ │ │ │ + * {} this gets set in Map.js when the popup is added to the map │ │ │ │ │ */ │ │ │ │ │ - layerName: null, │ │ │ │ │ + map: null, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIProperty: isDefault │ │ │ │ │ - * {Boolean} │ │ │ │ │ + /** │ │ │ │ │ + * Constructor: OpenLayers.Popup │ │ │ │ │ + * Create a popup. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * id - {String} a unqiue identifier for this popup. If null is passed │ │ │ │ │ + * an identifier will be automatically generated. │ │ │ │ │ + * lonlat - {} The position on the map the popup will │ │ │ │ │ + * be shown. │ │ │ │ │ + * contentSize - {} The size of the content. │ │ │ │ │ + * contentHTML - {String} An HTML string to display inside the │ │ │ │ │ + * popup. │ │ │ │ │ + * closeBox - {Boolean} Whether to display a close box inside │ │ │ │ │ + * the popup. │ │ │ │ │ + * closeBoxCallback - {Function} Function to be called on closeBox click. │ │ │ │ │ */ │ │ │ │ │ - isDefault: false, │ │ │ │ │ + initialize: function(id, lonlat, contentSize, contentHTML, closeBox, closeBoxCallback) { │ │ │ │ │ + if (id == null) { │ │ │ │ │ + id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_"); │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + this.id = id; │ │ │ │ │ + this.lonlat = lonlat; │ │ │ │ │ + │ │ │ │ │ + this.contentSize = (contentSize != null) ? contentSize : │ │ │ │ │ + new OpenLayers.Size( │ │ │ │ │ + OpenLayers.Popup.WIDTH, │ │ │ │ │ + OpenLayers.Popup.HEIGHT); │ │ │ │ │ + if (contentHTML != null) { │ │ │ │ │ + this.contentHTML = contentHTML; │ │ │ │ │ + } │ │ │ │ │ + this.backgroundColor = OpenLayers.Popup.COLOR; │ │ │ │ │ + this.opacity = OpenLayers.Popup.OPACITY; │ │ │ │ │ + this.border = OpenLayers.Popup.BORDER; │ │ │ │ │ + │ │ │ │ │ + this.div = OpenLayers.Util.createDiv(this.id, null, null, │ │ │ │ │ + null, null, null, "hidden"); │ │ │ │ │ + this.div.className = this.displayClass; │ │ │ │ │ + │ │ │ │ │ + var groupDivId = this.id + "_GroupDiv"; │ │ │ │ │ + this.groupDiv = OpenLayers.Util.createDiv(groupDivId, null, null, │ │ │ │ │ + null, "relative", null, │ │ │ │ │ + "hidden"); │ │ │ │ │ + │ │ │ │ │ + var id = this.div.id + "_contentDiv"; │ │ │ │ │ + this.contentDiv = OpenLayers.Util.createDiv(id, null, this.contentSize.clone(), │ │ │ │ │ + null, "relative"); │ │ │ │ │ + this.contentDiv.className = this.contentDisplayClass; │ │ │ │ │ + this.groupDiv.appendChild(this.contentDiv); │ │ │ │ │ + this.div.appendChild(this.groupDiv); │ │ │ │ │ + │ │ │ │ │ + if (closeBox) { │ │ │ │ │ + this.addCloseBox(closeBoxCallback); │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + this.registerEvents(); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: rules │ │ │ │ │ - * {Array()} Collection of rendering rules. │ │ │ │ │ + * Method: destroy │ │ │ │ │ + * nullify references to prevent circular references and memory leaks │ │ │ │ │ */ │ │ │ │ │ - rules: null, │ │ │ │ │ + destroy: function() { │ │ │ │ │ + │ │ │ │ │ + this.id = null; │ │ │ │ │ + this.lonlat = null; │ │ │ │ │ + this.size = null; │ │ │ │ │ + this.contentHTML = null; │ │ │ │ │ + │ │ │ │ │ + this.backgroundColor = null; │ │ │ │ │ + this.opacity = null; │ │ │ │ │ + this.border = null; │ │ │ │ │ + │ │ │ │ │ + if (this.closeOnMove && this.map) { │ │ │ │ │ + this.map.events.unregister("movestart", this, this.hide); │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + this.events.destroy(); │ │ │ │ │ + this.events = null; │ │ │ │ │ + │ │ │ │ │ + if (this.closeDiv) { │ │ │ │ │ + OpenLayers.Event.stopObservingElement(this.closeDiv); │ │ │ │ │ + this.groupDiv.removeChild(this.closeDiv); │ │ │ │ │ + } │ │ │ │ │ + this.closeDiv = null; │ │ │ │ │ + │ │ │ │ │ + this.div.removeChild(this.groupDiv); │ │ │ │ │ + this.groupDiv = null; │ │ │ │ │ + │ │ │ │ │ + if (this.map != null) { │ │ │ │ │ + this.map.removePopup(this); │ │ │ │ │ + } │ │ │ │ │ + this.map = null; │ │ │ │ │ + this.div = null; │ │ │ │ │ + │ │ │ │ │ + this.autoSize = null; │ │ │ │ │ + this.minSize = null; │ │ │ │ │ + this.maxSize = null; │ │ │ │ │ + this.padding = null; │ │ │ │ │ + this.panMapIfOutOfView = null; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Constructor: OpenLayers.Style2 │ │ │ │ │ - * Creates a style representing a collection of rendering rules. │ │ │ │ │ + * Method: draw │ │ │ │ │ + * Constructs the elements that make up the popup. │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * config - {Object} An object containing properties to be set on the │ │ │ │ │ - * style. Any documented properties may be set at construction. │ │ │ │ │ - * │ │ │ │ │ + * px - {} the position the popup in pixels. │ │ │ │ │ + * │ │ │ │ │ * Returns: │ │ │ │ │ - * {} A new style object. │ │ │ │ │ + * {DOMElement} Reference to a div that contains the drawn popup │ │ │ │ │ */ │ │ │ │ │ - initialize: function(config) { │ │ │ │ │ - OpenLayers.Util.extend(this, config); │ │ │ │ │ - this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_"); │ │ │ │ │ + draw: function(px) { │ │ │ │ │ + if (px == null) { │ │ │ │ │ + if ((this.lonlat != null) && (this.map != null)) { │ │ │ │ │ + px = this.map.getLayerPxFromLonLat(this.lonlat); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + // this assumes that this.map already exists, which is okay because │ │ │ │ │ + // this.draw is only called once the popup has been added to the map. │ │ │ │ │ + if (this.closeOnMove) { │ │ │ │ │ + this.map.events.register("movestart", this, this.hide); │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + //listen to movestart, moveend to disable overflow (FF bug) │ │ │ │ │ + if (!this.disableFirefoxOverflowHack && OpenLayers.BROWSER_NAME == 'firefox') { │ │ │ │ │ + this.map.events.register("movestart", this, function() { │ │ │ │ │ + var style = document.defaultView.getComputedStyle( │ │ │ │ │ + this.contentDiv, null │ │ │ │ │ + ); │ │ │ │ │ + var currentOverflow = style.getPropertyValue("overflow"); │ │ │ │ │ + if (currentOverflow != "hidden") { │ │ │ │ │ + this.contentDiv._oldOverflow = currentOverflow; │ │ │ │ │ + this.contentDiv.style.overflow = "hidden"; │ │ │ │ │ + } │ │ │ │ │ + }); │ │ │ │ │ + this.map.events.register("moveend", this, function() { │ │ │ │ │ + var oldOverflow = this.contentDiv._oldOverflow; │ │ │ │ │ + if (oldOverflow) { │ │ │ │ │ + this.contentDiv.style.overflow = oldOverflow; │ │ │ │ │ + this.contentDiv._oldOverflow = null; │ │ │ │ │ + } │ │ │ │ │ + }); │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + this.moveTo(px); │ │ │ │ │ + if (!this.autoSize && !this.size) { │ │ │ │ │ + this.setSize(this.contentSize); │ │ │ │ │ + } │ │ │ │ │ + this.setBackgroundColor(); │ │ │ │ │ + this.setOpacity(); │ │ │ │ │ + this.setBorder(); │ │ │ │ │ + this.setContentHTML(); │ │ │ │ │ + │ │ │ │ │ + if (this.panMapIfOutOfView) { │ │ │ │ │ + this.panIntoView(); │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + return this.div; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: destroy │ │ │ │ │ - * nullify references to prevent circular references and memory leaks │ │ │ │ │ + * Method: updatePosition │ │ │ │ │ + * if the popup has a lonlat and its map members set, │ │ │ │ │ + * then have it move itself to its proper position │ │ │ │ │ */ │ │ │ │ │ - destroy: function() { │ │ │ │ │ - for (var i = 0, len = this.rules.length; i < len; i++) { │ │ │ │ │ - this.rules[i].destroy(); │ │ │ │ │ + updatePosition: function() { │ │ │ │ │ + if ((this.lonlat) && (this.map)) { │ │ │ │ │ + var px = this.map.getLayerPxFromLonLat(this.lonlat); │ │ │ │ │ + if (px) { │ │ │ │ │ + this.moveTo(px); │ │ │ │ │ + } │ │ │ │ │ } │ │ │ │ │ - delete this.rules; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: clone │ │ │ │ │ - * Clones this style. │ │ │ │ │ + * Method: moveTo │ │ │ │ │ * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {} Clone of this style. │ │ │ │ │ + * Parameters: │ │ │ │ │ + * px - {} the top and left position of the popup div. │ │ │ │ │ */ │ │ │ │ │ - clone: function() { │ │ │ │ │ - var config = OpenLayers.Util.extend({}, this); │ │ │ │ │ - // clone rules │ │ │ │ │ - if (this.rules) { │ │ │ │ │ - config.rules = []; │ │ │ │ │ - for (var i = 0, len = this.rules.length; i < len; ++i) { │ │ │ │ │ - config.rules.push(this.rules[i].clone()); │ │ │ │ │ - } │ │ │ │ │ + moveTo: function(px) { │ │ │ │ │ + if ((px != null) && (this.div != null)) { │ │ │ │ │ + this.div.style.left = px.x + "px"; │ │ │ │ │ + this.div.style.top = px.y + "px"; │ │ │ │ │ } │ │ │ │ │ - return new OpenLayers.Style2(config); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Style2" │ │ │ │ │ -}); │ │ │ │ │ -/* ====================================================================== │ │ │ │ │ - OpenLayers/Spherical.js │ │ │ │ │ - ====================================================================== */ │ │ │ │ │ + /** │ │ │ │ │ + * Method: visible │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Boolean} Boolean indicating whether or not the popup is visible │ │ │ │ │ + */ │ │ │ │ │ + visible: function() { │ │ │ │ │ + return OpenLayers.Element.visible(this.div); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ - * full text of the license. */ │ │ │ │ │ + /** │ │ │ │ │ + * Method: toggle │ │ │ │ │ + * Toggles visibility of the popup. │ │ │ │ │ + */ │ │ │ │ │ + toggle: function() { │ │ │ │ │ + if (this.visible()) { │ │ │ │ │ + this.hide(); │ │ │ │ │ + } else { │ │ │ │ │ + this.show(); │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * @requires OpenLayers/SingleFile.js │ │ │ │ │ - */ │ │ │ │ │ + /** │ │ │ │ │ + * Method: show │ │ │ │ │ + * Makes the popup visible. │ │ │ │ │ + */ │ │ │ │ │ + show: function() { │ │ │ │ │ + this.div.style.display = ''; │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * Namespace: Spherical │ │ │ │ │ - * The OpenLayers.Spherical namespace includes utility functions for │ │ │ │ │ - * calculations on the basis of a spherical earth (ignoring ellipsoidal │ │ │ │ │ - * effects), which is accurate enough for most purposes. │ │ │ │ │ - * │ │ │ │ │ - * Relevant links: │ │ │ │ │ - * * http://www.movable-type.co.uk/scripts/latlong.html │ │ │ │ │ - * * http://code.google.com/apis/maps/documentation/javascript/reference.html#spherical │ │ │ │ │ - */ │ │ │ │ │ + if (this.panMapIfOutOfView) { │ │ │ │ │ + this.panIntoView(); │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ -OpenLayers.Spherical = OpenLayers.Spherical || {}; │ │ │ │ │ + /** │ │ │ │ │ + * Method: hide │ │ │ │ │ + * Makes the popup invisible. │ │ │ │ │ + */ │ │ │ │ │ + hide: function() { │ │ │ │ │ + this.div.style.display = 'none'; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ -OpenLayers.Spherical.DEFAULT_RADIUS = 6378137; │ │ │ │ │ + /** │ │ │ │ │ + * Method: setSize │ │ │ │ │ + * Used to adjust the size of the popup. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * contentSize - {} the new size for the popup's │ │ │ │ │ + * contents div (in pixels). │ │ │ │ │ + */ │ │ │ │ │ + setSize: function(contentSize) { │ │ │ │ │ + this.size = contentSize.clone(); │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * APIFunction: computeDistanceBetween │ │ │ │ │ - * Computes the distance between two LonLats. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * from - {} or {Object} Starting point. A LonLat or │ │ │ │ │ - * a JavaScript literal with lon lat properties. │ │ │ │ │ - * to - {} or {Object} Ending point. A LonLat or a │ │ │ │ │ - * JavaScript literal with lon lat properties. │ │ │ │ │ - * radius - {Float} The radius. Optional. Defaults to 6378137 meters. │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Float} The distance in meters. │ │ │ │ │ - */ │ │ │ │ │ -OpenLayers.Spherical.computeDistanceBetween = function(from, to, radius) { │ │ │ │ │ - var R = radius || OpenLayers.Spherical.DEFAULT_RADIUS; │ │ │ │ │ - var sinHalfDeltaLon = Math.sin(Math.PI * (to.lon - from.lon) / 360); │ │ │ │ │ - var sinHalfDeltaLat = Math.sin(Math.PI * (to.lat - from.lat) / 360); │ │ │ │ │ - var a = sinHalfDeltaLat * sinHalfDeltaLat + │ │ │ │ │ - sinHalfDeltaLon * sinHalfDeltaLon * Math.cos(Math.PI * from.lat / 180) * Math.cos(Math.PI * to.lat / 180); │ │ │ │ │ - return 2 * R * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); │ │ │ │ │ -}; │ │ │ │ │ + // if our contentDiv has a css 'padding' set on it by a stylesheet, we │ │ │ │ │ + // must add that to the desired "size". │ │ │ │ │ + var contentDivPadding = this.getContentDivPadding(); │ │ │ │ │ + var wPadding = contentDivPadding.left + contentDivPadding.right; │ │ │ │ │ + var hPadding = contentDivPadding.top + contentDivPadding.bottom; │ │ │ │ │ │ │ │ │ │ + // take into account the popup's 'padding' property │ │ │ │ │ + this.fixPadding(); │ │ │ │ │ + wPadding += this.padding.left + this.padding.right; │ │ │ │ │ + hPadding += this.padding.top + this.padding.bottom; │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * APIFunction: computeHeading │ │ │ │ │ - * Computes the heading from one LonLat to another LonLat. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * from - {} or {Object} Starting point. A LonLat or │ │ │ │ │ - * a JavaScript literal with lon lat properties. │ │ │ │ │ - * to - {} or {Object} Ending point. A LonLat or a │ │ │ │ │ - * JavaScript literal with lon lat properties. │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Float} The heading in degrees. │ │ │ │ │ - */ │ │ │ │ │ -OpenLayers.Spherical.computeHeading = function(from, to) { │ │ │ │ │ - var y = Math.sin(Math.PI * (from.lon - to.lon) / 180) * Math.cos(Math.PI * to.lat / 180); │ │ │ │ │ - var x = Math.cos(Math.PI * from.lat / 180) * Math.sin(Math.PI * to.lat / 180) - │ │ │ │ │ - Math.sin(Math.PI * from.lat / 180) * Math.cos(Math.PI * to.lat / 180) * Math.cos(Math.PI * (from.lon - to.lon) / 180); │ │ │ │ │ - return 180 * Math.atan2(y, x) / Math.PI; │ │ │ │ │ -}; │ │ │ │ │ -/* ====================================================================== │ │ │ │ │ - OpenLayers/Strategy.js │ │ │ │ │ - ====================================================================== */ │ │ │ │ │ + // make extra space for the close div │ │ │ │ │ + if (this.closeDiv) { │ │ │ │ │ + var closeDivWidth = parseInt(this.closeDiv.style.width); │ │ │ │ │ + wPadding += closeDivWidth + contentDivPadding.right; │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ - * full text of the license. */ │ │ │ │ │ + //increase size of the main popup div to take into account the │ │ │ │ │ + // users's desired padding and close div. │ │ │ │ │ + this.size.w += wPadding; │ │ │ │ │ + this.size.h += hPadding; │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ │ - */ │ │ │ │ │ + //now if our browser is IE, we need to actually make the contents │ │ │ │ │ + // div itself bigger to take its own padding into effect. this makes │ │ │ │ │ + // me want to shoot someone, but so it goes. │ │ │ │ │ + if (OpenLayers.BROWSER_NAME == "msie") { │ │ │ │ │ + this.contentSize.w += │ │ │ │ │ + contentDivPadding.left + contentDivPadding.right; │ │ │ │ │ + this.contentSize.h += │ │ │ │ │ + contentDivPadding.bottom + contentDivPadding.top; │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * Class: OpenLayers.Strategy │ │ │ │ │ - * Abstract vector layer strategy class. Not to be instantiated directly. Use │ │ │ │ │ - * one of the strategy subclasses instead. │ │ │ │ │ - */ │ │ │ │ │ -OpenLayers.Strategy = OpenLayers.Class({ │ │ │ │ │ + if (this.div != null) { │ │ │ │ │ + this.div.style.width = this.size.w + "px"; │ │ │ │ │ + this.div.style.height = this.size.h + "px"; │ │ │ │ │ + } │ │ │ │ │ + if (this.contentDiv != null) { │ │ │ │ │ + this.contentDiv.style.width = contentSize.w + "px"; │ │ │ │ │ + this.contentDiv.style.height = contentSize.h + "px"; │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: layer │ │ │ │ │ - * {} The layer this strategy belongs to. │ │ │ │ │ + * APIMethod: updateSize │ │ │ │ │ + * Auto size the popup so that it precisely fits its contents (as │ │ │ │ │ + * determined by this.contentDiv.innerHTML). Popup size will, of │ │ │ │ │ + * course, be limited by the available space on the current map │ │ │ │ │ */ │ │ │ │ │ - layer: null, │ │ │ │ │ + updateSize: function() { │ │ │ │ │ + │ │ │ │ │ + // determine actual render dimensions of the contents by putting its │ │ │ │ │ + // contents into a fake contentDiv (for the CSS) and then measuring it │ │ │ │ │ + var preparedHTML = "
" + │ │ │ │ │ + this.contentDiv.innerHTML + │ │ │ │ │ + "
"; │ │ │ │ │ + │ │ │ │ │ + var containerElement = (this.map) ? this.map.div : document.body; │ │ │ │ │ + var realSize = OpenLayers.Util.getRenderedDimensions( │ │ │ │ │ + preparedHTML, null, { │ │ │ │ │ + displayClass: this.displayClass, │ │ │ │ │ + containerElement: containerElement │ │ │ │ │ + } │ │ │ │ │ + ); │ │ │ │ │ + │ │ │ │ │ + // is the "real" size of the div is safe to display in our map? │ │ │ │ │ + var safeSize = this.getSafeContentSize(realSize); │ │ │ │ │ + │ │ │ │ │ + var newSize = null; │ │ │ │ │ + if (safeSize.equals(realSize)) { │ │ │ │ │ + //real size of content is small enough to fit on the map, │ │ │ │ │ + // so we use real size. │ │ │ │ │ + newSize = realSize; │ │ │ │ │ + │ │ │ │ │ + } else { │ │ │ │ │ + │ │ │ │ │ + // make a new 'size' object with the clipped dimensions │ │ │ │ │ + // set or null if not clipped. │ │ │ │ │ + var fixedSize = { │ │ │ │ │ + w: (safeSize.w < realSize.w) ? safeSize.w : null, │ │ │ │ │ + h: (safeSize.h < realSize.h) ? safeSize.h : null │ │ │ │ │ + }; │ │ │ │ │ + │ │ │ │ │ + if (fixedSize.w && fixedSize.h) { │ │ │ │ │ + //content is too big in both directions, so we will use │ │ │ │ │ + // max popup size (safeSize), knowing well that it will │ │ │ │ │ + // overflow both ways. │ │ │ │ │ + newSize = safeSize; │ │ │ │ │ + } else { │ │ │ │ │ + //content is clipped in only one direction, so we need to │ │ │ │ │ + // run getRenderedDimensions() again with a fixed dimension │ │ │ │ │ + var clippedSize = OpenLayers.Util.getRenderedDimensions( │ │ │ │ │ + preparedHTML, fixedSize, { │ │ │ │ │ + displayClass: this.contentDisplayClass, │ │ │ │ │ + containerElement: containerElement │ │ │ │ │ + } │ │ │ │ │ + ); │ │ │ │ │ + │ │ │ │ │ + //if the clipped size is still the same as the safeSize, │ │ │ │ │ + // that means that our content must be fixed in the │ │ │ │ │ + // offending direction. If overflow is 'auto', this means │ │ │ │ │ + // we are going to have a scrollbar for sure, so we must │ │ │ │ │ + // adjust for that. │ │ │ │ │ + // │ │ │ │ │ + var currentOverflow = OpenLayers.Element.getStyle( │ │ │ │ │ + this.contentDiv, "overflow" │ │ │ │ │ + ); │ │ │ │ │ + if ((currentOverflow != "hidden") && │ │ │ │ │ + (clippedSize.equals(safeSize))) { │ │ │ │ │ + var scrollBar = OpenLayers.Util.getScrollbarWidth(); │ │ │ │ │ + if (fixedSize.w) { │ │ │ │ │ + clippedSize.h += scrollBar; │ │ │ │ │ + } else { │ │ │ │ │ + clippedSize.w += scrollBar; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + newSize = this.getSafeContentSize(clippedSize); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + this.setSize(newSize); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: options │ │ │ │ │ - * {Object} Any options sent to the constructor. │ │ │ │ │ + * Method: setBackgroundColor │ │ │ │ │ + * Sets the background color of the popup. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * color - {String} the background color. eg "#FFBBBB" │ │ │ │ │ */ │ │ │ │ │ - options: null, │ │ │ │ │ + setBackgroundColor: function(color) { │ │ │ │ │ + if (color != undefined) { │ │ │ │ │ + this.backgroundColor = color; │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: active │ │ │ │ │ - * {Boolean} The control is active. │ │ │ │ │ - */ │ │ │ │ │ - active: null, │ │ │ │ │ + if (this.div != null) { │ │ │ │ │ + this.div.style.backgroundColor = this.backgroundColor; │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * Method: setOpacity │ │ │ │ │ + * Sets the opacity of the popup. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * opacity - {float} A value between 0.0 (transparent) and 1.0 (solid). │ │ │ │ │ */ │ │ │ │ │ - autoActivate: true, │ │ │ │ │ + setOpacity: function(opacity) { │ │ │ │ │ + if (opacity != undefined) { │ │ │ │ │ + this.opacity = opacity; │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + if (this.div != null) { │ │ │ │ │ + // for Mozilla and Safari │ │ │ │ │ + this.div.style.opacity = this.opacity; │ │ │ │ │ + │ │ │ │ │ + // for IE │ │ │ │ │ + this.div.style.filter = 'alpha(opacity=' + this.opacity * 100 + ')'; │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: autoDestroy │ │ │ │ │ - * {Boolean} The creator of the strategy can set autoDestroy to false │ │ │ │ │ - * to fully control when the strategy is destroyed. Defaults to │ │ │ │ │ - * true. │ │ │ │ │ + * Method: setBorder │ │ │ │ │ + * Sets the border style of the popup. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * border - {String} The border style value. eg 2px │ │ │ │ │ */ │ │ │ │ │ - autoDestroy: true, │ │ │ │ │ + setBorder: function(border) { │ │ │ │ │ + if (border != undefined) { │ │ │ │ │ + this.border = border; │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + if (this.div != null) { │ │ │ │ │ + this.div.style.border = this.border; │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Constructor: OpenLayers.Strategy │ │ │ │ │ - * Abstract class for vector strategies. Create instances of a subclass. │ │ │ │ │ + * Method: setContentHTML │ │ │ │ │ + * Allows the user to set the HTML content of the popup. │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * options - {Object} Optional object whose properties will be set on the │ │ │ │ │ - * instance. │ │ │ │ │ + * contentHTML - {String} HTML for the div. │ │ │ │ │ */ │ │ │ │ │ - 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; │ │ │ │ │ + setContentHTML: function(contentHTML) { │ │ │ │ │ + │ │ │ │ │ + if (contentHTML != null) { │ │ │ │ │ + this.contentHTML = contentHTML; │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + if ((this.contentDiv != null) && │ │ │ │ │ + (this.contentHTML != null) && │ │ │ │ │ + (this.contentHTML != this.contentDiv.innerHTML)) { │ │ │ │ │ + │ │ │ │ │ + this.contentDiv.innerHTML = this.contentHTML; │ │ │ │ │ + │ │ │ │ │ + if (this.autoSize) { │ │ │ │ │ + │ │ │ │ │ + //if popup has images, listen for when they finish │ │ │ │ │ + // loading and resize accordingly │ │ │ │ │ + this.registerImageListeners(); │ │ │ │ │ + │ │ │ │ │ + //auto size the popup to its current contents │ │ │ │ │ + this.updateSize(); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: destroy │ │ │ │ │ - * Clean up the strategy. │ │ │ │ │ + * Method: registerImageListeners │ │ │ │ │ + * Called when an image contained by the popup loaded. this function │ │ │ │ │ + * updates the popup size, then unregisters the image load listener. │ │ │ │ │ */ │ │ │ │ │ - destroy: function() { │ │ │ │ │ - this.deactivate(); │ │ │ │ │ - this.layer = null; │ │ │ │ │ - this.options = null; │ │ │ │ │ + registerImageListeners: function() { │ │ │ │ │ + │ │ │ │ │ + // As the images load, this function will call updateSize() to │ │ │ │ │ + // resize the popup to fit the content div (which presumably is now │ │ │ │ │ + // bigger than when the image was not loaded). │ │ │ │ │ + // │ │ │ │ │ + // If the 'panMapIfOutOfView' property is set, we will pan the newly │ │ │ │ │ + // resized popup back into view. │ │ │ │ │ + // │ │ │ │ │ + // Note that this function, when called, will have 'popup' and │ │ │ │ │ + // 'img' properties in the context. │ │ │ │ │ + // │ │ │ │ │ + var onImgLoad = function() { │ │ │ │ │ + if (this.popup.id === null) { // this.popup has been destroyed! │ │ │ │ │ + return; │ │ │ │ │ + } │ │ │ │ │ + this.popup.updateSize(); │ │ │ │ │ + │ │ │ │ │ + if (this.popup.visible() && this.popup.panMapIfOutOfView) { │ │ │ │ │ + this.popup.panIntoView(); │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + OpenLayers.Event.stopObserving( │ │ │ │ │ + this.img, "load", this.img._onImgLoad │ │ │ │ │ + ); │ │ │ │ │ + │ │ │ │ │ + }; │ │ │ │ │ + │ │ │ │ │ + //cycle through the images and if their size is 0x0, that means that │ │ │ │ │ + // they haven't been loaded yet, so we attach the listener, which │ │ │ │ │ + // will fire when the images finish loading and will resize the │ │ │ │ │ + // popup accordingly to its new size. │ │ │ │ │ + var images = this.contentDiv.getElementsByTagName("img"); │ │ │ │ │ + for (var i = 0, len = images.length; i < len; i++) { │ │ │ │ │ + var img = images[i]; │ │ │ │ │ + if (img.width == 0 || img.height == 0) { │ │ │ │ │ + │ │ │ │ │ + var context = { │ │ │ │ │ + 'popup': this, │ │ │ │ │ + 'img': img │ │ │ │ │ + }; │ │ │ │ │ + │ │ │ │ │ + //expando this function to the image itself before registering │ │ │ │ │ + // it. This way we can easily and properly unregister it. │ │ │ │ │ + img._onImgLoad = OpenLayers.Function.bind(onImgLoad, context); │ │ │ │ │ + │ │ │ │ │ + OpenLayers.Event.observe(img, 'load', img._onImgLoad); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: setLayer │ │ │ │ │ - * Called to set the property. │ │ │ │ │ - * │ │ │ │ │ + * APIMethod: getSafeContentSize │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * layer - {} │ │ │ │ │ + * size - {} Desired size to make the popup. │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {} A size to make the popup which is neither smaller │ │ │ │ │ + * than the specified minimum size, nor bigger than the maximum │ │ │ │ │ + * size (which is calculated relative to the size of the viewport). │ │ │ │ │ */ │ │ │ │ │ - setLayer: function(layer) { │ │ │ │ │ - this.layer = layer; │ │ │ │ │ + getSafeContentSize: function(size) { │ │ │ │ │ + │ │ │ │ │ + var safeContentSize = size.clone(); │ │ │ │ │ + │ │ │ │ │ + // if our contentDiv has a css 'padding' set on it by a stylesheet, we │ │ │ │ │ + // must add that to the desired "size". │ │ │ │ │ + var contentDivPadding = this.getContentDivPadding(); │ │ │ │ │ + var wPadding = contentDivPadding.left + contentDivPadding.right; │ │ │ │ │ + var hPadding = contentDivPadding.top + contentDivPadding.bottom; │ │ │ │ │ + │ │ │ │ │ + // take into account the popup's 'padding' property │ │ │ │ │ + this.fixPadding(); │ │ │ │ │ + wPadding += this.padding.left + this.padding.right; │ │ │ │ │ + hPadding += this.padding.top + this.padding.bottom; │ │ │ │ │ + │ │ │ │ │ + if (this.closeDiv) { │ │ │ │ │ + var closeDivWidth = parseInt(this.closeDiv.style.width); │ │ │ │ │ + wPadding += closeDivWidth + contentDivPadding.right; │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + // prevent the popup from being smaller than a specified minimal size │ │ │ │ │ + if (this.minSize) { │ │ │ │ │ + safeContentSize.w = Math.max(safeContentSize.w, │ │ │ │ │ + (this.minSize.w - wPadding)); │ │ │ │ │ + safeContentSize.h = Math.max(safeContentSize.h, │ │ │ │ │ + (this.minSize.h - hPadding)); │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + // prevent the popup from being bigger than a specified maximum size │ │ │ │ │ + if (this.maxSize) { │ │ │ │ │ + safeContentSize.w = Math.min(safeContentSize.w, │ │ │ │ │ + (this.maxSize.w - wPadding)); │ │ │ │ │ + safeContentSize.h = Math.min(safeContentSize.h, │ │ │ │ │ + (this.maxSize.h - hPadding)); │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + //make sure the desired size to set doesn't result in a popup that │ │ │ │ │ + // is bigger than the map's viewport. │ │ │ │ │ + // │ │ │ │ │ + if (this.map && this.map.size) { │ │ │ │ │ + │ │ │ │ │ + var extraX = 0, │ │ │ │ │ + extraY = 0; │ │ │ │ │ + if (this.keepInMap && !this.panMapIfOutOfView) { │ │ │ │ │ + var px = this.map.getPixelFromLonLat(this.lonlat); │ │ │ │ │ + switch (this.relativePosition) { │ │ │ │ │ + case "tr": │ │ │ │ │ + extraX = px.x; │ │ │ │ │ + extraY = this.map.size.h - px.y; │ │ │ │ │ + break; │ │ │ │ │ + case "tl": │ │ │ │ │ + extraX = this.map.size.w - px.x; │ │ │ │ │ + extraY = this.map.size.h - px.y; │ │ │ │ │ + break; │ │ │ │ │ + case "bl": │ │ │ │ │ + extraX = this.map.size.w - px.x; │ │ │ │ │ + extraY = px.y; │ │ │ │ │ + break; │ │ │ │ │ + case "br": │ │ │ │ │ + extraX = px.x; │ │ │ │ │ + extraY = px.y; │ │ │ │ │ + break; │ │ │ │ │ + default: │ │ │ │ │ + extraX = px.x; │ │ │ │ │ + extraY = this.map.size.h - px.y; │ │ │ │ │ + break; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + var maxY = this.map.size.h - │ │ │ │ │ + this.map.paddingForPopups.top - │ │ │ │ │ + this.map.paddingForPopups.bottom - │ │ │ │ │ + hPadding - extraY; │ │ │ │ │ + │ │ │ │ │ + var maxX = this.map.size.w - │ │ │ │ │ + this.map.paddingForPopups.left - │ │ │ │ │ + this.map.paddingForPopups.right - │ │ │ │ │ + wPadding - extraX; │ │ │ │ │ + │ │ │ │ │ + safeContentSize.w = Math.min(safeContentSize.w, maxX); │ │ │ │ │ + safeContentSize.h = Math.min(safeContentSize.h, maxY); │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + return safeContentSize; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: activate │ │ │ │ │ - * Activate the strategy. Register any listeners, do appropriate setup. │ │ │ │ │ + * Method: getContentDivPadding │ │ │ │ │ + * Glorious, oh glorious hack in order to determine the css 'padding' of │ │ │ │ │ + * the contentDiv. IE/Opera return null here unless we actually add the │ │ │ │ │ + * popup's main 'div' element (which contains contentDiv) to the DOM. │ │ │ │ │ + * So we make it invisible and then add it to the document temporarily. │ │ │ │ │ + * │ │ │ │ │ + * Once we've taken the padding readings we need, we then remove it │ │ │ │ │ + * from the DOM (it will actually get added to the DOM in │ │ │ │ │ + * Map.js's addPopup) │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {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; │ │ │ │ │ + getContentDivPadding: function() { │ │ │ │ │ + │ │ │ │ │ + //use cached value if we have it │ │ │ │ │ + var contentDivPadding = this._contentDivPadding; │ │ │ │ │ + if (!contentDivPadding) { │ │ │ │ │ + │ │ │ │ │ + if (this.div.parentNode == null) { │ │ │ │ │ + //make the div invisible and add it to the page │ │ │ │ │ + this.div.style.display = "none"; │ │ │ │ │ + document.body.appendChild(this.div); │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + //read the padding settings from css, put them in an OL.Bounds │ │ │ │ │ + contentDivPadding = new OpenLayers.Bounds( │ │ │ │ │ + OpenLayers.Element.getStyle(this.contentDiv, "padding-left"), │ │ │ │ │ + OpenLayers.Element.getStyle(this.contentDiv, "padding-bottom"), │ │ │ │ │ + OpenLayers.Element.getStyle(this.contentDiv, "padding-right"), │ │ │ │ │ + OpenLayers.Element.getStyle(this.contentDiv, "padding-top") │ │ │ │ │ + ); │ │ │ │ │ + │ │ │ │ │ + //cache the value │ │ │ │ │ + this._contentDivPadding = contentDivPadding; │ │ │ │ │ + │ │ │ │ │ + if (this.div.parentNode == document.body) { │ │ │ │ │ + //remove the div from the page and make it visible again │ │ │ │ │ + document.body.removeChild(this.div); │ │ │ │ │ + this.div.style.display = ""; │ │ │ │ │ + } │ │ │ │ │ } │ │ │ │ │ - return false; │ │ │ │ │ + return contentDivPadding; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: deactivate │ │ │ │ │ - * Deactivate the strategy. Unregister any listeners, do appropriate │ │ │ │ │ - * tear-down. │ │ │ │ │ + * Method: addCloseBox │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * callback - {Function} The callback to be called when the close button │ │ │ │ │ + * is clicked. │ │ │ │ │ + */ │ │ │ │ │ + addCloseBox: function(callback) { │ │ │ │ │ + │ │ │ │ │ + this.closeDiv = OpenLayers.Util.createDiv( │ │ │ │ │ + this.id + "_close", null, { │ │ │ │ │ + w: 17, │ │ │ │ │ + h: 17 │ │ │ │ │ + } │ │ │ │ │ + ); │ │ │ │ │ + this.closeDiv.className = "olPopupCloseBox"; │ │ │ │ │ + │ │ │ │ │ + // use the content div's css padding to determine if we should │ │ │ │ │ + // padd the close div │ │ │ │ │ + var contentDivPadding = this.getContentDivPadding(); │ │ │ │ │ + │ │ │ │ │ + this.closeDiv.style.right = contentDivPadding.right + "px"; │ │ │ │ │ + this.closeDiv.style.top = contentDivPadding.top + "px"; │ │ │ │ │ + this.groupDiv.appendChild(this.closeDiv); │ │ │ │ │ + │ │ │ │ │ + var closePopup = callback || function(e) { │ │ │ │ │ + this.hide(); │ │ │ │ │ + OpenLayers.Event.stop(e); │ │ │ │ │ + }; │ │ │ │ │ + OpenLayers.Event.observe(this.closeDiv, "touchend", │ │ │ │ │ + OpenLayers.Function.bindAsEventListener(closePopup, this)); │ │ │ │ │ + OpenLayers.Event.observe(this.closeDiv, "click", │ │ │ │ │ + OpenLayers.Function.bindAsEventListener(closePopup, this)); │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Method: panIntoView │ │ │ │ │ + * Pans the map such that the popup is totaly viewable (if necessary) │ │ │ │ │ + */ │ │ │ │ │ + panIntoView: function() { │ │ │ │ │ + │ │ │ │ │ + var mapSize = this.map.getSize(); │ │ │ │ │ + │ │ │ │ │ + //start with the top left corner of the popup, in px, │ │ │ │ │ + // relative to the viewport │ │ │ │ │ + var origTL = this.map.getViewPortPxFromLayerPx(new OpenLayers.Pixel( │ │ │ │ │ + parseInt(this.div.style.left), │ │ │ │ │ + parseInt(this.div.style.top) │ │ │ │ │ + )); │ │ │ │ │ + var newTL = origTL.clone(); │ │ │ │ │ + │ │ │ │ │ + //new left (compare to margins, using this.size to calculate right) │ │ │ │ │ + if (origTL.x < this.map.paddingForPopups.left) { │ │ │ │ │ + newTL.x = this.map.paddingForPopups.left; │ │ │ │ │ + } else │ │ │ │ │ + if ((origTL.x + this.size.w) > (mapSize.w - this.map.paddingForPopups.right)) { │ │ │ │ │ + newTL.x = mapSize.w - this.map.paddingForPopups.right - this.size.w; │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + //new top (compare to margins, using this.size to calculate bottom) │ │ │ │ │ + if (origTL.y < this.map.paddingForPopups.top) { │ │ │ │ │ + newTL.y = this.map.paddingForPopups.top; │ │ │ │ │ + } else │ │ │ │ │ + if ((origTL.y + this.size.h) > (mapSize.h - this.map.paddingForPopups.bottom)) { │ │ │ │ │ + newTL.y = mapSize.h - this.map.paddingForPopups.bottom - this.size.h; │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + var dx = origTL.x - newTL.x; │ │ │ │ │ + var dy = origTL.y - newTL.y; │ │ │ │ │ + │ │ │ │ │ + this.map.pan(dx, dy); │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Method: registerEvents │ │ │ │ │ + * Registers events on the popup. │ │ │ │ │ * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Boolean} True if the strategy was successfully deactivated or false if │ │ │ │ │ - * the strategy was already inactive. │ │ │ │ │ + * Do this in a separate function so that subclasses can │ │ │ │ │ + * choose to override it if they wish to deal differently │ │ │ │ │ + * with mouse events │ │ │ │ │ + * │ │ │ │ │ + * Note in the following handler functions that some special │ │ │ │ │ + * care is needed to deal correctly with mousing and popups. │ │ │ │ │ + * │ │ │ │ │ + * Because the user might select the zoom-rectangle option and │ │ │ │ │ + * then drag it over a popup, we need a safe way to allow the │ │ │ │ │ + * mousemove and mouseup events to pass through the popup when │ │ │ │ │ + * they are initiated from outside. The same procedure is needed for │ │ │ │ │ + * touchmove and touchend events. │ │ │ │ │ + * │ │ │ │ │ + * Otherwise, we want to essentially kill the event propagation │ │ │ │ │ + * for all other events, though we have to do so carefully, │ │ │ │ │ + * without disabling basic html functionality, like clicking on │ │ │ │ │ + * hyperlinks or drag-selecting text. │ │ │ │ │ */ │ │ │ │ │ - deactivate: function() { │ │ │ │ │ - if (this.active) { │ │ │ │ │ - this.active = false; │ │ │ │ │ - return true; │ │ │ │ │ + registerEvents: function() { │ │ │ │ │ + this.events = new OpenLayers.Events(this, this.div, null, true); │ │ │ │ │ + │ │ │ │ │ + function onTouchstart(evt) { │ │ │ │ │ + OpenLayers.Event.stop(evt, true); │ │ │ │ │ } │ │ │ │ │ - return false; │ │ │ │ │ + this.events.on({ │ │ │ │ │ + "mousedown": this.onmousedown, │ │ │ │ │ + "mousemove": this.onmousemove, │ │ │ │ │ + "mouseup": this.onmouseup, │ │ │ │ │ + "click": this.onclick, │ │ │ │ │ + "mouseout": this.onmouseout, │ │ │ │ │ + "dblclick": this.ondblclick, │ │ │ │ │ + "touchstart": onTouchstart, │ │ │ │ │ + scope: this │ │ │ │ │ + }); │ │ │ │ │ + │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Strategy" │ │ │ │ │ + /** │ │ │ │ │ + * Method: onmousedown │ │ │ │ │ + * When mouse goes down within the popup, make a note of │ │ │ │ │ + * it locally, and then do not propagate the mousedown │ │ │ │ │ + * (but do so safely so that user can select text inside) │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * evt - {Event} │ │ │ │ │ + */ │ │ │ │ │ + onmousedown: function(evt) { │ │ │ │ │ + this.mousedown = true; │ │ │ │ │ + OpenLayers.Event.stop(evt, true); │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Method: onmousemove │ │ │ │ │ + * If the drag was started within the popup, then │ │ │ │ │ + * do not propagate the mousemove (but do so safely │ │ │ │ │ + * so that user can select text inside) │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * evt - {Event} │ │ │ │ │ + */ │ │ │ │ │ + onmousemove: function(evt) { │ │ │ │ │ + if (this.mousedown) { │ │ │ │ │ + OpenLayers.Event.stop(evt, true); │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Method: onmouseup │ │ │ │ │ + * When mouse comes up within the popup, after going down │ │ │ │ │ + * in it, reset the flag, and then (once again) do not │ │ │ │ │ + * propagate the event, but do so safely so that user can │ │ │ │ │ + * select text inside │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * evt - {Event} │ │ │ │ │ + */ │ │ │ │ │ + onmouseup: function(evt) { │ │ │ │ │ + if (this.mousedown) { │ │ │ │ │ + this.mousedown = false; │ │ │ │ │ + OpenLayers.Event.stop(evt, true); │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Method: onclick │ │ │ │ │ + * Ignore clicks, but allowing default browser handling │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * evt - {Event} │ │ │ │ │ + */ │ │ │ │ │ + onclick: function(evt) { │ │ │ │ │ + OpenLayers.Event.stop(evt, true); │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Method: onmouseout │ │ │ │ │ + * When mouse goes out of the popup set the flag to false so that │ │ │ │ │ + * if they let go and then drag back in, we won't be confused. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * evt - {Event} │ │ │ │ │ + */ │ │ │ │ │ + onmouseout: function(evt) { │ │ │ │ │ + this.mousedown = false; │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Method: ondblclick │ │ │ │ │ + * Ignore double-clicks, but allowing default browser handling │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * evt - {Event} │ │ │ │ │ + */ │ │ │ │ │ + ondblclick: function(evt) { │ │ │ │ │ + OpenLayers.Event.stop(evt, true); │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + CLASS_NAME: "OpenLayers.Popup" │ │ │ │ │ }); │ │ │ │ │ + │ │ │ │ │ +OpenLayers.Popup.WIDTH = 200; │ │ │ │ │ +OpenLayers.Popup.HEIGHT = 200; │ │ │ │ │ +OpenLayers.Popup.COLOR = "white"; │ │ │ │ │ +OpenLayers.Popup.OPACITY = 1; │ │ │ │ │ +OpenLayers.Popup.BORDER = "0px"; │ │ │ │ │ /* ====================================================================== │ │ │ │ │ OpenLayers/Protocol.js │ │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ @@ -29908,455 +32183,342 @@ │ │ │ │ │ │ │ │ │ │ CLASS_NAME: "OpenLayers.Protocol.Response" │ │ │ │ │ }); │ │ │ │ │ │ │ │ │ │ OpenLayers.Protocol.Response.SUCCESS = 1; │ │ │ │ │ OpenLayers.Protocol.Response.FAILURE = 0; │ │ │ │ │ /* ====================================================================== │ │ │ │ │ - OpenLayers/Renderer.js │ │ │ │ │ + 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.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. │ │ │ │ │ + * 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.Renderer = OpenLayers.Class({ │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Property: container │ │ │ │ │ - * {DOMElement} │ │ │ │ │ - */ │ │ │ │ │ - container: null, │ │ │ │ │ +OpenLayers.Handler = OpenLayers.Class({ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: root │ │ │ │ │ - * {DOMElement} │ │ │ │ │ + * Property: id │ │ │ │ │ + * {String} │ │ │ │ │ */ │ │ │ │ │ - root: null, │ │ │ │ │ + id: null, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: extent │ │ │ │ │ - * {} │ │ │ │ │ + /** │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - extent: null, │ │ │ │ │ + control: 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. │ │ │ │ │ + * Property: map │ │ │ │ │ + * {} │ │ │ │ │ */ │ │ │ │ │ - locked: false, │ │ │ │ │ + map: null, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: size │ │ │ │ │ - * {} │ │ │ │ │ + /** │ │ │ │ │ + * 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) │ │ │ │ │ */ │ │ │ │ │ - size: null, │ │ │ │ │ + keyMask: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: resolution │ │ │ │ │ - * {Float} cache of current map resolution │ │ │ │ │ + * Property: active │ │ │ │ │ + * {Boolean} │ │ │ │ │ */ │ │ │ │ │ - resolution: null, │ │ │ │ │ + active: false, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: map │ │ │ │ │ - * {} Reference to the map -- this is set in Vector's setMap() │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - map: null, │ │ │ │ │ + evt: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: featureDx │ │ │ │ │ - * {Number} Feature offset in x direction. Will be calculated for and │ │ │ │ │ - * applied to the current feature while rendering (see │ │ │ │ │ - * ). │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - featureDx: 0, │ │ │ │ │ + touch: false, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Constructor: OpenLayers.Renderer │ │ │ │ │ + * Constructor: OpenLayers.Handler │ │ │ │ │ + * Construct a handler. │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * containerID - {} │ │ │ │ │ - * options - {Object} options for this renderer. See sublcasses for │ │ │ │ │ - * supported options. │ │ │ │ │ + * 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(containerID, options) { │ │ │ │ │ - this.container = OpenLayers.Util.getElement(containerID); │ │ │ │ │ + initialize: function(control, callbacks, options) { │ │ │ │ │ OpenLayers.Util.extend(this, options); │ │ │ │ │ - }, │ │ │ │ │ + this.control = control; │ │ │ │ │ + this.callbacks = callbacks; │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIMethod: destroy │ │ │ │ │ - */ │ │ │ │ │ - destroy: function() { │ │ │ │ │ - this.container = null; │ │ │ │ │ - this.extent = null; │ │ │ │ │ - this.size = null; │ │ │ │ │ - this.resolution = null; │ │ │ │ │ - this.map = null; │ │ │ │ │ + var map = this.map || control.map; │ │ │ │ │ + if (map) { │ │ │ │ │ + this.setMap(map); │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_"); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: supported │ │ │ │ │ - * This should be overridden by specific subclasses │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Boolean} Whether or not the browser supports the renderer class │ │ │ │ │ + * Method: setMap │ │ │ │ │ */ │ │ │ │ │ - supported: function() { │ │ │ │ │ - return false; │ │ │ │ │ + setMap: function(map) { │ │ │ │ │ + this.map = map; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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} │ │ │ │ │ + * 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} 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. │ │ │ │ │ + * {Boolean} The keyMask matches the keys down on an event. │ │ │ │ │ */ │ │ │ │ │ - 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; │ │ │ │ │ + checkModifiers: function(evt) { │ │ │ │ │ + if (this.keyMask == null) { │ │ │ │ │ + return true; │ │ │ │ │ } │ │ │ │ │ - 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); │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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; │ │ │ │ │ + /* if it differs from the handler object's key mask, │ │ │ │ │ + bail out of the event handler */ │ │ │ │ │ + return (keyModifiers == this.keyMask); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: getResolution │ │ │ │ │ - * Uses cached copy of resolution if available to minimize computing │ │ │ │ │ + /** │ │ │ │ │ + * APIMethod: activate │ │ │ │ │ + * Turn on the handler. Returns false if the handler was already active. │ │ │ │ │ * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Float} The current map's resolution │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Boolean} The handler was activated. │ │ │ │ │ */ │ │ │ │ │ - getResolution: function() { │ │ │ │ │ - this.resolution = this.resolution || this.map.getResolution(); │ │ │ │ │ - return this.resolution; │ │ │ │ │ + activate: function() { │ │ │ │ │ + if (this.active) { │ │ │ │ │ + return false; │ │ │ │ │ + } │ │ │ │ │ + // register for event handlers defined on this class. │ │ │ │ │ + var events = OpenLayers.Events.prototype.BROWSER_EVENTS; │ │ │ │ │ + for (var i = 0, len = events.length; i < len; i++) { │ │ │ │ │ + if (this[events[i]]) { │ │ │ │ │ + this.register(events[i], this[events[i]]); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + this.active = true; │ │ │ │ │ + return true; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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 - {} │ │ │ │ │ + * APIMethod: deactivate │ │ │ │ │ + * Turn off the handler. Returns false if the handler was already inactive. │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {Boolean} true if the feature has been drawn completely, false if not, │ │ │ │ │ - * undefined if the feature had no geometry │ │ │ │ │ + * {Boolean} The handler was deactivated. │ │ │ │ │ */ │ │ │ │ │ - drawFeature: function(feature, style) { │ │ │ │ │ - if (style == null) { │ │ │ │ │ - style = feature.style; │ │ │ │ │ + deactivate: function() { │ │ │ │ │ + if (!this.active) { │ │ │ │ │ + return false; │ │ │ │ │ } │ │ │ │ │ - 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; │ │ │ │ │ + // unregister event handlers defined on this class. │ │ │ │ │ + var events = OpenLayers.Events.prototype.BROWSER_EVENTS; │ │ │ │ │ + for (var i = 0, len = events.length; i < len; i++) { │ │ │ │ │ + if (this[events[i]]) { │ │ │ │ │ + this.unregister(events[i], this[events[i]]); │ │ │ │ │ } │ │ │ │ │ } │ │ │ │ │ + this.touch = false; │ │ │ │ │ + this.active = false; │ │ │ │ │ + return true; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: 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 │ │ │ │ │ + * Method: startTouch │ │ │ │ │ + * Start touch events, this method must be called by subclasses in │ │ │ │ │ + * "touchstart" method. When touch events are started will be │ │ │ │ │ + * true and all mouse related listeners will do nothing. │ │ │ │ │ */ │ │ │ │ │ - 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; │ │ │ │ │ + startTouch: function() { │ │ │ │ │ + if (!this.touch) { │ │ │ │ │ + this.touch = true; │ │ │ │ │ + var events = [ │ │ │ │ │ + "mousedown", "mouseup", "mousemove", "click", "dblclick", │ │ │ │ │ + "mouseout" │ │ │ │ │ + ]; │ │ │ │ │ + for (var i = 0, len = events.length; i < len; i++) { │ │ │ │ │ + if (this[events[i]]) { │ │ │ │ │ + this.unregister(events[i], this[events[i]]); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ } │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: 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 - {} │ │ │ │ │ + * Method: callback │ │ │ │ │ + * Trigger the control's named callback with the given arguments │ │ │ │ │ * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {String} A feature id or undefined. │ │ │ │ │ - */ │ │ │ │ │ - getFeatureIdFromEvent: function(evt) {}, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Method: eraseFeatures │ │ │ │ │ - * This is called by the layer to erase features │ │ │ │ │ - * │ │ │ │ │ * Parameters: │ │ │ │ │ - * features - {Array()} │ │ │ │ │ + * name - {String} The key for the callback that is one of the properties │ │ │ │ │ + * of the handler's callbacks object. │ │ │ │ │ + * args - {Array(*)} An array of arguments (any type) with which to call │ │ │ │ │ + * the callback (defined by the control). │ │ │ │ │ */ │ │ │ │ │ - eraseFeatures: function(features) { │ │ │ │ │ - if (!(OpenLayers.Util.isArray(features))) { │ │ │ │ │ - features = [features]; │ │ │ │ │ - } │ │ │ │ │ - for (var i = 0, len = features.length; i < len; ++i) { │ │ │ │ │ - var feature = features[i]; │ │ │ │ │ - this.eraseGeometry(feature.geometry, feature.id); │ │ │ │ │ - this.removeText(feature.id); │ │ │ │ │ + callback: function(name, args) { │ │ │ │ │ + if (name && this.callbacks[name]) { │ │ │ │ │ + this.callbacks[name].apply(this.control, args); │ │ │ │ │ } │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: eraseGeometry │ │ │ │ │ - * Remove a geometry from the renderer (by id). │ │ │ │ │ - * virtual function. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * geometry - {} │ │ │ │ │ - * featureId - {String} │ │ │ │ │ + * Method: register │ │ │ │ │ + * register an event on the map │ │ │ │ │ */ │ │ │ │ │ - eraseGeometry: function(geometry, featureId) {}, │ │ │ │ │ + register: function(name, method) { │ │ │ │ │ + // TODO: deal with registerPriority in 3.0 │ │ │ │ │ + this.map.events.registerPriority(name, this, method); │ │ │ │ │ + this.map.events.registerPriority(name, this, this.setEvent); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: 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 │ │ │ │ │ + * Method: unregister │ │ │ │ │ + * unregister an event from the map │ │ │ │ │ */ │ │ │ │ │ - moveRoot: function(renderer) {}, │ │ │ │ │ + unregister: function(name, method) { │ │ │ │ │ + this.map.events.unregister(name, this, method); │ │ │ │ │ + this.map.events.unregister(name, this, this.setEvent); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * Method: setEvent │ │ │ │ │ + * With each registered browser event, the handler sets its own evt │ │ │ │ │ + * property. This property can be accessed by controls if needed │ │ │ │ │ + * to get more information about the event that the handler is │ │ │ │ │ + * processing. │ │ │ │ │ + * │ │ │ │ │ + * This allows modifier keys on the event to be checked (alt, shift, ctrl, │ │ │ │ │ + * and meta cannot be checked with the keyboard handler). For a │ │ │ │ │ + * control to determine which modifier keys are associated with the │ │ │ │ │ + * event that a handler is currently processing, it should access │ │ │ │ │ + * (code)handler.evt.altKey || handler.evt.shiftKey || │ │ │ │ │ + * handler.evt.ctrlKey || handler.evt.metaKey(end). │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * evt - {Event} The browser event. │ │ │ │ │ */ │ │ │ │ │ - getRenderLayerId: function() { │ │ │ │ │ - return this.container.id; │ │ │ │ │ + setEvent: function(evt) { │ │ │ │ │ + this.evt = evt; │ │ │ │ │ + return true; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: applyDefaultSymbolizer │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * symbolizer - {Object} │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Object} │ │ │ │ │ + * Method: destroy │ │ │ │ │ + * Deconstruct the handler. │ │ │ │ │ */ │ │ │ │ │ - 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; │ │ │ │ │ + destroy: function() { │ │ │ │ │ + // unregister event listeners │ │ │ │ │ + this.deactivate(); │ │ │ │ │ + // eliminate circular references │ │ │ │ │ + this.control = this.map = null; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Renderer" │ │ │ │ │ + CLASS_NAME: "OpenLayers.Handler" │ │ │ │ │ }); │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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) │ │ │ │ │ + * Constant: OpenLayers.Handler.MOD_NONE │ │ │ │ │ + * If set as the , returns false if any key is down. │ │ │ │ │ */ │ │ │ │ │ -OpenLayers.Renderer.defaultSymbolizer = { │ │ │ │ │ - fillColor: "#000000", │ │ │ │ │ - strokeColor: "#000000", │ │ │ │ │ - strokeWidth: 2, │ │ │ │ │ - fillOpacity: 1, │ │ │ │ │ - strokeOpacity: 1, │ │ │ │ │ - pointRadius: 0, │ │ │ │ │ - labelAlign: 'cm' │ │ │ │ │ -}; │ │ │ │ │ +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.Renderer.symbol │ │ │ │ │ - * Coordinate arrays for well known (named) symbols. │ │ │ │ │ + * Constant: OpenLayers.Handler.MOD_ALT │ │ │ │ │ + * If set as the , returns false if Alt is down. │ │ │ │ │ */ │ │ │ │ │ -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.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/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 │ │ │ │ │ @@ -30725,3857 +32887,4213 @@ │ │ │ │ │ OpenLayers.Control.TYPE_TOGGLE = 2; │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ * Constant: OpenLayers.Control.TYPE_TOOL │ │ │ │ │ */ │ │ │ │ │ OpenLayers.Control.TYPE_TOOL = 3; │ │ │ │ │ /* ====================================================================== │ │ │ │ │ - OpenLayers/Format/WPSDescribeProcess.js │ │ │ │ │ + OpenLayers/Spherical.js │ │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * @requires OpenLayers/Format/XML.js │ │ │ │ │ - * @requires OpenLayers/Format/OWSCommon/v1_1_0.js │ │ │ │ │ + * @requires OpenLayers/SingleFile.js │ │ │ │ │ */ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Class: OpenLayers.Format.WPSDescribeProcess │ │ │ │ │ - * Read WPS DescribeProcess responses. │ │ │ │ │ + * Namespace: Spherical │ │ │ │ │ + * The OpenLayers.Spherical namespace includes utility functions for │ │ │ │ │ + * calculations on the basis of a spherical earth (ignoring ellipsoidal │ │ │ │ │ + * effects), which is accurate enough for most purposes. │ │ │ │ │ * │ │ │ │ │ - * Inherits from: │ │ │ │ │ - * - │ │ │ │ │ + * Relevant links: │ │ │ │ │ + * * http://www.movable-type.co.uk/scripts/latlong.html │ │ │ │ │ + * * http://code.google.com/apis/maps/documentation/javascript/reference.html#spherical │ │ │ │ │ */ │ │ │ │ │ -OpenLayers.Format.WPSDescribeProcess = OpenLayers.Class( │ │ │ │ │ - OpenLayers.Format.XML, { │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Constant: VERSION │ │ │ │ │ - * {String} 1.0.0 │ │ │ │ │ - */ │ │ │ │ │ - VERSION: "1.0.0", │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Property: namespaces │ │ │ │ │ - * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ │ - */ │ │ │ │ │ - namespaces: { │ │ │ │ │ - wps: "http://www.opengis.net/wps/1.0.0", │ │ │ │ │ - ows: "http://www.opengis.net/ows/1.1", │ │ │ │ │ - xsi: "http://www.w3.org/2001/XMLSchema-instance" │ │ │ │ │ - }, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Property: schemaLocation │ │ │ │ │ - * {String} Schema location │ │ │ │ │ - */ │ │ │ │ │ - schemaLocation: "http://www.opengis.net/wps/1.0.0 http://schemas.opengis.net/wps/1.0.0/wpsAll.xsd", │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Property: defaultPrefix │ │ │ │ │ - */ │ │ │ │ │ - defaultPrefix: "wps", │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Property: regExes │ │ │ │ │ - * Compiled regular expressions for manipulating strings. │ │ │ │ │ - */ │ │ │ │ │ - regExes: { │ │ │ │ │ - trimSpace: (/^\s*|\s*$/g), │ │ │ │ │ - removeSpace: (/\s*/g), │ │ │ │ │ - splitSpace: (/\s+/), │ │ │ │ │ - trimComma: (/\s*,\s*/g) │ │ │ │ │ - }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Constructor: OpenLayers.Format.WPSDescribeProcess │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ │ - * this instance. │ │ │ │ │ - */ │ │ │ │ │ +OpenLayers.Spherical = OpenLayers.Spherical || {}; │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIMethod: read │ │ │ │ │ - * Parse a WPS DescribeProcess and return an object with its information. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * data - {String} or {DOMElement} data to read/parse. │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Object} │ │ │ │ │ - */ │ │ │ │ │ - read: function(data) { │ │ │ │ │ - if (typeof data == "string") { │ │ │ │ │ - data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); │ │ │ │ │ - } │ │ │ │ │ - if (data && data.nodeType == 9) { │ │ │ │ │ - data = data.documentElement; │ │ │ │ │ - } │ │ │ │ │ - var info = {}; │ │ │ │ │ - this.readNode(data, info); │ │ │ │ │ - return info; │ │ │ │ │ - }, │ │ │ │ │ +OpenLayers.Spherical.DEFAULT_RADIUS = 6378137; │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: readers │ │ │ │ │ - * Contains public functions, grouped by namespace prefix, that will │ │ │ │ │ - * be applied when a namespaced node is found matching the function │ │ │ │ │ - * name. The function will be applied in the scope of this parser │ │ │ │ │ - * with two arguments: the node being read and a context object passed │ │ │ │ │ - * from the parent. │ │ │ │ │ - */ │ │ │ │ │ - readers: { │ │ │ │ │ - "wps": { │ │ │ │ │ - "ProcessDescriptions": function(node, obj) { │ │ │ │ │ - obj.processDescriptions = {}; │ │ │ │ │ - this.readChildNodes(node, obj.processDescriptions); │ │ │ │ │ - }, │ │ │ │ │ - "ProcessDescription": function(node, processDescriptions) { │ │ │ │ │ - var processVersion = this.getAttributeNS(node, this.namespaces.wps, "processVersion"); │ │ │ │ │ - var processDescription = { │ │ │ │ │ - processVersion: processVersion, │ │ │ │ │ - statusSupported: (node.getAttribute("statusSupported") === "true"), │ │ │ │ │ - storeSupported: (node.getAttribute("storeSupported") === "true") │ │ │ │ │ - }; │ │ │ │ │ - this.readChildNodes(node, processDescription); │ │ │ │ │ - processDescriptions[processDescription.identifier] = processDescription; │ │ │ │ │ - }, │ │ │ │ │ - "DataInputs": function(node, processDescription) { │ │ │ │ │ - processDescription.dataInputs = []; │ │ │ │ │ - this.readChildNodes(node, processDescription.dataInputs); │ │ │ │ │ - }, │ │ │ │ │ - "ProcessOutputs": function(node, processDescription) { │ │ │ │ │ - processDescription.processOutputs = []; │ │ │ │ │ - this.readChildNodes(node, processDescription.processOutputs); │ │ │ │ │ - }, │ │ │ │ │ - "Output": function(node, processOutputs) { │ │ │ │ │ - var output = {}; │ │ │ │ │ - this.readChildNodes(node, output); │ │ │ │ │ - processOutputs.push(output); │ │ │ │ │ - }, │ │ │ │ │ - "ComplexOutput": function(node, output) { │ │ │ │ │ - output.complexOutput = {}; │ │ │ │ │ - this.readChildNodes(node, output.complexOutput); │ │ │ │ │ - }, │ │ │ │ │ - "LiteralOutput": function(node, output) { │ │ │ │ │ - output.literalOutput = {}; │ │ │ │ │ - this.readChildNodes(node, output.literalOutput); │ │ │ │ │ - }, │ │ │ │ │ - "Input": function(node, dataInputs) { │ │ │ │ │ - var input = { │ │ │ │ │ - maxOccurs: parseInt(node.getAttribute("maxOccurs")), │ │ │ │ │ - minOccurs: parseInt(node.getAttribute("minOccurs")) │ │ │ │ │ - }; │ │ │ │ │ - this.readChildNodes(node, input); │ │ │ │ │ - dataInputs.push(input); │ │ │ │ │ - }, │ │ │ │ │ - "BoundingBoxData": function(node, input) { │ │ │ │ │ - input.boundingBoxData = {}; │ │ │ │ │ - this.readChildNodes(node, input.boundingBoxData); │ │ │ │ │ - }, │ │ │ │ │ - "CRS": function(node, obj) { │ │ │ │ │ - if (!obj.CRSs) { │ │ │ │ │ - obj.CRSs = {}; │ │ │ │ │ - } │ │ │ │ │ - obj.CRSs[this.getChildValue(node)] = true; │ │ │ │ │ - }, │ │ │ │ │ - "LiteralData": function(node, input) { │ │ │ │ │ - input.literalData = {}; │ │ │ │ │ - this.readChildNodes(node, input.literalData); │ │ │ │ │ - }, │ │ │ │ │ - "ComplexData": function(node, input) { │ │ │ │ │ - input.complexData = {}; │ │ │ │ │ - this.readChildNodes(node, input.complexData); │ │ │ │ │ - }, │ │ │ │ │ - "Default": function(node, complexData) { │ │ │ │ │ - complexData["default"] = {}; │ │ │ │ │ - this.readChildNodes(node, complexData["default"]); │ │ │ │ │ - }, │ │ │ │ │ - "Supported": function(node, complexData) { │ │ │ │ │ - complexData["supported"] = {}; │ │ │ │ │ - this.readChildNodes(node, complexData["supported"]); │ │ │ │ │ - }, │ │ │ │ │ - "Format": function(node, obj) { │ │ │ │ │ - var format = {}; │ │ │ │ │ - this.readChildNodes(node, format); │ │ │ │ │ - if (!obj.formats) { │ │ │ │ │ - obj.formats = {}; │ │ │ │ │ - } │ │ │ │ │ - obj.formats[format.mimeType] = true; │ │ │ │ │ - }, │ │ │ │ │ - "MimeType": function(node, format) { │ │ │ │ │ - format.mimeType = this.getChildValue(node); │ │ │ │ │ - } │ │ │ │ │ - }, │ │ │ │ │ - "ows": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers["ows"] │ │ │ │ │ - }, │ │ │ │ │ +/** │ │ │ │ │ + * APIFunction: computeDistanceBetween │ │ │ │ │ + * Computes the distance between two LonLats. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * from - {} or {Object} Starting point. A LonLat or │ │ │ │ │ + * a JavaScript literal with lon lat properties. │ │ │ │ │ + * to - {} or {Object} Ending point. A LonLat or a │ │ │ │ │ + * JavaScript literal with lon lat properties. │ │ │ │ │ + * radius - {Float} The radius. Optional. Defaults to 6378137 meters. │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Float} The distance in meters. │ │ │ │ │ + */ │ │ │ │ │ +OpenLayers.Spherical.computeDistanceBetween = function(from, to, radius) { │ │ │ │ │ + var R = radius || OpenLayers.Spherical.DEFAULT_RADIUS; │ │ │ │ │ + var sinHalfDeltaLon = Math.sin(Math.PI * (to.lon - from.lon) / 360); │ │ │ │ │ + var sinHalfDeltaLat = Math.sin(Math.PI * (to.lat - from.lat) / 360); │ │ │ │ │ + var a = sinHalfDeltaLat * sinHalfDeltaLat + │ │ │ │ │ + sinHalfDeltaLon * sinHalfDeltaLon * Math.cos(Math.PI * from.lat / 180) * Math.cos(Math.PI * to.lat / 180); │ │ │ │ │ + return 2 * R * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); │ │ │ │ │ +}; │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.WPSDescribeProcess" │ │ │ │ │ │ │ │ │ │ - }); │ │ │ │ │ +/** │ │ │ │ │ + * APIFunction: computeHeading │ │ │ │ │ + * Computes the heading from one LonLat to another LonLat. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * from - {} or {Object} Starting point. A LonLat or │ │ │ │ │ + * a JavaScript literal with lon lat properties. │ │ │ │ │ + * to - {} or {Object} Ending point. A LonLat or a │ │ │ │ │ + * JavaScript literal with lon lat properties. │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Float} The heading in degrees. │ │ │ │ │ + */ │ │ │ │ │ +OpenLayers.Spherical.computeHeading = function(from, to) { │ │ │ │ │ + var y = Math.sin(Math.PI * (from.lon - to.lon) / 180) * Math.cos(Math.PI * to.lat / 180); │ │ │ │ │ + var x = Math.cos(Math.PI * from.lat / 180) * Math.sin(Math.PI * to.lat / 180) - │ │ │ │ │ + Math.sin(Math.PI * from.lat / 180) * Math.cos(Math.PI * to.lat / 180) * Math.cos(Math.PI * (from.lon - to.lon) / 180); │ │ │ │ │ + return 180 * Math.atan2(y, x) / Math.PI; │ │ │ │ │ +}; │ │ │ │ │ /* ====================================================================== │ │ │ │ │ - OpenLayers/WPSClient.js │ │ │ │ │ + OpenLayers/Renderer/Elements.js │ │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * @requires OpenLayers/SingleFile.js │ │ │ │ │ + * @requires OpenLayers/Renderer.js │ │ │ │ │ */ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * @requires OpenLayers/Events.js │ │ │ │ │ - * @requires OpenLayers/WPSProcess.js │ │ │ │ │ - * @requires OpenLayers/Format/WPSDescribeProcess.js │ │ │ │ │ - * @requires OpenLayers/Request.js │ │ │ │ │ + * Class: OpenLayers.ElementsIndexer │ │ │ │ │ + * This class takes care of figuring out which order elements should be │ │ │ │ │ + * placed in the DOM based on given indexing methods. │ │ │ │ │ */ │ │ │ │ │ +OpenLayers.ElementsIndexer = OpenLayers.Class({ │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * Class: OpenLayers.WPSClient │ │ │ │ │ - * High level API for interaction with Web Processing Services (WPS). │ │ │ │ │ - * An instance is used to create │ │ │ │ │ - * instances for servers known to the WPSClient. The WPSClient also caches │ │ │ │ │ - * DescribeProcess responses to reduce the number of requests sent to servers │ │ │ │ │ - * when processes are created. │ │ │ │ │ - */ │ │ │ │ │ -OpenLayers.WPSClient = OpenLayers.Class({ │ │ │ │ │ + /** │ │ │ │ │ + * Property: maxZIndex │ │ │ │ │ + * {Integer} This is the largest-most z-index value for a node │ │ │ │ │ + * contained within the indexer. │ │ │ │ │ + */ │ │ │ │ │ + maxZIndex: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: servers │ │ │ │ │ - * {Object} Service metadata, keyed by a local identifier. │ │ │ │ │ - * │ │ │ │ │ - * Properties: │ │ │ │ │ - * url - {String} the url of the server │ │ │ │ │ - * version - {String} WPS version of the server │ │ │ │ │ - * processDescription - {Object} Cache of raw DescribeProcess │ │ │ │ │ - * responses, keyed by process identifier. │ │ │ │ │ + * Property: order │ │ │ │ │ + * {Array} 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. │ │ │ │ │ */ │ │ │ │ │ - servers: null, │ │ │ │ │ + order: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: version │ │ │ │ │ - * {String} The default WPS version to use if none is configured. Default │ │ │ │ │ - * is '1.0.0'. │ │ │ │ │ + * 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). │ │ │ │ │ */ │ │ │ │ │ - version: '1.0.0', │ │ │ │ │ + indices: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: lazy │ │ │ │ │ - * {Boolean} Should the DescribeProcess be deferred until a process is │ │ │ │ │ - * fully configured? Default is false. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - lazy: false, │ │ │ │ │ + compare: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: events │ │ │ │ │ - * {} │ │ │ │ │ - * │ │ │ │ │ - * Supported event types: │ │ │ │ │ - * describeprocess - Fires when the process description is available. │ │ │ │ │ - * Listeners receive an object with a 'raw' property holding the raw │ │ │ │ │ - * DescribeProcess response, and an 'identifier' property holding the │ │ │ │ │ - * process identifier of the described process. │ │ │ │ │ + * APIMethod: initialize │ │ │ │ │ + * Create a new indexer with │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * yOrdering - {Boolean} Whether to use y-ordering. │ │ │ │ │ */ │ │ │ │ │ - events: null, │ │ │ │ │ + initialize: function(yOrdering) { │ │ │ │ │ + │ │ │ │ │ + this.compare = yOrdering ? │ │ │ │ │ + OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_Y_ORDER : │ │ │ │ │ + OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_DRAWING_ORDER; │ │ │ │ │ + │ │ │ │ │ + this.clear(); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Constructor: OpenLayers.WPSClient │ │ │ │ │ - * │ │ │ │ │ + * 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: │ │ │ │ │ - * options - {Object} Object whose properties will be set on the instance. │ │ │ │ │ - * │ │ │ │ │ - * Avaliable options: │ │ │ │ │ - * servers - {Object} Mandatory. Service metadata, keyed by a local │ │ │ │ │ - * identifier. Can either be a string with the service url or an │ │ │ │ │ - * object literal with additional metadata: │ │ │ │ │ + * 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 │ │ │ │ │ * │ │ │ │ │ - * (code) │ │ │ │ │ - * servers: { │ │ │ │ │ - * local: '/geoserver/wps' │ │ │ │ │ - * }, { │ │ │ │ │ - * opengeo: { │ │ │ │ │ - * url: 'http://demo.opengeo.org/geoserver/wps', │ │ │ │ │ - * version: '1.0.0' │ │ │ │ │ - * } │ │ │ │ │ - * } │ │ │ │ │ - * (end) │ │ │ │ │ + * Parameters: │ │ │ │ │ + * node - {DOMElement} The node to test for existence. │ │ │ │ │ * │ │ │ │ │ - * lazy - {Boolean} Optional. Set to true if DescribeProcess should not be │ │ │ │ │ - * requested until a process is fully configured. Default is false. │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Boolean} Whether or not the node exists in the indexer? │ │ │ │ │ */ │ │ │ │ │ - initialize: function(options) { │ │ │ │ │ - OpenLayers.Util.extend(this, options); │ │ │ │ │ - this.events = new OpenLayers.Events(this); │ │ │ │ │ - this.servers = {}; │ │ │ │ │ - for (var s in options.servers) { │ │ │ │ │ - this.servers[s] = typeof options.servers[s] == 'string' ? { │ │ │ │ │ - url: options.servers[s], │ │ │ │ │ - version: this.version, │ │ │ │ │ - processDescription: {} │ │ │ │ │ - } : options.servers[s]; │ │ │ │ │ + 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: execute │ │ │ │ │ - * Shortcut to execute a process with a single function call. This is │ │ │ │ │ - * equivalent to using and then calling execute on the │ │ │ │ │ - * process. │ │ │ │ │ - * │ │ │ │ │ + * APIMethod: getNextElement │ │ │ │ │ + * Get the next element in the order stack. │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * options - {Object} Options for the execute operation. │ │ │ │ │ + * 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. │ │ │ │ │ * │ │ │ │ │ - * Available options: │ │ │ │ │ - * server - {String} Mandatory. One of the local identifiers of the │ │ │ │ │ - * configured servers. │ │ │ │ │ - * process - {String} Mandatory. A process identifier known to the │ │ │ │ │ - * server. │ │ │ │ │ - * inputs - {Object} The inputs for the process, keyed by input identifier. │ │ │ │ │ - * For spatial data inputs, the value of an input is usually an │ │ │ │ │ - * , an or an array of │ │ │ │ │ - * geometries or features. │ │ │ │ │ - * output - {String} The identifier of an output to parse. Optional. If not │ │ │ │ │ - * provided, the first output will be parsed. │ │ │ │ │ - * success - {Function} Callback to call when the process is complete. │ │ │ │ │ - * This function is called with an outputs object as argument, which │ │ │ │ │ - * will have a property with the identifier of the requested output │ │ │ │ │ - * (e.g. 'result'). For processes that generate spatial output, the │ │ │ │ │ - * value will either be a single or an │ │ │ │ │ - * array of features. │ │ │ │ │ - * scope - {Object} Optional scope for the success callback. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - execute: function(options) { │ │ │ │ │ - var process = this.getProcess(options.server, options.process); │ │ │ │ │ - process.execute({ │ │ │ │ │ - inputs: options.inputs, │ │ │ │ │ - success: options.success, │ │ │ │ │ - scope: options.scope │ │ │ │ │ - }); │ │ │ │ │ + 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); │ │ │ │ │ + } │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: getProcess │ │ │ │ │ - * Creates an . │ │ │ │ │ + * 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: │ │ │ │ │ - * serverID - {String} Local identifier from the servers that this instance │ │ │ │ │ - * was constructed with. │ │ │ │ │ - * processID - {String} Process identifier known to the server. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - getProcess: function(serverID, processID) { │ │ │ │ │ - var process = new OpenLayers.WPSProcess({ │ │ │ │ │ - client: this, │ │ │ │ │ - server: serverID, │ │ │ │ │ - identifier: processID │ │ │ │ │ - }); │ │ │ │ │ - if (!this.lazy) { │ │ │ │ │ - process.describe(); │ │ │ │ │ + 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 process; │ │ │ │ │ + return coordSysUnchanged; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: describeProcess │ │ │ │ │ + /** │ │ │ │ │ + * 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: │ │ │ │ │ - * serverID - {String} Identifier of the server │ │ │ │ │ - * processID - {String} Identifier of the requested process │ │ │ │ │ - * callback - {Function} Callback to call when the description is available │ │ │ │ │ - * scope - {Object} Optional execution scope for the callback function │ │ │ │ │ + * geometry - {} │ │ │ │ │ + * style - {Object} │ │ │ │ │ + * featureId - {String} │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Boolean} true if the geometry has been drawn completely; null if │ │ │ │ │ + * incomplete; false otherwise │ │ │ │ │ */ │ │ │ │ │ - describeProcess: function(serverID, processID, callback, scope) { │ │ │ │ │ - var server = this.servers[serverID]; │ │ │ │ │ - if (!server.processDescription[processID]) { │ │ │ │ │ - if (!(processID in server.processDescription)) { │ │ │ │ │ - // set to null so we know a describeFeature request is pending │ │ │ │ │ - server.processDescription[processID] = null; │ │ │ │ │ - OpenLayers.Request.GET({ │ │ │ │ │ - url: server.url, │ │ │ │ │ - params: { │ │ │ │ │ - SERVICE: 'WPS', │ │ │ │ │ - VERSION: server.version, │ │ │ │ │ - REQUEST: 'DescribeProcess', │ │ │ │ │ - IDENTIFIER: processID │ │ │ │ │ - }, │ │ │ │ │ - success: function(response) { │ │ │ │ │ - server.processDescription[processID] = response.responseText; │ │ │ │ │ - this.events.triggerEvent('describeprocess', { │ │ │ │ │ - identifier: processID, │ │ │ │ │ - raw: response.responseText │ │ │ │ │ - }); │ │ │ │ │ - }, │ │ │ │ │ - scope: this │ │ │ │ │ - }); │ │ │ │ │ + drawGeometry: function(geometry, style, featureId) { │ │ │ │ │ + var className = geometry.CLASS_NAME; │ │ │ │ │ + var rendered = true; │ │ │ │ │ + if ((className == "OpenLayers.Geometry.Collection") || │ │ │ │ │ + (className == "OpenLayers.Geometry.MultiPoint") || │ │ │ │ │ + (className == "OpenLayers.Geometry.MultiLineString") || │ │ │ │ │ + (className == "OpenLayers.Geometry.MultiPolygon")) { │ │ │ │ │ + for (var i = 0, len = geometry.components.length; i < len; i++) { │ │ │ │ │ + rendered = this.drawGeometry( │ │ │ │ │ + geometry.components[i], style, featureId) && rendered; │ │ │ │ │ + } │ │ │ │ │ + return rendered; │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + rendered = false; │ │ │ │ │ + var removeBackground = false; │ │ │ │ │ + if (style.display != "none") { │ │ │ │ │ + if (style.backgroundGraphic) { │ │ │ │ │ + this.redrawBackgroundNode(geometry.id, geometry, style, │ │ │ │ │ + featureId); │ │ │ │ │ } else { │ │ │ │ │ - // pending request │ │ │ │ │ - this.events.register('describeprocess', this, function describe(evt) { │ │ │ │ │ - if (evt.identifier === processID) { │ │ │ │ │ - this.events.unregister('describeprocess', this, describe); │ │ │ │ │ - callback.call(scope, evt.raw); │ │ │ │ │ - } │ │ │ │ │ - }); │ │ │ │ │ + removeBackground = true; │ │ │ │ │ + } │ │ │ │ │ + rendered = this.redrawNode(geometry.id, geometry, style, │ │ │ │ │ + featureId); │ │ │ │ │ + } │ │ │ │ │ + if (rendered == false) { │ │ │ │ │ + var node = document.getElementById(geometry.id); │ │ │ │ │ + if (node) { │ │ │ │ │ + if (node._style.backgroundGraphic) { │ │ │ │ │ + removeBackground = true; │ │ │ │ │ + } │ │ │ │ │ + node.parentNode.removeChild(node); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + if (removeBackground) { │ │ │ │ │ + var node = document.getElementById( │ │ │ │ │ + geometry.id + this.BACKGROUND_ID_SUFFIX); │ │ │ │ │ + if (node) { │ │ │ │ │ + node.parentNode.removeChild(node); │ │ │ │ │ } │ │ │ │ │ - } else { │ │ │ │ │ - window.setTimeout(function() { │ │ │ │ │ - callback.call(scope, server.processDescription[processID]); │ │ │ │ │ - }, 0); │ │ │ │ │ } │ │ │ │ │ + return rendered; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: destroy │ │ │ │ │ + * Method: redrawNode │ │ │ │ │ + * │ │ │ │ │ + * 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 │ │ │ │ │ */ │ │ │ │ │ - destroy: function() { │ │ │ │ │ - this.events.destroy(); │ │ │ │ │ - this.events = null; │ │ │ │ │ - this.servers = null; │ │ │ │ │ + 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; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: 'OpenLayers.WPSClient' │ │ │ │ │ + /** │ │ │ │ │ + * 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); │ │ │ │ │ │ │ │ │ │ -}); │ │ │ │ │ -/* ====================================================================== │ │ │ │ │ - OpenLayers/Handler.js │ │ │ │ │ - ====================================================================== */ │ │ │ │ │ + // 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; │ │ │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ - * full list of 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 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; │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ │ - * @requires OpenLayers/Events.js │ │ │ │ │ - */ │ │ │ │ │ + node._options = options; │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * 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({ │ │ │ │ │ + //set style │ │ │ │ │ + //TBD simplify this │ │ │ │ │ + if (drawn != false) { │ │ │ │ │ + return { │ │ │ │ │ + node: this.setStyle(node, style, options, geometry), │ │ │ │ │ + complete: drawn │ │ │ │ │ + }; │ │ │ │ │ + } else { │ │ │ │ │ + return false; │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: id │ │ │ │ │ - * {String} │ │ │ │ │ + * 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} │ │ │ │ │ */ │ │ │ │ │ - id: null, │ │ │ │ │ + postDraw: function(node) {}, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * 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 │ │ │ │ │ */ │ │ │ │ │ - control: null, │ │ │ │ │ + drawPoint: function(node, geometry) {}, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: map │ │ │ │ │ - * {} │ │ │ │ │ + * 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 │ │ │ │ │ */ │ │ │ │ │ - map: null, │ │ │ │ │ + drawLineString: function(node, geometry) {}, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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) │ │ │ │ │ + * 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 │ │ │ │ │ */ │ │ │ │ │ - keyMask: null, │ │ │ │ │ + drawLinearRing: function(node, geometry) {}, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: active │ │ │ │ │ - * {Boolean} │ │ │ │ │ + * 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 │ │ │ │ │ */ │ │ │ │ │ - active: false, │ │ │ │ │ + drawPolygon: function(node, geometry) {}, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * 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 │ │ │ │ │ */ │ │ │ │ │ - evt: null, │ │ │ │ │ + drawRectangle: function(node, geometry) {}, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * 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 │ │ │ │ │ */ │ │ │ │ │ - touch: false, │ │ │ │ │ + drawCircle: function(node, geometry) {}, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Constructor: OpenLayers.Handler │ │ │ │ │ - * Construct a handler. │ │ │ │ │ - * │ │ │ │ │ + * Method: removeText │ │ │ │ │ + * Removes a label │ │ │ │ │ + * │ │ │ │ │ * 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. │ │ │ │ │ + * featureId - {String} │ │ │ │ │ */ │ │ │ │ │ - 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); │ │ │ │ │ + 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); │ │ │ │ │ } │ │ │ │ │ - │ │ │ │ │ - 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. │ │ │ │ │ + * Method: getFeatureIdFromEvent │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * evt - {Object} An object │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {Boolean} The keyMask matches the keys down on an event. │ │ │ │ │ + * {String} A feature id or undefined. │ │ │ │ │ */ │ │ │ │ │ - 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); │ │ │ │ │ + getFeatureIdFromEvent: function(evt) { │ │ │ │ │ + var target = evt.target; │ │ │ │ │ + var useElement = target && target.correspondingUseElement; │ │ │ │ │ + var node = useElement ? useElement : (target || evt.srcElement); │ │ │ │ │ + return node._featureId; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIMethod: activate │ │ │ │ │ - * Turn on the handler. Returns false if the handler was already active. │ │ │ │ │ + /** │ │ │ │ │ + * 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. │ │ │ │ │ * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Boolean} The handler was activated. │ │ │ │ │ + * Parameters: │ │ │ │ │ + * geometry - {} │ │ │ │ │ + * featureId - {String} │ │ │ │ │ */ │ │ │ │ │ - activate: function() { │ │ │ │ │ - if (this.active) { │ │ │ │ │ - return false; │ │ │ │ │ - } │ │ │ │ │ - // register for event handlers defined on this class. │ │ │ │ │ - var events = OpenLayers.Events.prototype.BROWSER_EVENTS; │ │ │ │ │ - for (var i = 0, len = events.length; i < len; i++) { │ │ │ │ │ - if (this[events[i]]) { │ │ │ │ │ - this.register(events[i], this[events[i]]); │ │ │ │ │ + eraseGeometry: function(geometry, featureId) { │ │ │ │ │ + if ((geometry.CLASS_NAME == "OpenLayers.Geometry.MultiPoint") || │ │ │ │ │ + (geometry.CLASS_NAME == "OpenLayers.Geometry.MultiLineString") || │ │ │ │ │ + (geometry.CLASS_NAME == "OpenLayers.Geometry.MultiPolygon") || │ │ │ │ │ + (geometry.CLASS_NAME == "OpenLayers.Geometry.Collection")) { │ │ │ │ │ + for (var i = 0, len = geometry.components.length; i < len; i++) { │ │ │ │ │ + this.eraseGeometry(geometry.components[i], featureId); │ │ │ │ │ + } │ │ │ │ │ + } else { │ │ │ │ │ + var element = OpenLayers.Util.getElement(geometry.id); │ │ │ │ │ + if (element && element.parentNode) { │ │ │ │ │ + if (element.geometry) { │ │ │ │ │ + element.geometry.destroy(); │ │ │ │ │ + element.geometry = null; │ │ │ │ │ + } │ │ │ │ │ + element.parentNode.removeChild(element); │ │ │ │ │ + │ │ │ │ │ + if (this.indexer) { │ │ │ │ │ + this.indexer.remove(element); │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + if (element._style.backgroundGraphic) { │ │ │ │ │ + var backgroundId = geometry.id + this.BACKGROUND_ID_SUFFIX; │ │ │ │ │ + var bElem = OpenLayers.Util.getElement(backgroundId); │ │ │ │ │ + if (bElem && bElem.parentNode) { │ │ │ │ │ + // No need to destroy the geometry since the element and the background │ │ │ │ │ + // node share the same geometry. │ │ │ │ │ + bElem.parentNode.removeChild(bElem); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ } │ │ │ │ │ } │ │ │ │ │ - this.active = true; │ │ │ │ │ - return true; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIMethod: deactivate │ │ │ │ │ - * Turn off the handler. Returns false if the handler was already inactive. │ │ │ │ │ + /** │ │ │ │ │ + * Method: nodeFactory │ │ │ │ │ + * Create new node of the specified type, with the (optional) specified id. │ │ │ │ │ + * │ │ │ │ │ + * If node already exists with same ID and a different type, we remove it │ │ │ │ │ + * and then call ourselves again to recreate it. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * id - {String} │ │ │ │ │ + * type - {String} type Kind of node to draw. │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {Boolean} The handler was deactivated. │ │ │ │ │ + * {DOMElement} A new node of the given type and id. │ │ │ │ │ */ │ │ │ │ │ - deactivate: function() { │ │ │ │ │ - if (!this.active) { │ │ │ │ │ - return false; │ │ │ │ │ - } │ │ │ │ │ - // unregister event handlers defined on this class. │ │ │ │ │ - var events = OpenLayers.Events.prototype.BROWSER_EVENTS; │ │ │ │ │ - for (var i = 0, len = events.length; i < len; i++) { │ │ │ │ │ - if (this[events[i]]) { │ │ │ │ │ - this.unregister(events[i], this[events[i]]); │ │ │ │ │ + nodeFactory: function(id, type) { │ │ │ │ │ + var node = OpenLayers.Util.getElement(id); │ │ │ │ │ + if (node) { │ │ │ │ │ + if (!this.nodeTypeCompare(node, type)) { │ │ │ │ │ + node.parentNode.removeChild(node); │ │ │ │ │ + node = this.nodeFactory(id, type); │ │ │ │ │ } │ │ │ │ │ + } else { │ │ │ │ │ + node = this.createNode(type, id); │ │ │ │ │ } │ │ │ │ │ - this.touch = false; │ │ │ │ │ - this.active = false; │ │ │ │ │ - return true; │ │ │ │ │ + return node; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: startTouch │ │ │ │ │ - * Start touch events, this method must be called by subclasses in │ │ │ │ │ - * "touchstart" method. When touch events are started will be │ │ │ │ │ - * true and all mouse related listeners will do nothing. │ │ │ │ │ + /** │ │ │ │ │ + * Method: nodeTypeCompare │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * node - {DOMElement} │ │ │ │ │ + * type - {String} Kind of node │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Boolean} Whether or not the specified node is of the specified type │ │ │ │ │ + * This function must be overridden by subclasses. │ │ │ │ │ */ │ │ │ │ │ - startTouch: function() { │ │ │ │ │ - if (!this.touch) { │ │ │ │ │ - this.touch = true; │ │ │ │ │ - var events = [ │ │ │ │ │ - "mousedown", "mouseup", "mousemove", "click", "dblclick", │ │ │ │ │ - "mouseout" │ │ │ │ │ - ]; │ │ │ │ │ - for (var i = 0, len = events.length; i < len; i++) { │ │ │ │ │ - if (this[events[i]]) { │ │ │ │ │ - this.unregister(events[i], this[events[i]]); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - }, │ │ │ │ │ + nodeTypeCompare: function(node, type) {}, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: callback │ │ │ │ │ - * Trigger the control's named callback with the given arguments │ │ │ │ │ - * │ │ │ │ │ + /** │ │ │ │ │ + * Method: createNode │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * name - {String} The key for the callback that is one of the properties │ │ │ │ │ - * of the handler's callbacks object. │ │ │ │ │ - * args - {Array(*)} An array of arguments (any type) with which to call │ │ │ │ │ - * the callback (defined by the control). │ │ │ │ │ + * type - {String} Kind of node to draw. │ │ │ │ │ + * id - {String} Id for node. │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {DOMElement} A new node of the given type and id. │ │ │ │ │ + * This function must be overridden by subclasses. │ │ │ │ │ */ │ │ │ │ │ - callback: function(name, args) { │ │ │ │ │ - if (name && this.callbacks[name]) { │ │ │ │ │ - this.callbacks[name].apply(this.control, args); │ │ │ │ │ - } │ │ │ │ │ - }, │ │ │ │ │ + createNode: function(type, id) {}, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: register │ │ │ │ │ - * register an event on the map │ │ │ │ │ + * Method: moveRoot │ │ │ │ │ + * moves this renderer's root to a different renderer. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * renderer - {} target renderer for the moved root │ │ │ │ │ */ │ │ │ │ │ - register: function(name, method) { │ │ │ │ │ - // TODO: deal with registerPriority in 3.0 │ │ │ │ │ - this.map.events.registerPriority(name, this, method); │ │ │ │ │ - this.map.events.registerPriority(name, this, this.setEvent); │ │ │ │ │ + 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: unregister │ │ │ │ │ - * unregister an event from the map │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - unregister: function(name, method) { │ │ │ │ │ - this.map.events.unregister(name, this, method); │ │ │ │ │ - this.map.events.unregister(name, this, this.setEvent); │ │ │ │ │ + getRenderLayerId: function() { │ │ │ │ │ + return this.root.parentNode.parentNode.id; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: setEvent │ │ │ │ │ - * With each registered browser event, the handler sets its own evt │ │ │ │ │ - * property. This property can be accessed by controls if needed │ │ │ │ │ - * to get more information about the event that the handler is │ │ │ │ │ - * processing. │ │ │ │ │ - * │ │ │ │ │ - * This allows modifier keys on the event to be checked (alt, shift, ctrl, │ │ │ │ │ - * and meta cannot be checked with the keyboard handler). For a │ │ │ │ │ - * control to determine which modifier keys are associated with the │ │ │ │ │ - * event that a handler is currently processing, it should access │ │ │ │ │ - * (code)handler.evt.altKey || handler.evt.shiftKey || │ │ │ │ │ - * handler.evt.ctrlKey || handler.evt.metaKey(end). │ │ │ │ │ - * │ │ │ │ │ + * Method: isComplexSymbol │ │ │ │ │ + * Determines if a symbol cannot be rendered using drawCircle │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * evt - {Event} The browser event. │ │ │ │ │ - */ │ │ │ │ │ - setEvent: function(evt) { │ │ │ │ │ - this.evt = evt; │ │ │ │ │ - return true; │ │ │ │ │ - }, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Method: destroy │ │ │ │ │ - * Deconstruct the handler. │ │ │ │ │ + * graphicName - {String} │ │ │ │ │ + * │ │ │ │ │ + * Returns │ │ │ │ │ + * {Boolean} true if the symbol is complex, false if not │ │ │ │ │ */ │ │ │ │ │ - destroy: function() { │ │ │ │ │ - // unregister event listeners │ │ │ │ │ - this.deactivate(); │ │ │ │ │ - // eliminate circular references │ │ │ │ │ - this.control = this.map = null; │ │ │ │ │ + isComplexSymbol: function(graphicName) { │ │ │ │ │ + return (graphicName != "circle") && !!graphicName; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Handler" │ │ │ │ │ + CLASS_NAME: "OpenLayers.Renderer.Elements" │ │ │ │ │ }); │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * Constant: OpenLayers.Handler.MOD_NONE │ │ │ │ │ - * If set as the , 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/StyleMap.js │ │ │ │ │ + OpenLayers/Renderer/VML.js │ │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ │ - * @requires OpenLayers/Style.js │ │ │ │ │ - * @requires OpenLayers/Feature/Vector.js │ │ │ │ │ + * @requires OpenLayers/Renderer/Elements.js │ │ │ │ │ */ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Class: OpenLayers.StyleMap │ │ │ │ │ + * Class: OpenLayers.Renderer.VML │ │ │ │ │ + * Render vector features in browsers with VML capability. Construct a new │ │ │ │ │ + * VML renderer with the constructor. │ │ │ │ │ + * │ │ │ │ │ + * Note that for all calculations in this class, we use (num | 0) to truncate a │ │ │ │ │ + * float value to an integer. This is done because it seems that VML doesn't │ │ │ │ │ + * support float values. │ │ │ │ │ + * │ │ │ │ │ + * Inherits from: │ │ │ │ │ + * - │ │ │ │ │ */ │ │ │ │ │ -OpenLayers.StyleMap = OpenLayers.Class({ │ │ │ │ │ +OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, { │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: styles │ │ │ │ │ - * {Object} Hash of {}, keyed by names of well known │ │ │ │ │ - * rendering intents (e.g. "default", "temporary", "select", "delete"). │ │ │ │ │ + * Property: xmlns │ │ │ │ │ + * {String} XML Namespace URN │ │ │ │ │ */ │ │ │ │ │ - styles: null, │ │ │ │ │ + xmlns: "urn:schemas-microsoft-com:vml", │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * Property: symbolCache │ │ │ │ │ + * {DOMElement} node holding symbols. This hash is keyed by symbol name, │ │ │ │ │ + * and each value is a hash with a "path" and an "extent" property. │ │ │ │ │ */ │ │ │ │ │ - extendDefault: true, │ │ │ │ │ + symbolCache: {}, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Constructor: OpenLayers.StyleMap │ │ │ │ │ - * │ │ │ │ │ + * Property: offset │ │ │ │ │ + * {Object} Hash with "x" and "y" properties │ │ │ │ │ + */ │ │ │ │ │ + offset: null, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Constructor: OpenLayers.Renderer.VML │ │ │ │ │ + * Create a new VML renderer. │ │ │ │ │ + * │ │ │ │ │ * 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 │ │ │ │ │ + * containerID - {String} The id for the element that contains the renderer │ │ │ │ │ */ │ │ │ │ │ - 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"]) │ │ │ │ │ - }; │ │ │ │ │ + initialize: function(containerID) { │ │ │ │ │ + if (!this.supported()) { │ │ │ │ │ + return; │ │ │ │ │ + } │ │ │ │ │ + if (!document.namespaces.olv) { │ │ │ │ │ + document.namespaces.add("olv", this.xmlns); │ │ │ │ │ + var style = document.createStyleSheet(); │ │ │ │ │ + var shapes = ['shape', 'rect', 'oval', 'fill', 'stroke', 'imagedata', 'group', 'textbox']; │ │ │ │ │ + for (var i = 0, len = shapes.length; i < len; i++) { │ │ │ │ │ │ │ │ │ │ - // 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; │ │ │ │ │ - } │ │ │ │ │ + style.addRule('olv\\:' + shapes[i], "behavior: url(#default#VML); " + │ │ │ │ │ + "position: absolute; display: inline-block;"); │ │ │ │ │ } │ │ │ │ │ } │ │ │ │ │ - OpenLayers.Util.extend(this, options); │ │ │ │ │ + │ │ │ │ │ + OpenLayers.Renderer.Elements.prototype.initialize.apply(this, │ │ │ │ │ + arguments); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: destroy │ │ │ │ │ + * APIMethod: supported │ │ │ │ │ + * Determine whether a browser supports this renderer. │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Boolean} The browser supports the VML renderer │ │ │ │ │ */ │ │ │ │ │ - destroy: function() { │ │ │ │ │ - for (var key in this.styles) { │ │ │ │ │ - this.styles[key].destroy(); │ │ │ │ │ - } │ │ │ │ │ - this.styles = null; │ │ │ │ │ + supported: function() { │ │ │ │ │ + return !!(document.namespaces); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: createSymbolizer │ │ │ │ │ - * Creates the symbolizer for a feature for a render intent. │ │ │ │ │ - * │ │ │ │ │ + * Method: setExtent │ │ │ │ │ + * Set the renderer's extent │ │ │ │ │ + * │ │ │ │ │ * 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). │ │ │ │ │ + * extent - {} │ │ │ │ │ + * resolutionChanged - {Boolean} │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {Object} symbolizer hash │ │ │ │ │ + * {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. │ │ │ │ │ */ │ │ │ │ │ - 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)); │ │ │ │ │ - }, │ │ │ │ │ + setExtent: function(extent, resolutionChanged) { │ │ │ │ │ + var coordSysUnchanged = OpenLayers.Renderer.Elements.prototype.setExtent.apply(this, arguments); │ │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * 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 │ │ │ │ │ - }) │ │ │ │ │ - })); │ │ │ │ │ + var left = (extent.left / resolution) | 0; │ │ │ │ │ + var top = (extent.top / resolution - this.size.h) | 0; │ │ │ │ │ + if (resolutionChanged || !this.offset) { │ │ │ │ │ + this.offset = { │ │ │ │ │ + x: left, │ │ │ │ │ + y: top │ │ │ │ │ + }; │ │ │ │ │ + left = 0; │ │ │ │ │ + top = 0; │ │ │ │ │ + } else { │ │ │ │ │ + left = left - this.offset.x; │ │ │ │ │ + top = top - this.offset.y; │ │ │ │ │ } │ │ │ │ │ - this.styles[renderIntent].addRules(rules); │ │ │ │ │ - }, │ │ │ │ │ - │ │ │ │ │ - CLASS_NAME: "OpenLayers.StyleMap" │ │ │ │ │ -}); │ │ │ │ │ -/* ====================================================================== │ │ │ │ │ - OpenLayers/Popup.js │ │ │ │ │ - ====================================================================== */ │ │ │ │ │ - │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ - * full text of the license. */ │ │ │ │ │ - │ │ │ │ │ -/** │ │ │ │ │ - * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ │ - */ │ │ │ │ │ - │ │ │ │ │ - │ │ │ │ │ -/** │ │ │ │ │ - * Class: OpenLayers.Popup │ │ │ │ │ - * A popup is a small div that can opened and closed on the map. │ │ │ │ │ - * Typically opened in response to clicking on a marker. │ │ │ │ │ - * See . Popup's don't require their own │ │ │ │ │ - * layer and are added the the map using the │ │ │ │ │ - * method. │ │ │ │ │ - * │ │ │ │ │ - * Example: │ │ │ │ │ - * (code) │ │ │ │ │ - * popup = new OpenLayers.Popup("chicken", │ │ │ │ │ - * new OpenLayers.LonLat(5,40), │ │ │ │ │ - * new OpenLayers.Size(200,200), │ │ │ │ │ - * "example popup", │ │ │ │ │ - * true); │ │ │ │ │ - * │ │ │ │ │ - * map.addPopup(popup); │ │ │ │ │ - * (end) │ │ │ │ │ - */ │ │ │ │ │ -OpenLayers.Popup = OpenLayers.Class({ │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Property: events │ │ │ │ │ - * {} custom event manager │ │ │ │ │ - */ │ │ │ │ │ - events: null, │ │ │ │ │ - │ │ │ │ │ - /** Property: id │ │ │ │ │ - * {String} the unique identifier assigned to this popup. │ │ │ │ │ - */ │ │ │ │ │ - id: "", │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Property: lonlat │ │ │ │ │ - * {} the position of this popup on the map │ │ │ │ │ - */ │ │ │ │ │ - lonlat: null, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Property: div │ │ │ │ │ - * {DOMElement} the div that contains this popup. │ │ │ │ │ - */ │ │ │ │ │ - div: null, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Property: contentSize │ │ │ │ │ - * {} the width and height of the content. │ │ │ │ │ - */ │ │ │ │ │ - contentSize: null, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Property: size │ │ │ │ │ - * {} the width and height of the popup. │ │ │ │ │ - */ │ │ │ │ │ - size: null, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Property: contentHTML │ │ │ │ │ - * {String} An HTML string for this popup to display. │ │ │ │ │ - */ │ │ │ │ │ - contentHTML: null, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Property: backgroundColor │ │ │ │ │ - * {String} the background color used by the popup. │ │ │ │ │ - */ │ │ │ │ │ - backgroundColor: "", │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Property: opacity │ │ │ │ │ - * {float} the opacity of this popup (between 0.0 and 1.0) │ │ │ │ │ - */ │ │ │ │ │ - opacity: "", │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: border │ │ │ │ │ - * {String} the border size of the popup. (eg 2px) │ │ │ │ │ - */ │ │ │ │ │ - border: "", │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: contentDiv │ │ │ │ │ - * {DOMElement} a reference to the element that holds the content of │ │ │ │ │ - * the div. │ │ │ │ │ - */ │ │ │ │ │ - contentDiv: null, │ │ │ │ │ + var org = (left - this.xOffset) + " " + top; │ │ │ │ │ + this.root.coordorigin = org; │ │ │ │ │ + var roots = [this.root, this.vectorRoot, this.textRoot]; │ │ │ │ │ + var root; │ │ │ │ │ + for (var i = 0, len = roots.length; i < len; ++i) { │ │ │ │ │ + root = roots[i]; │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: groupDiv │ │ │ │ │ - * {DOMElement} First and only child of 'div'. The group Div contains the │ │ │ │ │ - * 'contentDiv' and the 'closeDiv'. │ │ │ │ │ - */ │ │ │ │ │ - groupDiv: null, │ │ │ │ │ + var size = this.size.w + " " + this.size.h; │ │ │ │ │ + root.coordsize = size; │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: closeDiv │ │ │ │ │ - * {DOMElement} the optional closer image │ │ │ │ │ - */ │ │ │ │ │ - closeDiv: null, │ │ │ │ │ + } │ │ │ │ │ + // flip the VML display Y axis upside down so it │ │ │ │ │ + // matches the display Y axis of the map │ │ │ │ │ + this.root.style.flip = "y"; │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIProperty: autoSize │ │ │ │ │ - * {Boolean} Resize the popup to auto-fit the contents. │ │ │ │ │ - * Default is false. │ │ │ │ │ - */ │ │ │ │ │ - autoSize: false, │ │ │ │ │ + return coordSysUnchanged; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIProperty: minSize │ │ │ │ │ - * {} Minimum size allowed for the popup's contents. │ │ │ │ │ - */ │ │ │ │ │ - minSize: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: maxSize │ │ │ │ │ - * {} Maximum size allowed for the popup's contents. │ │ │ │ │ - */ │ │ │ │ │ - maxSize: null, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Property: displayClass │ │ │ │ │ - * {String} The CSS class of the popup. │ │ │ │ │ - */ │ │ │ │ │ - displayClass: "olPopup", │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Property: contentDisplayClass │ │ │ │ │ - * {String} The CSS class of the popup content div. │ │ │ │ │ - */ │ │ │ │ │ - contentDisplayClass: "olPopupContent", │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Property: padding │ │ │ │ │ - * {int or } An extra opportunity to specify internal │ │ │ │ │ - * padding of the content div inside the popup. This was originally │ │ │ │ │ - * confused with the css padding as specified in style.css's │ │ │ │ │ - * 'olPopupContent' class. We would like to get rid of this altogether, │ │ │ │ │ - * except that it does come in handy for the framed and anchoredbubble │ │ │ │ │ - * popups, who need to maintain yet another barrier between their │ │ │ │ │ - * content and the outer border of the popup itself. │ │ │ │ │ - * │ │ │ │ │ - * Note that in order to not break API, we must continue to support │ │ │ │ │ - * this property being set as an integer. Really, though, we'd like to │ │ │ │ │ - * have this specified as a Bounds object so that user can specify │ │ │ │ │ - * distinct left, top, right, bottom paddings. With the 3.0 release │ │ │ │ │ - * we can make this only a bounds. │ │ │ │ │ - */ │ │ │ │ │ - padding: 0, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Property: disableFirefoxOverflowHack │ │ │ │ │ - * {Boolean} The hack for overflow in Firefox causes all elements │ │ │ │ │ - * to be re-drawn, which causes Flash elements to be │ │ │ │ │ - * re-initialized, which is troublesome. │ │ │ │ │ - * With this property the hack can be disabled. │ │ │ │ │ + * Method: setSize │ │ │ │ │ + * Set the size of the drawing surface │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * size - {} the size of the drawing surface │ │ │ │ │ */ │ │ │ │ │ - disableFirefoxOverflowHack: false, │ │ │ │ │ + setSize: function(size) { │ │ │ │ │ + OpenLayers.Renderer.prototype.setSize.apply(this, arguments); │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: fixPadding │ │ │ │ │ - * To be removed in 3.0, this function merely helps us to deal with the │ │ │ │ │ - * case where the user may have set an integer value for padding, │ │ │ │ │ - * instead of an object. │ │ │ │ │ - */ │ │ │ │ │ - fixPadding: function() { │ │ │ │ │ - if (typeof this.padding == "number") { │ │ │ │ │ - this.padding = new OpenLayers.Bounds( │ │ │ │ │ - this.padding, this.padding, this.padding, this.padding │ │ │ │ │ - ); │ │ │ │ │ + // setting width and height on all roots to avoid flicker which we │ │ │ │ │ + // would get with 100% width and height on child roots │ │ │ │ │ + var roots = [ │ │ │ │ │ + this.rendererRoot, │ │ │ │ │ + this.root, │ │ │ │ │ + this.vectorRoot, │ │ │ │ │ + this.textRoot │ │ │ │ │ + ]; │ │ │ │ │ + var w = this.size.w + "px"; │ │ │ │ │ + var h = this.size.h + "px"; │ │ │ │ │ + var root; │ │ │ │ │ + for (var i = 0, len = roots.length; i < len; ++i) { │ │ │ │ │ + root = roots[i]; │ │ │ │ │ + root.style.width = w; │ │ │ │ │ + root.style.height = h; │ │ │ │ │ } │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: panMapIfOutOfView │ │ │ │ │ - * {Boolean} When drawn, pan map such that the entire popup is visible in │ │ │ │ │ - * the current viewport (if necessary). │ │ │ │ │ - * Default is false. │ │ │ │ │ - */ │ │ │ │ │ - panMapIfOutOfView: false, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * APIProperty: keepInMap │ │ │ │ │ - * {Boolean} If panMapIfOutOfView is false, and this property is true, │ │ │ │ │ - * contrain the popup such that it always fits in the available map │ │ │ │ │ - * space. By default, this is not set on the base class. If you are │ │ │ │ │ - * creating popups that are near map edges and not allowing pannning, │ │ │ │ │ - * and especially if you have a popup which has a │ │ │ │ │ - * fixedRelativePosition, setting this to false may be a smart thing to │ │ │ │ │ - * do. Subclasses may want to override this setting. │ │ │ │ │ - * │ │ │ │ │ - * Default is false. │ │ │ │ │ + * Method: getNodeType │ │ │ │ │ + * Get the node type for a geometry and style │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * geometry - {} │ │ │ │ │ + * style - {Object} │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {String} The corresponding node type for the specified geometry │ │ │ │ │ */ │ │ │ │ │ - keepInMap: false, │ │ │ │ │ + getNodeType: function(geometry, style) { │ │ │ │ │ + var nodeType = null; │ │ │ │ │ + switch (geometry.CLASS_NAME) { │ │ │ │ │ + case "OpenLayers.Geometry.Point": │ │ │ │ │ + if (style.externalGraphic) { │ │ │ │ │ + nodeType = "olv:rect"; │ │ │ │ │ + } else if (this.isComplexSymbol(style.graphicName)) { │ │ │ │ │ + nodeType = "olv:shape"; │ │ │ │ │ + } else { │ │ │ │ │ + nodeType = "olv:oval"; │ │ │ │ │ + } │ │ │ │ │ + break; │ │ │ │ │ + case "OpenLayers.Geometry.Rectangle": │ │ │ │ │ + nodeType = "olv:rect"; │ │ │ │ │ + break; │ │ │ │ │ + case "OpenLayers.Geometry.LineString": │ │ │ │ │ + case "OpenLayers.Geometry.LinearRing": │ │ │ │ │ + case "OpenLayers.Geometry.Polygon": │ │ │ │ │ + case "OpenLayers.Geometry.Curve": │ │ │ │ │ + nodeType = "olv:shape"; │ │ │ │ │ + break; │ │ │ │ │ + default: │ │ │ │ │ + break; │ │ │ │ │ + } │ │ │ │ │ + return nodeType; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIProperty: closeOnMove │ │ │ │ │ - * {Boolean} When map pans, close the popup. │ │ │ │ │ - * Default is false. │ │ │ │ │ - */ │ │ │ │ │ - closeOnMove: false, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Property: map │ │ │ │ │ - * {} this gets set in Map.js when the popup is added to the map │ │ │ │ │ - */ │ │ │ │ │ - map: null, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Constructor: OpenLayers.Popup │ │ │ │ │ - * Create a popup. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * id - {String} a unqiue identifier for this popup. If null is passed │ │ │ │ │ - * an identifier will be automatically generated. │ │ │ │ │ - * lonlat - {} The position on the map the popup will │ │ │ │ │ - * be shown. │ │ │ │ │ - * contentSize - {} The size of the content. │ │ │ │ │ - * contentHTML - {String} An HTML string to display inside the │ │ │ │ │ - * popup. │ │ │ │ │ - * closeBox - {Boolean} Whether to display a close box inside │ │ │ │ │ - * the popup. │ │ │ │ │ - * closeBoxCallback - {Function} Function to be called on closeBox click. │ │ │ │ │ + * Method: setStyle │ │ │ │ │ + * Use to set all the style attributes to a VML node. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * node - {DOMElement} An VML element to decorate │ │ │ │ │ + * style - {Object} │ │ │ │ │ + * options - {Object} Currently supported options include │ │ │ │ │ + * 'isFilled' {Boolean} and │ │ │ │ │ + * 'isStroked' {Boolean} │ │ │ │ │ + * geometry - {} │ │ │ │ │ */ │ │ │ │ │ - initialize: function(id, lonlat, contentSize, contentHTML, closeBox, closeBoxCallback) { │ │ │ │ │ - if (id == null) { │ │ │ │ │ - id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_"); │ │ │ │ │ - } │ │ │ │ │ - │ │ │ │ │ - this.id = id; │ │ │ │ │ - this.lonlat = lonlat; │ │ │ │ │ + setStyle: function(node, style, options, geometry) { │ │ │ │ │ + style = style || node._style; │ │ │ │ │ + options = options || node._options; │ │ │ │ │ + var fillColor = style.fillColor; │ │ │ │ │ │ │ │ │ │ - this.contentSize = (contentSize != null) ? contentSize : │ │ │ │ │ - new OpenLayers.Size( │ │ │ │ │ - OpenLayers.Popup.WIDTH, │ │ │ │ │ - OpenLayers.Popup.HEIGHT); │ │ │ │ │ - if (contentHTML != null) { │ │ │ │ │ - this.contentHTML = contentHTML; │ │ │ │ │ + var title = style.title || style.graphicTitle; │ │ │ │ │ + if (title) { │ │ │ │ │ + node.title = title; │ │ │ │ │ } │ │ │ │ │ - this.backgroundColor = OpenLayers.Popup.COLOR; │ │ │ │ │ - this.opacity = OpenLayers.Popup.OPACITY; │ │ │ │ │ - this.border = OpenLayers.Popup.BORDER; │ │ │ │ │ │ │ │ │ │ - this.div = OpenLayers.Util.createDiv(this.id, null, null, │ │ │ │ │ - null, null, null, "hidden"); │ │ │ │ │ - this.div.className = this.displayClass; │ │ │ │ │ + if (node._geometryClass === "OpenLayers.Geometry.Point") { │ │ │ │ │ + if (style.externalGraphic) { │ │ │ │ │ + options.isFilled = true; │ │ │ │ │ + var width = style.graphicWidth || style.graphicHeight; │ │ │ │ │ + var height = style.graphicHeight || style.graphicWidth; │ │ │ │ │ + width = width ? width : style.pointRadius * 2; │ │ │ │ │ + height = height ? height : style.pointRadius * 2; │ │ │ │ │ │ │ │ │ │ - var groupDivId = this.id + "_GroupDiv"; │ │ │ │ │ - this.groupDiv = OpenLayers.Util.createDiv(groupDivId, null, null, │ │ │ │ │ - null, "relative", null, │ │ │ │ │ - "hidden"); │ │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ │ + var xOffset = (style.graphicXOffset != undefined) ? │ │ │ │ │ + style.graphicXOffset : -(0.5 * width); │ │ │ │ │ + var yOffset = (style.graphicYOffset != undefined) ? │ │ │ │ │ + style.graphicYOffset : -(0.5 * height); │ │ │ │ │ │ │ │ │ │ - var id = this.div.id + "_contentDiv"; │ │ │ │ │ - this.contentDiv = OpenLayers.Util.createDiv(id, null, this.contentSize.clone(), │ │ │ │ │ - null, "relative"); │ │ │ │ │ - this.contentDiv.className = this.contentDisplayClass; │ │ │ │ │ - this.groupDiv.appendChild(this.contentDiv); │ │ │ │ │ - this.div.appendChild(this.groupDiv); │ │ │ │ │ + node.style.left = ((((geometry.x - this.featureDx) / resolution - this.offset.x) + xOffset) | 0) + "px"; │ │ │ │ │ + node.style.top = (((geometry.y / resolution - this.offset.y) - (yOffset + height)) | 0) + "px"; │ │ │ │ │ + node.style.width = width + "px"; │ │ │ │ │ + node.style.height = height + "px"; │ │ │ │ │ + node.style.flip = "y"; │ │ │ │ │ │ │ │ │ │ - if (closeBox) { │ │ │ │ │ - this.addCloseBox(closeBoxCallback); │ │ │ │ │ + // modify fillColor and options for stroke styling below │ │ │ │ │ + fillColor = "none"; │ │ │ │ │ + options.isStroked = false; │ │ │ │ │ + } else if (this.isComplexSymbol(style.graphicName)) { │ │ │ │ │ + var cache = this.importSymbol(style.graphicName); │ │ │ │ │ + node.path = cache.path; │ │ │ │ │ + node.coordorigin = cache.left + "," + cache.bottom; │ │ │ │ │ + var size = cache.size; │ │ │ │ │ + node.coordsize = size + "," + size; │ │ │ │ │ + this.drawCircle(node, geometry, style.pointRadius); │ │ │ │ │ + node.style.flip = "y"; │ │ │ │ │ + } else { │ │ │ │ │ + this.drawCircle(node, geometry, style.pointRadius); │ │ │ │ │ + } │ │ │ │ │ } │ │ │ │ │ │ │ │ │ │ - this.registerEvents(); │ │ │ │ │ - }, │ │ │ │ │ + // fill │ │ │ │ │ + if (options.isFilled) { │ │ │ │ │ + node.fillcolor = fillColor; │ │ │ │ │ + } else { │ │ │ │ │ + node.filled = "false"; │ │ │ │ │ + } │ │ │ │ │ + var fills = node.getElementsByTagName("fill"); │ │ │ │ │ + var fill = (fills.length == 0) ? null : fills[0]; │ │ │ │ │ + if (!options.isFilled) { │ │ │ │ │ + if (fill) { │ │ │ │ │ + node.removeChild(fill); │ │ │ │ │ + } │ │ │ │ │ + } else { │ │ │ │ │ + if (!fill) { │ │ │ │ │ + fill = this.createNode('olv:fill', node.id + "_fill"); │ │ │ │ │ + } │ │ │ │ │ + fill.opacity = style.fillOpacity; │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: destroy │ │ │ │ │ - * nullify references to prevent circular references and memory leaks │ │ │ │ │ - */ │ │ │ │ │ - destroy: function() { │ │ │ │ │ + if (node._geometryClass === "OpenLayers.Geometry.Point" && │ │ │ │ │ + style.externalGraphic) { │ │ │ │ │ │ │ │ │ │ - this.id = null; │ │ │ │ │ - this.lonlat = null; │ │ │ │ │ - this.size = null; │ │ │ │ │ - this.contentHTML = null; │ │ │ │ │ + // override fillOpacity │ │ │ │ │ + if (style.graphicOpacity) { │ │ │ │ │ + fill.opacity = style.graphicOpacity; │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ - this.backgroundColor = null; │ │ │ │ │ - this.opacity = null; │ │ │ │ │ - this.border = null; │ │ │ │ │ + fill.src = style.externalGraphic; │ │ │ │ │ + fill.type = "frame"; │ │ │ │ │ │ │ │ │ │ - if (this.closeOnMove && this.map) { │ │ │ │ │ - this.map.events.unregister("movestart", this, this.hide); │ │ │ │ │ + if (!(style.graphicWidth && style.graphicHeight)) { │ │ │ │ │ + fill.aspect = "atmost"; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + if (fill.parentNode != node) { │ │ │ │ │ + node.appendChild(fill); │ │ │ │ │ + } │ │ │ │ │ } │ │ │ │ │ │ │ │ │ │ - this.events.destroy(); │ │ │ │ │ - this.events = null; │ │ │ │ │ - │ │ │ │ │ - if (this.closeDiv) { │ │ │ │ │ - OpenLayers.Event.stopObservingElement(this.closeDiv); │ │ │ │ │ - this.groupDiv.removeChild(this.closeDiv); │ │ │ │ │ + // additional rendering for rotated graphics or symbols │ │ │ │ │ + var rotation = style.rotation; │ │ │ │ │ + if ((rotation !== undefined || node._rotation !== undefined)) { │ │ │ │ │ + node._rotation = rotation; │ │ │ │ │ + if (style.externalGraphic) { │ │ │ │ │ + this.graphicRotate(node, xOffset, yOffset, style); │ │ │ │ │ + // make the fill fully transparent, because we now have │ │ │ │ │ + // the graphic as imagedata element. We cannot just remove │ │ │ │ │ + // the fill, because this is part of the hack described │ │ │ │ │ + // in graphicRotate │ │ │ │ │ + fill.opacity = 0; │ │ │ │ │ + } else if (node._geometryClass === "OpenLayers.Geometry.Point") { │ │ │ │ │ + node.style.rotation = rotation || 0; │ │ │ │ │ + } │ │ │ │ │ } │ │ │ │ │ - this.closeDiv = null; │ │ │ │ │ │ │ │ │ │ - this.div.removeChild(this.groupDiv); │ │ │ │ │ - this.groupDiv = null; │ │ │ │ │ - │ │ │ │ │ - if (this.map != null) { │ │ │ │ │ - this.map.removePopup(this); │ │ │ │ │ + // stroke │ │ │ │ │ + var strokes = node.getElementsByTagName("stroke"); │ │ │ │ │ + var stroke = (strokes.length == 0) ? null : strokes[0]; │ │ │ │ │ + if (!options.isStroked) { │ │ │ │ │ + node.stroked = false; │ │ │ │ │ + if (stroke) { │ │ │ │ │ + stroke.on = false; │ │ │ │ │ + } │ │ │ │ │ + } else { │ │ │ │ │ + if (!stroke) { │ │ │ │ │ + stroke = this.createNode('olv:stroke', node.id + "_stroke"); │ │ │ │ │ + node.appendChild(stroke); │ │ │ │ │ + } │ │ │ │ │ + stroke.on = true; │ │ │ │ │ + stroke.color = style.strokeColor; │ │ │ │ │ + stroke.weight = style.strokeWidth + "px"; │ │ │ │ │ + stroke.opacity = style.strokeOpacity; │ │ │ │ │ + stroke.endcap = style.strokeLinecap == 'butt' ? 'flat' : │ │ │ │ │ + (style.strokeLinecap || 'round'); │ │ │ │ │ + if (style.strokeDashstyle) { │ │ │ │ │ + stroke.dashstyle = this.dashStyle(style); │ │ │ │ │ + } │ │ │ │ │ } │ │ │ │ │ - this.map = null; │ │ │ │ │ - this.div = null; │ │ │ │ │ │ │ │ │ │ - this.autoSize = null; │ │ │ │ │ - this.minSize = null; │ │ │ │ │ - this.maxSize = null; │ │ │ │ │ - this.padding = null; │ │ │ │ │ - this.panMapIfOutOfView = null; │ │ │ │ │ + if (style.cursor != "inherit" && style.cursor != null) { │ │ │ │ │ + node.style.cursor = style.cursor; │ │ │ │ │ + } │ │ │ │ │ + return node; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: draw │ │ │ │ │ - * Constructs the elements that make up the popup. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * px - {} the position the popup in pixels. │ │ │ │ │ + /** │ │ │ │ │ + * Method: graphicRotate │ │ │ │ │ + * If a point is to be styled with externalGraphic and rotation, VML fills │ │ │ │ │ + * cannot be used to display the graphic, because rotation of graphic │ │ │ │ │ + * fills is not supported by the VML implementation of Internet Explorer. │ │ │ │ │ + * This method creates a olv:imagedata element inside the VML node, │ │ │ │ │ + * DXImageTransform.Matrix and BasicImage filters for rotation and │ │ │ │ │ + * opacity, and a 3-step hack to remove rendering artefacts from the │ │ │ │ │ + * graphic and preserve the ability of graphics to trigger events. │ │ │ │ │ + * Finally, OpenLayers methods are used to determine the correct │ │ │ │ │ + * insertion point of the rotated image, because DXImageTransform.Matrix │ │ │ │ │ + * does the rotation without the ability to specify a rotation center │ │ │ │ │ + * point. │ │ │ │ │ * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {DOMElement} Reference to a div that contains the drawn popup │ │ │ │ │ + * Parameters: │ │ │ │ │ + * node - {DOMElement} │ │ │ │ │ + * xOffset - {Number} rotation center relative to image, x coordinate │ │ │ │ │ + * yOffset - {Number} rotation center relative to image, y coordinate │ │ │ │ │ + * style - {Object} │ │ │ │ │ */ │ │ │ │ │ - draw: function(px) { │ │ │ │ │ - if (px == null) { │ │ │ │ │ - if ((this.lonlat != null) && (this.map != null)) { │ │ │ │ │ - px = this.map.getLayerPxFromLonLat(this.lonlat); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - │ │ │ │ │ - // this assumes that this.map already exists, which is okay because │ │ │ │ │ - // this.draw is only called once the popup has been added to the map. │ │ │ │ │ - if (this.closeOnMove) { │ │ │ │ │ - this.map.events.register("movestart", this, this.hide); │ │ │ │ │ - } │ │ │ │ │ + graphicRotate: function(node, xOffset, yOffset, style) { │ │ │ │ │ + var style = style || node._style; │ │ │ │ │ + var rotation = style.rotation || 0; │ │ │ │ │ │ │ │ │ │ - //listen to movestart, moveend to disable overflow (FF bug) │ │ │ │ │ - if (!this.disableFirefoxOverflowHack && OpenLayers.BROWSER_NAME == 'firefox') { │ │ │ │ │ - this.map.events.register("movestart", this, function() { │ │ │ │ │ - var style = document.defaultView.getComputedStyle( │ │ │ │ │ - this.contentDiv, null │ │ │ │ │ - ); │ │ │ │ │ - var currentOverflow = style.getPropertyValue("overflow"); │ │ │ │ │ - if (currentOverflow != "hidden") { │ │ │ │ │ - this.contentDiv._oldOverflow = currentOverflow; │ │ │ │ │ - this.contentDiv.style.overflow = "hidden"; │ │ │ │ │ - } │ │ │ │ │ - }); │ │ │ │ │ - this.map.events.register("moveend", this, function() { │ │ │ │ │ - var oldOverflow = this.contentDiv._oldOverflow; │ │ │ │ │ - if (oldOverflow) { │ │ │ │ │ - this.contentDiv.style.overflow = oldOverflow; │ │ │ │ │ - this.contentDiv._oldOverflow = null; │ │ │ │ │ + var aspectRatio, size; │ │ │ │ │ + if (!(style.graphicWidth && style.graphicHeight)) { │ │ │ │ │ + // load the image to determine its size │ │ │ │ │ + var img = new Image(); │ │ │ │ │ + img.onreadystatechange = OpenLayers.Function.bind(function() { │ │ │ │ │ + if (img.readyState == "complete" || │ │ │ │ │ + img.readyState == "interactive") { │ │ │ │ │ + aspectRatio = img.width / img.height; │ │ │ │ │ + size = Math.max(style.pointRadius * 2, │ │ │ │ │ + style.graphicWidth || 0, │ │ │ │ │ + style.graphicHeight || 0); │ │ │ │ │ + xOffset = xOffset * aspectRatio; │ │ │ │ │ + style.graphicWidth = size * aspectRatio; │ │ │ │ │ + style.graphicHeight = size; │ │ │ │ │ + this.graphicRotate(node, xOffset, yOffset, style); │ │ │ │ │ } │ │ │ │ │ - }); │ │ │ │ │ - } │ │ │ │ │ + }, this); │ │ │ │ │ + img.src = style.externalGraphic; │ │ │ │ │ │ │ │ │ │ - this.moveTo(px); │ │ │ │ │ - if (!this.autoSize && !this.size) { │ │ │ │ │ - this.setSize(this.contentSize); │ │ │ │ │ + // will be called again by the onreadystate handler │ │ │ │ │ + return; │ │ │ │ │ + } else { │ │ │ │ │ + size = Math.max(style.graphicWidth, style.graphicHeight); │ │ │ │ │ + aspectRatio = style.graphicWidth / style.graphicHeight; │ │ │ │ │ } │ │ │ │ │ - this.setBackgroundColor(); │ │ │ │ │ - this.setOpacity(); │ │ │ │ │ - this.setBorder(); │ │ │ │ │ - this.setContentHTML(); │ │ │ │ │ │ │ │ │ │ - if (this.panMapIfOutOfView) { │ │ │ │ │ - this.panIntoView(); │ │ │ │ │ + var width = Math.round(style.graphicWidth || size * aspectRatio); │ │ │ │ │ + var height = Math.round(style.graphicHeight || size); │ │ │ │ │ + node.style.width = width + "px"; │ │ │ │ │ + node.style.height = height + "px"; │ │ │ │ │ + │ │ │ │ │ + // Three steps are required to remove artefacts for images with │ │ │ │ │ + // transparent backgrounds (resulting from using DXImageTransform │ │ │ │ │ + // filters on svg objects), while preserving awareness for browser │ │ │ │ │ + // events on images: │ │ │ │ │ + // - Use the fill as usual (like for unrotated images) to handle │ │ │ │ │ + // events │ │ │ │ │ + // - specify an imagedata element with the same src as the fill │ │ │ │ │ + // - style the imagedata element with an AlphaImageLoader filter │ │ │ │ │ + // with empty src │ │ │ │ │ + var image = document.getElementById(node.id + "_image"); │ │ │ │ │ + if (!image) { │ │ │ │ │ + image = this.createNode("olv:imagedata", node.id + "_image"); │ │ │ │ │ + node.appendChild(image); │ │ │ │ │ } │ │ │ │ │ + image.style.width = width + "px"; │ │ │ │ │ + image.style.height = height + "px"; │ │ │ │ │ + image.src = style.externalGraphic; │ │ │ │ │ + image.style.filter = │ │ │ │ │ + "progid:DXImageTransform.Microsoft.AlphaImageLoader(" + │ │ │ │ │ + "src='', sizingMethod='scale')"; │ │ │ │ │ │ │ │ │ │ - return this.div; │ │ │ │ │ - }, │ │ │ │ │ + var rot = rotation * Math.PI / 180; │ │ │ │ │ + var sintheta = Math.sin(rot); │ │ │ │ │ + var costheta = Math.cos(rot); │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: updatePosition │ │ │ │ │ - * if the popup has a lonlat and its map members set, │ │ │ │ │ - * then have it move itself to its proper position │ │ │ │ │ - */ │ │ │ │ │ - updatePosition: function() { │ │ │ │ │ - if ((this.lonlat) && (this.map)) { │ │ │ │ │ - var px = this.map.getLayerPxFromLonLat(this.lonlat); │ │ │ │ │ - if (px) { │ │ │ │ │ - this.moveTo(px); │ │ │ │ │ - } │ │ │ │ │ + // do the rotation on the image │ │ │ │ │ + var filter = │ │ │ │ │ + "progid:DXImageTransform.Microsoft.Matrix(M11=" + costheta + │ │ │ │ │ + ",M12=" + (-sintheta) + ",M21=" + sintheta + ",M22=" + costheta + │ │ │ │ │ + ",SizingMethod='auto expand')\n"; │ │ │ │ │ + │ │ │ │ │ + // set the opacity (needed for the imagedata) │ │ │ │ │ + var opacity = style.graphicOpacity || style.fillOpacity; │ │ │ │ │ + if (opacity && opacity != 1) { │ │ │ │ │ + filter += │ │ │ │ │ + "progid:DXImageTransform.Microsoft.BasicImage(opacity=" + │ │ │ │ │ + opacity + ")\n"; │ │ │ │ │ } │ │ │ │ │ + node.style.filter = filter; │ │ │ │ │ + │ │ │ │ │ + // do the rotation again on a box, so we know the insertion point │ │ │ │ │ + var centerPoint = new OpenLayers.Geometry.Point(-xOffset, -yOffset); │ │ │ │ │ + var imgBox = new OpenLayers.Bounds(0, 0, width, height).toGeometry(); │ │ │ │ │ + imgBox.rotate(style.rotation, centerPoint); │ │ │ │ │ + var imgBounds = imgBox.getBounds(); │ │ │ │ │ + │ │ │ │ │ + node.style.left = Math.round( │ │ │ │ │ + parseInt(node.style.left) + imgBounds.left) + "px"; │ │ │ │ │ + node.style.top = Math.round( │ │ │ │ │ + parseInt(node.style.top) - imgBounds.bottom) + "px"; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: moveTo │ │ │ │ │ + * Method: postDraw │ │ │ │ │ + * Does some node postprocessing to work around browser issues: │ │ │ │ │ + * - Some versions of Internet Explorer seem to be unable to set fillcolor │ │ │ │ │ + * and strokecolor to "none" correctly before the fill node is appended │ │ │ │ │ + * to a visible vml node. This method takes care of that and sets │ │ │ │ │ + * fillcolor and strokecolor again if needed. │ │ │ │ │ + * - In some cases, a node won't become visible after being drawn. Setting │ │ │ │ │ + * style.visibility to "visible" works around that. │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * px - {} the top and left position of the popup div. │ │ │ │ │ + * node - {DOMElement} │ │ │ │ │ */ │ │ │ │ │ - moveTo: function(px) { │ │ │ │ │ - if ((px != null) && (this.div != null)) { │ │ │ │ │ - this.div.style.left = px.x + "px"; │ │ │ │ │ - this.div.style.top = px.y + "px"; │ │ │ │ │ + postDraw: function(node) { │ │ │ │ │ + node.style.visibility = "visible"; │ │ │ │ │ + var fillColor = node._style.fillColor; │ │ │ │ │ + var strokeColor = node._style.strokeColor; │ │ │ │ │ + if (fillColor == "none" && │ │ │ │ │ + node.fillcolor != fillColor) { │ │ │ │ │ + node.fillcolor = fillColor; │ │ │ │ │ + } │ │ │ │ │ + if (strokeColor == "none" && │ │ │ │ │ + node.strokecolor != strokeColor) { │ │ │ │ │ + node.strokecolor = strokeColor; │ │ │ │ │ } │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: visible │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Boolean} Boolean indicating whether or not the popup is visible │ │ │ │ │ - */ │ │ │ │ │ - visible: function() { │ │ │ │ │ - return OpenLayers.Element.visible(this.div); │ │ │ │ │ - }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: toggle │ │ │ │ │ - * Toggles visibility of the popup. │ │ │ │ │ + * Method: setNodeDimension │ │ │ │ │ + * Get the geometry's bounds, convert it to our vml coordinate system, │ │ │ │ │ + * then set the node's position, size, and local coordinate system. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * node - {DOMElement} │ │ │ │ │ + * geometry - {} │ │ │ │ │ */ │ │ │ │ │ - toggle: function() { │ │ │ │ │ - if (this.visible()) { │ │ │ │ │ - this.hide(); │ │ │ │ │ - } else { │ │ │ │ │ - this.show(); │ │ │ │ │ - } │ │ │ │ │ - }, │ │ │ │ │ + setNodeDimension: function(node, geometry) { │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: show │ │ │ │ │ - * Makes the popup visible. │ │ │ │ │ - */ │ │ │ │ │ - show: function() { │ │ │ │ │ - this.div.style.display = ''; │ │ │ │ │ + var bbox = geometry.getBounds(); │ │ │ │ │ + if (bbox) { │ │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ │ │ │ │ │ │ - if (this.panMapIfOutOfView) { │ │ │ │ │ - this.panIntoView(); │ │ │ │ │ + var scaledBox = │ │ │ │ │ + new OpenLayers.Bounds(((bbox.left - this.featureDx) / resolution - this.offset.x) | 0, │ │ │ │ │ + (bbox.bottom / resolution - this.offset.y) | 0, │ │ │ │ │ + ((bbox.right - this.featureDx) / resolution - this.offset.x) | 0, │ │ │ │ │ + (bbox.top / resolution - this.offset.y) | 0); │ │ │ │ │ + │ │ │ │ │ + // Set the internal coordinate system to draw the path │ │ │ │ │ + node.style.left = scaledBox.left + "px"; │ │ │ │ │ + node.style.top = scaledBox.top + "px"; │ │ │ │ │ + node.style.width = scaledBox.getWidth() + "px"; │ │ │ │ │ + node.style.height = scaledBox.getHeight() + "px"; │ │ │ │ │ + │ │ │ │ │ + node.coordorigin = scaledBox.left + " " + scaledBox.top; │ │ │ │ │ + node.coordsize = scaledBox.getWidth() + " " + scaledBox.getHeight(); │ │ │ │ │ } │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: hide │ │ │ │ │ - * Makes the popup invisible. │ │ │ │ │ + /** │ │ │ │ │ + * Method: dashStyle │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * style - {Object} │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {String} A VML compliant 'stroke-dasharray' value │ │ │ │ │ */ │ │ │ │ │ - hide: function() { │ │ │ │ │ - this.div.style.display = 'none'; │ │ │ │ │ + dashStyle: function(style) { │ │ │ │ │ + var dash = style.strokeDashstyle; │ │ │ │ │ + switch (dash) { │ │ │ │ │ + case 'solid': │ │ │ │ │ + case 'dot': │ │ │ │ │ + case 'dash': │ │ │ │ │ + case 'dashdot': │ │ │ │ │ + case 'longdash': │ │ │ │ │ + case 'longdashdot': │ │ │ │ │ + return dash; │ │ │ │ │ + default: │ │ │ │ │ + // very basic guessing of dash style patterns │ │ │ │ │ + var parts = dash.split(/[ ,]/); │ │ │ │ │ + if (parts.length == 2) { │ │ │ │ │ + if (1 * parts[0] >= 2 * parts[1]) { │ │ │ │ │ + return "longdash"; │ │ │ │ │ + } │ │ │ │ │ + return (parts[0] == 1 || parts[1] == 1) ? "dot" : "dash"; │ │ │ │ │ + } else if (parts.length == 4) { │ │ │ │ │ + return (1 * parts[0] >= 2 * parts[1]) ? "longdashdot" : │ │ │ │ │ + "dashdot"; │ │ │ │ │ + } │ │ │ │ │ + return "solid"; │ │ │ │ │ + } │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: setSize │ │ │ │ │ - * Used to adjust the size of the popup. │ │ │ │ │ + * Method: createNode │ │ │ │ │ + * Create a new node │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * contentSize - {} the new size for the popup's │ │ │ │ │ - * contents div (in pixels). │ │ │ │ │ + * type - {String} Kind of node to draw │ │ │ │ │ + * id - {String} Id for node │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {DOMElement} A new node of the given type and id │ │ │ │ │ */ │ │ │ │ │ - setSize: function(contentSize) { │ │ │ │ │ - this.size = contentSize.clone(); │ │ │ │ │ - │ │ │ │ │ - // if our contentDiv has a css 'padding' set on it by a stylesheet, we │ │ │ │ │ - // must add that to the desired "size". │ │ │ │ │ - var contentDivPadding = this.getContentDivPadding(); │ │ │ │ │ - var wPadding = contentDivPadding.left + contentDivPadding.right; │ │ │ │ │ - var hPadding = contentDivPadding.top + contentDivPadding.bottom; │ │ │ │ │ - │ │ │ │ │ - // take into account the popup's 'padding' property │ │ │ │ │ - this.fixPadding(); │ │ │ │ │ - wPadding += this.padding.left + this.padding.right; │ │ │ │ │ - hPadding += this.padding.top + this.padding.bottom; │ │ │ │ │ - │ │ │ │ │ - // make extra space for the close div │ │ │ │ │ - if (this.closeDiv) { │ │ │ │ │ - var closeDivWidth = parseInt(this.closeDiv.style.width); │ │ │ │ │ - wPadding += closeDivWidth + contentDivPadding.right; │ │ │ │ │ + createNode: function(type, id) { │ │ │ │ │ + var node = document.createElement(type); │ │ │ │ │ + if (id) { │ │ │ │ │ + node.id = id; │ │ │ │ │ } │ │ │ │ │ │ │ │ │ │ - //increase size of the main popup div to take into account the │ │ │ │ │ - // users's desired padding and close div. │ │ │ │ │ - this.size.w += wPadding; │ │ │ │ │ - this.size.h += hPadding; │ │ │ │ │ - │ │ │ │ │ - //now if our browser is IE, we need to actually make the contents │ │ │ │ │ - // div itself bigger to take its own padding into effect. this makes │ │ │ │ │ - // me want to shoot someone, but so it goes. │ │ │ │ │ - if (OpenLayers.BROWSER_NAME == "msie") { │ │ │ │ │ - this.contentSize.w += │ │ │ │ │ - contentDivPadding.left + contentDivPadding.right; │ │ │ │ │ - this.contentSize.h += │ │ │ │ │ - contentDivPadding.bottom + contentDivPadding.top; │ │ │ │ │ - } │ │ │ │ │ + // IE hack to make elements unselectable, to prevent 'blue flash' │ │ │ │ │ + // while dragging vectors; #1410 │ │ │ │ │ + node.unselectable = 'on'; │ │ │ │ │ + node.onselectstart = OpenLayers.Function.False; │ │ │ │ │ │ │ │ │ │ - if (this.div != null) { │ │ │ │ │ - this.div.style.width = this.size.w + "px"; │ │ │ │ │ - this.div.style.height = this.size.h + "px"; │ │ │ │ │ - } │ │ │ │ │ - if (this.contentDiv != null) { │ │ │ │ │ - this.contentDiv.style.width = contentSize.w + "px"; │ │ │ │ │ - this.contentDiv.style.height = contentSize.h + "px"; │ │ │ │ │ - } │ │ │ │ │ + return node; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: updateSize │ │ │ │ │ - * Auto size the popup so that it precisely fits its contents (as │ │ │ │ │ - * determined by this.contentDiv.innerHTML). Popup size will, of │ │ │ │ │ - * course, be limited by the available space on the current map │ │ │ │ │ + * Method: nodeTypeCompare │ │ │ │ │ + * Determine whether a node is of a given type │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * node - {DOMElement} An VML element │ │ │ │ │ + * type - {String} Kind of node │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Boolean} Whether or not the specified node is of the specified type │ │ │ │ │ */ │ │ │ │ │ - updateSize: function() { │ │ │ │ │ - │ │ │ │ │ - // determine actual render dimensions of the contents by putting its │ │ │ │ │ - // contents into a fake contentDiv (for the CSS) and then measuring it │ │ │ │ │ - var preparedHTML = "
" + │ │ │ │ │ - this.contentDiv.innerHTML + │ │ │ │ │ - "
"; │ │ │ │ │ - │ │ │ │ │ - var containerElement = (this.map) ? this.map.div : document.body; │ │ │ │ │ - var realSize = OpenLayers.Util.getRenderedDimensions( │ │ │ │ │ - preparedHTML, null, { │ │ │ │ │ - displayClass: this.displayClass, │ │ │ │ │ - containerElement: containerElement │ │ │ │ │ - } │ │ │ │ │ - ); │ │ │ │ │ - │ │ │ │ │ - // is the "real" size of the div is safe to display in our map? │ │ │ │ │ - var safeSize = this.getSafeContentSize(realSize); │ │ │ │ │ - │ │ │ │ │ - var newSize = null; │ │ │ │ │ - if (safeSize.equals(realSize)) { │ │ │ │ │ - //real size of content is small enough to fit on the map, │ │ │ │ │ - // so we use real size. │ │ │ │ │ - newSize = realSize; │ │ │ │ │ - │ │ │ │ │ - } else { │ │ │ │ │ - │ │ │ │ │ - // make a new 'size' object with the clipped dimensions │ │ │ │ │ - // set or null if not clipped. │ │ │ │ │ - var fixedSize = { │ │ │ │ │ - w: (safeSize.w < realSize.w) ? safeSize.w : null, │ │ │ │ │ - h: (safeSize.h < realSize.h) ? safeSize.h : null │ │ │ │ │ - }; │ │ │ │ │ - │ │ │ │ │ - if (fixedSize.w && fixedSize.h) { │ │ │ │ │ - //content is too big in both directions, so we will use │ │ │ │ │ - // max popup size (safeSize), knowing well that it will │ │ │ │ │ - // overflow both ways. │ │ │ │ │ - newSize = safeSize; │ │ │ │ │ - } else { │ │ │ │ │ - //content is clipped in only one direction, so we need to │ │ │ │ │ - // run getRenderedDimensions() again with a fixed dimension │ │ │ │ │ - var clippedSize = OpenLayers.Util.getRenderedDimensions( │ │ │ │ │ - preparedHTML, fixedSize, { │ │ │ │ │ - displayClass: this.contentDisplayClass, │ │ │ │ │ - containerElement: containerElement │ │ │ │ │ - } │ │ │ │ │ - ); │ │ │ │ │ + nodeTypeCompare: function(node, type) { │ │ │ │ │ │ │ │ │ │ - //if the clipped size is still the same as the safeSize, │ │ │ │ │ - // that means that our content must be fixed in the │ │ │ │ │ - // offending direction. If overflow is 'auto', this means │ │ │ │ │ - // we are going to have a scrollbar for sure, so we must │ │ │ │ │ - // adjust for that. │ │ │ │ │ - // │ │ │ │ │ - var currentOverflow = OpenLayers.Element.getStyle( │ │ │ │ │ - this.contentDiv, "overflow" │ │ │ │ │ - ); │ │ │ │ │ - if ((currentOverflow != "hidden") && │ │ │ │ │ - (clippedSize.equals(safeSize))) { │ │ │ │ │ - var scrollBar = OpenLayers.Util.getScrollbarWidth(); │ │ │ │ │ - if (fixedSize.w) { │ │ │ │ │ - clippedSize.h += scrollBar; │ │ │ │ │ - } else { │ │ │ │ │ - clippedSize.w += scrollBar; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ + //split type │ │ │ │ │ + var subType = type; │ │ │ │ │ + var splitIndex = subType.indexOf(":"); │ │ │ │ │ + if (splitIndex != -1) { │ │ │ │ │ + subType = subType.substr(splitIndex + 1); │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ - newSize = this.getSafeContentSize(clippedSize); │ │ │ │ │ - } │ │ │ │ │ + //split nodeName │ │ │ │ │ + var nodeName = node.nodeName; │ │ │ │ │ + splitIndex = nodeName.indexOf(":"); │ │ │ │ │ + if (splitIndex != -1) { │ │ │ │ │ + nodeName = nodeName.substr(splitIndex + 1); │ │ │ │ │ } │ │ │ │ │ - this.setSize(newSize); │ │ │ │ │ + │ │ │ │ │ + return (subType == nodeName); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: setBackgroundColor │ │ │ │ │ - * Sets the background color of the popup. │ │ │ │ │ + * Method: createRenderRoot │ │ │ │ │ + * Create the renderer root │ │ │ │ │ * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * color - {String} the background color. eg "#FFBBBB" │ │ │ │ │ + * Returns: │ │ │ │ │ + * {DOMElement} The specific render engine's root element │ │ │ │ │ */ │ │ │ │ │ - setBackgroundColor: function(color) { │ │ │ │ │ - if (color != undefined) { │ │ │ │ │ - this.backgroundColor = color; │ │ │ │ │ - } │ │ │ │ │ - │ │ │ │ │ - if (this.div != null) { │ │ │ │ │ - this.div.style.backgroundColor = this.backgroundColor; │ │ │ │ │ - } │ │ │ │ │ + createRenderRoot: function() { │ │ │ │ │ + return this.nodeFactory(this.container.id + "_vmlRoot", "div"); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: setOpacity │ │ │ │ │ - * Sets the opacity of the popup. │ │ │ │ │ + * Method: createRoot │ │ │ │ │ + * Create the main root element │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * opacity - {float} A value between 0.0 (transparent) and 1.0 (solid). │ │ │ │ │ + * suffix - {String} suffix to append to the id │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {DOMElement} │ │ │ │ │ */ │ │ │ │ │ - setOpacity: function(opacity) { │ │ │ │ │ - if (opacity != undefined) { │ │ │ │ │ - this.opacity = opacity; │ │ │ │ │ - } │ │ │ │ │ - │ │ │ │ │ - if (this.div != null) { │ │ │ │ │ - // for Mozilla and Safari │ │ │ │ │ - this.div.style.opacity = this.opacity; │ │ │ │ │ - │ │ │ │ │ - // for IE │ │ │ │ │ - this.div.style.filter = 'alpha(opacity=' + this.opacity * 100 + ')'; │ │ │ │ │ - } │ │ │ │ │ + createRoot: function(suffix) { │ │ │ │ │ + return this.nodeFactory(this.container.id + suffix, "olv:group"); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ + /************************************** │ │ │ │ │ + * * │ │ │ │ │ + * GEOMETRY DRAWING FUNCTIONS * │ │ │ │ │ + * * │ │ │ │ │ + **************************************/ │ │ │ │ │ + │ │ │ │ │ /** │ │ │ │ │ - * Method: setBorder │ │ │ │ │ - * Sets the border style of the popup. │ │ │ │ │ - * │ │ │ │ │ + * Method: drawPoint │ │ │ │ │ + * Render a point │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * border - {String} The border style value. eg 2px │ │ │ │ │ + * node - {DOMElement} │ │ │ │ │ + * geometry - {} │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {DOMElement} or false if the point could not be drawn │ │ │ │ │ */ │ │ │ │ │ - setBorder: function(border) { │ │ │ │ │ - if (border != undefined) { │ │ │ │ │ - this.border = border; │ │ │ │ │ - } │ │ │ │ │ - │ │ │ │ │ - if (this.div != null) { │ │ │ │ │ - this.div.style.border = this.border; │ │ │ │ │ - } │ │ │ │ │ + drawPoint: function(node, geometry) { │ │ │ │ │ + return this.drawCircle(node, geometry, 1); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: setContentHTML │ │ │ │ │ - * Allows the user to set the HTML content of the popup. │ │ │ │ │ - * │ │ │ │ │ + * Method: drawCircle │ │ │ │ │ + * Render a circle. │ │ │ │ │ + * Size and Center a circle given geometry (x,y center) and radius │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * contentHTML - {String} HTML for the div. │ │ │ │ │ + * node - {DOMElement} │ │ │ │ │ + * geometry - {} │ │ │ │ │ + * radius - {float} │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {DOMElement} or false if the circle could not ne drawn │ │ │ │ │ */ │ │ │ │ │ - setContentHTML: function(contentHTML) { │ │ │ │ │ - │ │ │ │ │ - if (contentHTML != null) { │ │ │ │ │ - this.contentHTML = contentHTML; │ │ │ │ │ - } │ │ │ │ │ - │ │ │ │ │ - if ((this.contentDiv != null) && │ │ │ │ │ - (this.contentHTML != null) && │ │ │ │ │ - (this.contentHTML != this.contentDiv.innerHTML)) { │ │ │ │ │ - │ │ │ │ │ - this.contentDiv.innerHTML = this.contentHTML; │ │ │ │ │ + drawCircle: function(node, geometry, radius) { │ │ │ │ │ + if (!isNaN(geometry.x) && !isNaN(geometry.y)) { │ │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ │ │ │ │ │ │ - if (this.autoSize) { │ │ │ │ │ + node.style.left = ((((geometry.x - this.featureDx) / resolution - this.offset.x) | 0) - radius) + "px"; │ │ │ │ │ + node.style.top = (((geometry.y / resolution - this.offset.y) | 0) - radius) + "px"; │ │ │ │ │ │ │ │ │ │ - //if popup has images, listen for when they finish │ │ │ │ │ - // loading and resize accordingly │ │ │ │ │ - this.registerImageListeners(); │ │ │ │ │ + var diameter = radius * 2; │ │ │ │ │ │ │ │ │ │ - //auto size the popup to its current contents │ │ │ │ │ - this.updateSize(); │ │ │ │ │ - } │ │ │ │ │ + node.style.width = diameter + "px"; │ │ │ │ │ + node.style.height = diameter + "px"; │ │ │ │ │ + return node; │ │ │ │ │ } │ │ │ │ │ - │ │ │ │ │ + return false; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ + │ │ │ │ │ /** │ │ │ │ │ - * Method: registerImageListeners │ │ │ │ │ - * Called when an image contained by the popup loaded. this function │ │ │ │ │ - * updates the popup size, then unregisters the image load listener. │ │ │ │ │ + * Method: drawLineString │ │ │ │ │ + * Render a linestring. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * node - {DOMElement} │ │ │ │ │ + * geometry - {} │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {DOMElement} │ │ │ │ │ */ │ │ │ │ │ - registerImageListeners: function() { │ │ │ │ │ - │ │ │ │ │ - // As the images load, this function will call updateSize() to │ │ │ │ │ - // resize the popup to fit the content div (which presumably is now │ │ │ │ │ - // bigger than when the image was not loaded). │ │ │ │ │ - // │ │ │ │ │ - // If the 'panMapIfOutOfView' property is set, we will pan the newly │ │ │ │ │ - // resized popup back into view. │ │ │ │ │ - // │ │ │ │ │ - // Note that this function, when called, will have 'popup' and │ │ │ │ │ - // 'img' properties in the context. │ │ │ │ │ - // │ │ │ │ │ - var onImgLoad = function() { │ │ │ │ │ - if (this.popup.id === null) { // this.popup has been destroyed! │ │ │ │ │ - return; │ │ │ │ │ - } │ │ │ │ │ - this.popup.updateSize(); │ │ │ │ │ - │ │ │ │ │ - if (this.popup.visible() && this.popup.panMapIfOutOfView) { │ │ │ │ │ - this.popup.panIntoView(); │ │ │ │ │ - } │ │ │ │ │ - │ │ │ │ │ - OpenLayers.Event.stopObserving( │ │ │ │ │ - this.img, "load", this.img._onImgLoad │ │ │ │ │ - ); │ │ │ │ │ - │ │ │ │ │ - }; │ │ │ │ │ - │ │ │ │ │ - //cycle through the images and if their size is 0x0, that means that │ │ │ │ │ - // they haven't been loaded yet, so we attach the listener, which │ │ │ │ │ - // will fire when the images finish loading and will resize the │ │ │ │ │ - // popup accordingly to its new size. │ │ │ │ │ - var images = this.contentDiv.getElementsByTagName("img"); │ │ │ │ │ - for (var i = 0, len = images.length; i < len; i++) { │ │ │ │ │ - var img = images[i]; │ │ │ │ │ - if (img.width == 0 || img.height == 0) { │ │ │ │ │ - │ │ │ │ │ - var context = { │ │ │ │ │ - 'popup': this, │ │ │ │ │ - 'img': img │ │ │ │ │ - }; │ │ │ │ │ - │ │ │ │ │ - //expando this function to the image itself before registering │ │ │ │ │ - // it. This way we can easily and properly unregister it. │ │ │ │ │ - img._onImgLoad = OpenLayers.Function.bind(onImgLoad, context); │ │ │ │ │ - │ │ │ │ │ - OpenLayers.Event.observe(img, 'load', img._onImgLoad); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ + drawLineString: function(node, geometry) { │ │ │ │ │ + return this.drawLine(node, geometry, false); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: getSafeContentSize │ │ │ │ │ + * Method: drawLinearRing │ │ │ │ │ + * Render a linearring │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * size - {} Desired size to make the popup. │ │ │ │ │ + * node - {DOMElement} │ │ │ │ │ + * geometry - {} │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {} A size to make the popup which is neither smaller │ │ │ │ │ - * than the specified minimum size, nor bigger than the maximum │ │ │ │ │ - * size (which is calculated relative to the size of the viewport). │ │ │ │ │ + * {DOMElement} │ │ │ │ │ */ │ │ │ │ │ - getSafeContentSize: function(size) { │ │ │ │ │ - │ │ │ │ │ - var safeContentSize = size.clone(); │ │ │ │ │ - │ │ │ │ │ - // if our contentDiv has a css 'padding' set on it by a stylesheet, we │ │ │ │ │ - // must add that to the desired "size". │ │ │ │ │ - var contentDivPadding = this.getContentDivPadding(); │ │ │ │ │ - var wPadding = contentDivPadding.left + contentDivPadding.right; │ │ │ │ │ - var hPadding = contentDivPadding.top + contentDivPadding.bottom; │ │ │ │ │ - │ │ │ │ │ - // take into account the popup's 'padding' property │ │ │ │ │ - this.fixPadding(); │ │ │ │ │ - wPadding += this.padding.left + this.padding.right; │ │ │ │ │ - hPadding += this.padding.top + this.padding.bottom; │ │ │ │ │ - │ │ │ │ │ - if (this.closeDiv) { │ │ │ │ │ - var closeDivWidth = parseInt(this.closeDiv.style.width); │ │ │ │ │ - wPadding += closeDivWidth + contentDivPadding.right; │ │ │ │ │ - } │ │ │ │ │ - │ │ │ │ │ - // prevent the popup from being smaller than a specified minimal size │ │ │ │ │ - if (this.minSize) { │ │ │ │ │ - safeContentSize.w = Math.max(safeContentSize.w, │ │ │ │ │ - (this.minSize.w - wPadding)); │ │ │ │ │ - safeContentSize.h = Math.max(safeContentSize.h, │ │ │ │ │ - (this.minSize.h - hPadding)); │ │ │ │ │ - } │ │ │ │ │ - │ │ │ │ │ - // prevent the popup from being bigger than a specified maximum size │ │ │ │ │ - if (this.maxSize) { │ │ │ │ │ - safeContentSize.w = Math.min(safeContentSize.w, │ │ │ │ │ - (this.maxSize.w - wPadding)); │ │ │ │ │ - safeContentSize.h = Math.min(safeContentSize.h, │ │ │ │ │ - (this.maxSize.h - hPadding)); │ │ │ │ │ - } │ │ │ │ │ - │ │ │ │ │ - //make sure the desired size to set doesn't result in a popup that │ │ │ │ │ - // is bigger than the map's viewport. │ │ │ │ │ - // │ │ │ │ │ - if (this.map && this.map.size) { │ │ │ │ │ + drawLinearRing: function(node, geometry) { │ │ │ │ │ + return this.drawLine(node, geometry, true); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - var extraX = 0, │ │ │ │ │ - extraY = 0; │ │ │ │ │ - if (this.keepInMap && !this.panMapIfOutOfView) { │ │ │ │ │ - var px = this.map.getPixelFromLonLat(this.lonlat); │ │ │ │ │ - switch (this.relativePosition) { │ │ │ │ │ - case "tr": │ │ │ │ │ - extraX = px.x; │ │ │ │ │ - extraY = this.map.size.h - px.y; │ │ │ │ │ - break; │ │ │ │ │ - case "tl": │ │ │ │ │ - extraX = this.map.size.w - px.x; │ │ │ │ │ - extraY = this.map.size.h - px.y; │ │ │ │ │ - break; │ │ │ │ │ - case "bl": │ │ │ │ │ - extraX = this.map.size.w - px.x; │ │ │ │ │ - extraY = px.y; │ │ │ │ │ - break; │ │ │ │ │ - case "br": │ │ │ │ │ - extraX = px.x; │ │ │ │ │ - extraY = px.y; │ │ │ │ │ - break; │ │ │ │ │ - default: │ │ │ │ │ - extraX = px.x; │ │ │ │ │ - extraY = this.map.size.h - px.y; │ │ │ │ │ - break; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ + /** │ │ │ │ │ + * Method: DrawLine │ │ │ │ │ + * Render a line. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * node - {DOMElement} │ │ │ │ │ + * geometry - {} │ │ │ │ │ + * closeLine - {Boolean} Close the line? (make it a ring?) │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {DOMElement} │ │ │ │ │ + */ │ │ │ │ │ + drawLine: function(node, geometry, closeLine) { │ │ │ │ │ │ │ │ │ │ - var maxY = this.map.size.h - │ │ │ │ │ - this.map.paddingForPopups.top - │ │ │ │ │ - this.map.paddingForPopups.bottom - │ │ │ │ │ - hPadding - extraY; │ │ │ │ │ + this.setNodeDimension(node, geometry); │ │ │ │ │ │ │ │ │ │ - var maxX = this.map.size.w - │ │ │ │ │ - this.map.paddingForPopups.left - │ │ │ │ │ - this.map.paddingForPopups.right - │ │ │ │ │ - wPadding - extraX; │ │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ │ + var numComponents = geometry.components.length; │ │ │ │ │ + var parts = new Array(numComponents); │ │ │ │ │ │ │ │ │ │ - safeContentSize.w = Math.min(safeContentSize.w, maxX); │ │ │ │ │ - safeContentSize.h = Math.min(safeContentSize.h, maxY); │ │ │ │ │ + var comp, x, y; │ │ │ │ │ + for (var i = 0; i < numComponents; i++) { │ │ │ │ │ + comp = geometry.components[i]; │ │ │ │ │ + x = ((comp.x - this.featureDx) / resolution - this.offset.x) | 0; │ │ │ │ │ + y = (comp.y / resolution - this.offset.y) | 0; │ │ │ │ │ + parts[i] = " " + x + "," + y + " l "; │ │ │ │ │ } │ │ │ │ │ - │ │ │ │ │ - return safeContentSize; │ │ │ │ │ + var end = (closeLine) ? " x e" : " e"; │ │ │ │ │ + node.path = "m" + parts.join("") + end; │ │ │ │ │ + return node; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: getContentDivPadding │ │ │ │ │ - * Glorious, oh glorious hack in order to determine the css 'padding' of │ │ │ │ │ - * the contentDiv. IE/Opera return null here unless we actually add the │ │ │ │ │ - * popup's main 'div' element (which contains contentDiv) to the DOM. │ │ │ │ │ - * So we make it invisible and then add it to the document temporarily. │ │ │ │ │ - * │ │ │ │ │ - * Once we've taken the padding readings we need, we then remove it │ │ │ │ │ - * from the DOM (it will actually get added to the DOM in │ │ │ │ │ - * Map.js's addPopup) │ │ │ │ │ - * │ │ │ │ │ + * Method: drawPolygon │ │ │ │ │ + * Render a polygon │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * node - {DOMElement} │ │ │ │ │ + * geometry - {} │ │ │ │ │ + * │ │ │ │ │ * Returns: │ │ │ │ │ - * {} │ │ │ │ │ + * {DOMElement} │ │ │ │ │ */ │ │ │ │ │ - getContentDivPadding: function() { │ │ │ │ │ - │ │ │ │ │ - //use cached value if we have it │ │ │ │ │ - var contentDivPadding = this._contentDivPadding; │ │ │ │ │ - if (!contentDivPadding) { │ │ │ │ │ - │ │ │ │ │ - if (this.div.parentNode == null) { │ │ │ │ │ - //make the div invisible and add it to the page │ │ │ │ │ - this.div.style.display = "none"; │ │ │ │ │ - document.body.appendChild(this.div); │ │ │ │ │ - } │ │ │ │ │ - │ │ │ │ │ - //read the padding settings from css, put them in an OL.Bounds │ │ │ │ │ - contentDivPadding = new OpenLayers.Bounds( │ │ │ │ │ - OpenLayers.Element.getStyle(this.contentDiv, "padding-left"), │ │ │ │ │ - OpenLayers.Element.getStyle(this.contentDiv, "padding-bottom"), │ │ │ │ │ - OpenLayers.Element.getStyle(this.contentDiv, "padding-right"), │ │ │ │ │ - OpenLayers.Element.getStyle(this.contentDiv, "padding-top") │ │ │ │ │ - ); │ │ │ │ │ + drawPolygon: function(node, geometry) { │ │ │ │ │ + this.setNodeDimension(node, geometry); │ │ │ │ │ │ │ │ │ │ - //cache the value │ │ │ │ │ - this._contentDivPadding = contentDivPadding; │ │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ │ │ │ │ │ │ - if (this.div.parentNode == document.body) { │ │ │ │ │ - //remove the div from the page and make it visible again │ │ │ │ │ - document.body.removeChild(this.div); │ │ │ │ │ - this.div.style.display = ""; │ │ │ │ │ + var path = []; │ │ │ │ │ + var j, jj, points, area, first, second, i, ii, comp, pathComp, x, y; │ │ │ │ │ + for (j = 0, jj = geometry.components.length; j < jj; j++) { │ │ │ │ │ + path.push("m"); │ │ │ │ │ + points = geometry.components[j].components; │ │ │ │ │ + // we only close paths of interior rings with area │ │ │ │ │ + area = (j === 0); │ │ │ │ │ + first = null; │ │ │ │ │ + second = null; │ │ │ │ │ + for (i = 0, ii = points.length; i < ii; i++) { │ │ │ │ │ + comp = points[i]; │ │ │ │ │ + x = ((comp.x - this.featureDx) / resolution - this.offset.x) | 0; │ │ │ │ │ + y = (comp.y / resolution - this.offset.y) | 0; │ │ │ │ │ + pathComp = " " + x + "," + y; │ │ │ │ │ + path.push(pathComp); │ │ │ │ │ + if (i == 0) { │ │ │ │ │ + path.push(" l"); │ │ │ │ │ + } │ │ │ │ │ + if (!area) { │ │ │ │ │ + // IE improperly renders sub-paths that have no area. │ │ │ │ │ + // Instead of checking the area of every ring, we confirm │ │ │ │ │ + // the ring has at least three distinct points. This does │ │ │ │ │ + // not catch all non-zero area cases, but it greatly improves │ │ │ │ │ + // interior ring digitizing and is a minor performance hit │ │ │ │ │ + // when rendering rings with many points. │ │ │ │ │ + if (!first) { │ │ │ │ │ + first = pathComp; │ │ │ │ │ + } else if (first != pathComp) { │ │ │ │ │ + if (!second) { │ │ │ │ │ + second = pathComp; │ │ │ │ │ + } else if (second != pathComp) { │ │ │ │ │ + // stop looking │ │ │ │ │ + area = true; │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ } │ │ │ │ │ + path.push(area ? " x " : " "); │ │ │ │ │ } │ │ │ │ │ - return contentDivPadding; │ │ │ │ │ + path.push("e"); │ │ │ │ │ + node.path = path.join(""); │ │ │ │ │ + return node; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: addCloseBox │ │ │ │ │ + * Method: drawRectangle │ │ │ │ │ + * Render a rectangle │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * callback - {Function} The callback to be called when the close button │ │ │ │ │ - * is clicked. │ │ │ │ │ + * node - {DOMElement} │ │ │ │ │ + * geometry - {} │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {DOMElement} │ │ │ │ │ */ │ │ │ │ │ - addCloseBox: function(callback) { │ │ │ │ │ - │ │ │ │ │ - this.closeDiv = OpenLayers.Util.createDiv( │ │ │ │ │ - this.id + "_close", null, { │ │ │ │ │ - w: 17, │ │ │ │ │ - h: 17 │ │ │ │ │ - } │ │ │ │ │ - ); │ │ │ │ │ - this.closeDiv.className = "olPopupCloseBox"; │ │ │ │ │ - │ │ │ │ │ - // use the content div's css padding to determine if we should │ │ │ │ │ - // padd the close div │ │ │ │ │ - var contentDivPadding = this.getContentDivPadding(); │ │ │ │ │ + drawRectangle: function(node, geometry) { │ │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ │ │ │ │ │ │ - this.closeDiv.style.right = contentDivPadding.right + "px"; │ │ │ │ │ - this.closeDiv.style.top = contentDivPadding.top + "px"; │ │ │ │ │ - this.groupDiv.appendChild(this.closeDiv); │ │ │ │ │ + node.style.left = (((geometry.x - this.featureDx) / resolution - this.offset.x) | 0) + "px"; │ │ │ │ │ + node.style.top = ((geometry.y / resolution - this.offset.y) | 0) + "px"; │ │ │ │ │ + node.style.width = ((geometry.width / resolution) | 0) + "px"; │ │ │ │ │ + node.style.height = ((geometry.height / resolution) | 0) + "px"; │ │ │ │ │ │ │ │ │ │ - var closePopup = callback || function(e) { │ │ │ │ │ - this.hide(); │ │ │ │ │ - OpenLayers.Event.stop(e); │ │ │ │ │ - }; │ │ │ │ │ - OpenLayers.Event.observe(this.closeDiv, "touchend", │ │ │ │ │ - OpenLayers.Function.bindAsEventListener(closePopup, this)); │ │ │ │ │ - OpenLayers.Event.observe(this.closeDiv, "click", │ │ │ │ │ - OpenLayers.Function.bindAsEventListener(closePopup, this)); │ │ │ │ │ + return node; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: panIntoView │ │ │ │ │ - * Pans the map such that the popup is totaly viewable (if necessary) │ │ │ │ │ + * Method: drawText │ │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * featureId - {String} │ │ │ │ │ + * style - │ │ │ │ │ + * location - {} │ │ │ │ │ */ │ │ │ │ │ - panIntoView: function() { │ │ │ │ │ + drawText: function(featureId, style, location) { │ │ │ │ │ + var label = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX, "olv:rect"); │ │ │ │ │ + var textbox = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX + "_textbox", "olv:textbox"); │ │ │ │ │ │ │ │ │ │ - var mapSize = this.map.getSize(); │ │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ │ + label.style.left = (((location.x - this.featureDx) / resolution - this.offset.x) | 0) + "px"; │ │ │ │ │ + label.style.top = ((location.y / resolution - this.offset.y) | 0) + "px"; │ │ │ │ │ + label.style.flip = "y"; │ │ │ │ │ │ │ │ │ │ - //start with the top left corner of the popup, in px, │ │ │ │ │ - // relative to the viewport │ │ │ │ │ - var origTL = this.map.getViewPortPxFromLayerPx(new OpenLayers.Pixel( │ │ │ │ │ - parseInt(this.div.style.left), │ │ │ │ │ - parseInt(this.div.style.top) │ │ │ │ │ - )); │ │ │ │ │ - var newTL = origTL.clone(); │ │ │ │ │ + textbox.innerText = style.label; │ │ │ │ │ │ │ │ │ │ - //new left (compare to margins, using this.size to calculate right) │ │ │ │ │ - if (origTL.x < this.map.paddingForPopups.left) { │ │ │ │ │ - newTL.x = this.map.paddingForPopups.left; │ │ │ │ │ - } else │ │ │ │ │ - if ((origTL.x + this.size.w) > (mapSize.w - this.map.paddingForPopups.right)) { │ │ │ │ │ - newTL.x = mapSize.w - this.map.paddingForPopups.right - this.size.w; │ │ │ │ │ + if (style.cursor != "inherit" && style.cursor != null) { │ │ │ │ │ + textbox.style.cursor = style.cursor; │ │ │ │ │ + } │ │ │ │ │ + if (style.fontColor) { │ │ │ │ │ + textbox.style.color = style.fontColor; │ │ │ │ │ + } │ │ │ │ │ + if (style.fontOpacity) { │ │ │ │ │ + textbox.style.filter = 'alpha(opacity=' + (style.fontOpacity * 100) + ')'; │ │ │ │ │ + } │ │ │ │ │ + if (style.fontFamily) { │ │ │ │ │ + textbox.style.fontFamily = style.fontFamily; │ │ │ │ │ + } │ │ │ │ │ + if (style.fontSize) { │ │ │ │ │ + textbox.style.fontSize = style.fontSize; │ │ │ │ │ + } │ │ │ │ │ + if (style.fontWeight) { │ │ │ │ │ + textbox.style.fontWeight = style.fontWeight; │ │ │ │ │ + } │ │ │ │ │ + if (style.fontStyle) { │ │ │ │ │ + textbox.style.fontStyle = style.fontStyle; │ │ │ │ │ + } │ │ │ │ │ + if (style.labelSelect === true) { │ │ │ │ │ + label._featureId = featureId; │ │ │ │ │ + textbox._featureId = featureId; │ │ │ │ │ + textbox._geometry = location; │ │ │ │ │ + textbox._geometryClass = location.CLASS_NAME; │ │ │ │ │ } │ │ │ │ │ + textbox.style.whiteSpace = "nowrap"; │ │ │ │ │ + // fun with IE: IE7 in standards compliant mode does not display any │ │ │ │ │ + // text with a left inset of 0. So we set this to 1px and subtract one │ │ │ │ │ + // pixel later when we set label.style.left │ │ │ │ │ + textbox.inset = "1px,0px,0px,0px"; │ │ │ │ │ │ │ │ │ │ - //new top (compare to margins, using this.size to calculate bottom) │ │ │ │ │ - if (origTL.y < this.map.paddingForPopups.top) { │ │ │ │ │ - newTL.y = this.map.paddingForPopups.top; │ │ │ │ │ - } else │ │ │ │ │ - if ((origTL.y + this.size.h) > (mapSize.h - this.map.paddingForPopups.bottom)) { │ │ │ │ │ - newTL.y = mapSize.h - this.map.paddingForPopups.bottom - this.size.h; │ │ │ │ │ + if (!label.parentNode) { │ │ │ │ │ + label.appendChild(textbox); │ │ │ │ │ + this.textRoot.appendChild(label); │ │ │ │ │ } │ │ │ │ │ │ │ │ │ │ - var dx = origTL.x - newTL.x; │ │ │ │ │ - var dy = origTL.y - newTL.y; │ │ │ │ │ + var align = style.labelAlign || "cm"; │ │ │ │ │ + if (align.length == 1) { │ │ │ │ │ + align += "m"; │ │ │ │ │ + } │ │ │ │ │ + var xshift = textbox.clientWidth * │ │ │ │ │ + (OpenLayers.Renderer.VML.LABEL_SHIFT[align.substr(0, 1)]); │ │ │ │ │ + var yshift = textbox.clientHeight * │ │ │ │ │ + (OpenLayers.Renderer.VML.LABEL_SHIFT[align.substr(1, 1)]); │ │ │ │ │ + label.style.left = parseInt(label.style.left) - xshift - 1 + "px"; │ │ │ │ │ + label.style.top = parseInt(label.style.top) + yshift + "px"; │ │ │ │ │ │ │ │ │ │ - this.map.pan(dx, dy); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: registerEvents │ │ │ │ │ - * Registers events on the popup. │ │ │ │ │ - * │ │ │ │ │ - * Do this in a separate function so that subclasses can │ │ │ │ │ - * choose to override it if they wish to deal differently │ │ │ │ │ - * with mouse events │ │ │ │ │ + /** │ │ │ │ │ + * Method: moveRoot │ │ │ │ │ + * moves this renderer's root to a different renderer. │ │ │ │ │ * │ │ │ │ │ - * Note in the following handler functions that some special │ │ │ │ │ - * care is needed to deal correctly with mousing and popups. │ │ │ │ │ - * │ │ │ │ │ - * Because the user might select the zoom-rectangle option and │ │ │ │ │ - * then drag it over a popup, we need a safe way to allow the │ │ │ │ │ - * mousemove and mouseup events to pass through the popup when │ │ │ │ │ - * they are initiated from outside. The same procedure is needed for │ │ │ │ │ - * touchmove and touchend events. │ │ │ │ │ + * Parameters: │ │ │ │ │ + * renderer - {} target renderer for the moved root │ │ │ │ │ + * root - {DOMElement} optional root node. To be used when this renderer │ │ │ │ │ + * holds roots from multiple layers to tell this method which one to │ │ │ │ │ + * detach │ │ │ │ │ * │ │ │ │ │ - * Otherwise, we want to essentially kill the event propagation │ │ │ │ │ - * for all other events, though we have to do so carefully, │ │ │ │ │ - * without disabling basic html functionality, like clicking on │ │ │ │ │ - * hyperlinks or drag-selecting text. │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Boolean} true if successful, false otherwise │ │ │ │ │ */ │ │ │ │ │ - registerEvents: function() { │ │ │ │ │ - this.events = new OpenLayers.Events(this, this.div, null, true); │ │ │ │ │ - │ │ │ │ │ - function onTouchstart(evt) { │ │ │ │ │ - OpenLayers.Event.stop(evt, true); │ │ │ │ │ + moveRoot: function(renderer) { │ │ │ │ │ + var layer = this.map.getLayer(renderer.container.id); │ │ │ │ │ + if (layer instanceof OpenLayers.Layer.Vector.RootContainer) { │ │ │ │ │ + layer = this.map.getLayer(this.container.id); │ │ │ │ │ } │ │ │ │ │ - this.events.on({ │ │ │ │ │ - "mousedown": this.onmousedown, │ │ │ │ │ - "mousemove": this.onmousemove, │ │ │ │ │ - "mouseup": this.onmouseup, │ │ │ │ │ - "click": this.onclick, │ │ │ │ │ - "mouseout": this.onmouseout, │ │ │ │ │ - "dblclick": this.ondblclick, │ │ │ │ │ - "touchstart": onTouchstart, │ │ │ │ │ - scope: this │ │ │ │ │ - }); │ │ │ │ │ - │ │ │ │ │ + layer && layer.renderer.clear(); │ │ │ │ │ + OpenLayers.Renderer.Elements.prototype.moveRoot.apply(this, arguments); │ │ │ │ │ + layer && layer.redraw(); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: onmousedown │ │ │ │ │ - * When mouse goes down within the popup, make a note of │ │ │ │ │ - * it locally, and then do not propagate the mousedown │ │ │ │ │ - * (but do so safely so that user can select text inside) │ │ │ │ │ + /** │ │ │ │ │ + * Method: importSymbol │ │ │ │ │ + * add a new symbol definition from the rendererer's symbol hash │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * evt - {Event} │ │ │ │ │ - */ │ │ │ │ │ - onmousedown: function(evt) { │ │ │ │ │ - this.mousedown = true; │ │ │ │ │ - OpenLayers.Event.stop(evt, true); │ │ │ │ │ - }, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Method: onmousemove │ │ │ │ │ - * If the drag was started within the popup, then │ │ │ │ │ - * do not propagate the mousemove (but do so safely │ │ │ │ │ - * so that user can select text inside) │ │ │ │ │ + * graphicName - {String} name of the symbol to import │ │ │ │ │ * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * evt - {Event} │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Object} - hash of {DOMElement} "symbol" and {Number} "size" │ │ │ │ │ */ │ │ │ │ │ - onmousemove: function(evt) { │ │ │ │ │ - if (this.mousedown) { │ │ │ │ │ - OpenLayers.Event.stop(evt, true); │ │ │ │ │ + importSymbol: function(graphicName) { │ │ │ │ │ + var id = this.container.id + "-" + graphicName; │ │ │ │ │ + │ │ │ │ │ + // check if symbol already exists in the cache │ │ │ │ │ + var cache = this.symbolCache[id]; │ │ │ │ │ + if (cache) { │ │ │ │ │ + return cache; │ │ │ │ │ } │ │ │ │ │ - }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: onmouseup │ │ │ │ │ - * When mouse comes up within the popup, after going down │ │ │ │ │ - * in it, reset the flag, and then (once again) do not │ │ │ │ │ - * propagate the event, but do so safely so that user can │ │ │ │ │ - * select text inside │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * evt - {Event} │ │ │ │ │ - */ │ │ │ │ │ - onmouseup: function(evt) { │ │ │ │ │ - if (this.mousedown) { │ │ │ │ │ - this.mousedown = false; │ │ │ │ │ - OpenLayers.Event.stop(evt, true); │ │ │ │ │ + var symbol = OpenLayers.Renderer.symbol[graphicName]; │ │ │ │ │ + if (!symbol) { │ │ │ │ │ + throw new Error(graphicName + ' is not a valid symbol name'); │ │ │ │ │ } │ │ │ │ │ - }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: onclick │ │ │ │ │ - * Ignore clicks, but allowing default browser handling │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * evt - {Event} │ │ │ │ │ - */ │ │ │ │ │ - onclick: function(evt) { │ │ │ │ │ - OpenLayers.Event.stop(evt, true); │ │ │ │ │ - }, │ │ │ │ │ + var symbolExtent = new OpenLayers.Bounds( │ │ │ │ │ + Number.MAX_VALUE, Number.MAX_VALUE, 0, 0); │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: onmouseout │ │ │ │ │ - * When mouse goes out of the popup set the flag to false so that │ │ │ │ │ - * if they let go and then drag back in, we won't be confused. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * evt - {Event} │ │ │ │ │ - */ │ │ │ │ │ - onmouseout: function(evt) { │ │ │ │ │ - this.mousedown = false; │ │ │ │ │ - }, │ │ │ │ │ + var pathitems = ["m"]; │ │ │ │ │ + for (var i = 0; i < symbol.length; i = i + 2) { │ │ │ │ │ + var x = symbol[i]; │ │ │ │ │ + var y = symbol[i + 1]; │ │ │ │ │ + symbolExtent.left = Math.min(symbolExtent.left, x); │ │ │ │ │ + symbolExtent.bottom = Math.min(symbolExtent.bottom, y); │ │ │ │ │ + symbolExtent.right = Math.max(symbolExtent.right, x); │ │ │ │ │ + symbolExtent.top = Math.max(symbolExtent.top, y); │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: ondblclick │ │ │ │ │ - * Ignore double-clicks, but allowing default browser handling │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * evt - {Event} │ │ │ │ │ - */ │ │ │ │ │ - ondblclick: function(evt) { │ │ │ │ │ - OpenLayers.Event.stop(evt, true); │ │ │ │ │ + pathitems.push(x); │ │ │ │ │ + pathitems.push(y); │ │ │ │ │ + if (i == 0) { │ │ │ │ │ + pathitems.push("l"); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + pathitems.push("x e"); │ │ │ │ │ + var path = pathitems.join(" "); │ │ │ │ │ + │ │ │ │ │ + var diff = (symbolExtent.getWidth() - symbolExtent.getHeight()) / 2; │ │ │ │ │ + if (diff > 0) { │ │ │ │ │ + symbolExtent.bottom = symbolExtent.bottom - diff; │ │ │ │ │ + symbolExtent.top = symbolExtent.top + diff; │ │ │ │ │ + } else { │ │ │ │ │ + symbolExtent.left = symbolExtent.left + diff; │ │ │ │ │ + symbolExtent.right = symbolExtent.right - diff; │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + cache = { │ │ │ │ │ + path: path, │ │ │ │ │ + size: symbolExtent.getWidth(), // equals getHeight() now │ │ │ │ │ + left: symbolExtent.left, │ │ │ │ │ + bottom: symbolExtent.bottom │ │ │ │ │ + }; │ │ │ │ │ + this.symbolCache[id] = cache; │ │ │ │ │ + │ │ │ │ │ + return cache; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Popup" │ │ │ │ │ + CLASS_NAME: "OpenLayers.Renderer.VML" │ │ │ │ │ }); │ │ │ │ │ │ │ │ │ │ -OpenLayers.Popup.WIDTH = 200; │ │ │ │ │ -OpenLayers.Popup.HEIGHT = 200; │ │ │ │ │ -OpenLayers.Popup.COLOR = "white"; │ │ │ │ │ -OpenLayers.Popup.OPACITY = 1; │ │ │ │ │ -OpenLayers.Popup.BORDER = "0px"; │ │ │ │ │ +/** │ │ │ │ │ + * Constant: OpenLayers.Renderer.VML.LABEL_SHIFT │ │ │ │ │ + * {Object} │ │ │ │ │ + */ │ │ │ │ │ +OpenLayers.Renderer.VML.LABEL_SHIFT = { │ │ │ │ │ + "l": 0, │ │ │ │ │ + "c": .5, │ │ │ │ │ + "r": 1, │ │ │ │ │ + "t": 0, │ │ │ │ │ + "m": .5, │ │ │ │ │ + "b": 1 │ │ │ │ │ +}; │ │ │ │ │ /* ====================================================================== │ │ │ │ │ - OpenLayers/Marker.js │ │ │ │ │ + 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/BaseTypes/Class.js │ │ │ │ │ - * @requires OpenLayers/Events.js │ │ │ │ │ - * @requires OpenLayers/Icon.js │ │ │ │ │ + * @requires OpenLayers/Renderer.js │ │ │ │ │ */ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Class: OpenLayers.Marker │ │ │ │ │ - * Instances of OpenLayers.Marker are a combination of a │ │ │ │ │ - * and an . │ │ │ │ │ - * │ │ │ │ │ - * Markers are generally added to a special layer called │ │ │ │ │ - * . │ │ │ │ │ - * │ │ │ │ │ - * Example: │ │ │ │ │ - * (code) │ │ │ │ │ - * var markers = new OpenLayers.Layer.Markers( "Markers" ); │ │ │ │ │ - * map.addLayer(markers); │ │ │ │ │ - * │ │ │ │ │ - * var size = new OpenLayers.Size(21,25); │ │ │ │ │ - * var offset = new OpenLayers.Pixel(-(size.w/2), -size.h); │ │ │ │ │ - * var icon = new OpenLayers.Icon('http://www.openlayers.org/dev/img/marker.png', size, offset); │ │ │ │ │ - * markers.addMarker(new OpenLayers.Marker(new OpenLayers.LonLat(0,0),icon)); │ │ │ │ │ - * markers.addMarker(new OpenLayers.Marker(new OpenLayers.LonLat(0,0),icon.clone())); │ │ │ │ │ - * │ │ │ │ │ - * (end) │ │ │ │ │ - * │ │ │ │ │ - * Note that if you pass an icon into the Marker constructor, it will take │ │ │ │ │ - * that icon and use it. This means that you should not share icons between │ │ │ │ │ - * markers -- you use them once, but you should clone() for any additional │ │ │ │ │ - * markers using that same icon. │ │ │ │ │ + * Class: OpenLayers.Renderer.Canvas │ │ │ │ │ + * A renderer based on the 2D 'canvas' drawing element. │ │ │ │ │ + * │ │ │ │ │ + * Inherits: │ │ │ │ │ + * - │ │ │ │ │ */ │ │ │ │ │ -OpenLayers.Marker = OpenLayers.Class({ │ │ │ │ │ +OpenLayers.Renderer.Canvas = OpenLayers.Class(OpenLayers.Renderer, { │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: icon │ │ │ │ │ - * {} The icon used by this marker. │ │ │ │ │ + /** │ │ │ │ │ + * APIProperty: hitDetection │ │ │ │ │ + * {Boolean} Allow for hit detection of features. Default is true. │ │ │ │ │ */ │ │ │ │ │ - icon: null, │ │ │ │ │ + hitDetection: true, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: lonlat │ │ │ │ │ - * {} location of object │ │ │ │ │ + /** │ │ │ │ │ + * 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). │ │ │ │ │ */ │ │ │ │ │ - lonlat: null, │ │ │ │ │ + hitOverflow: 0, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: events │ │ │ │ │ - * {} the event handler. │ │ │ │ │ + /** │ │ │ │ │ + * Property: canvas │ │ │ │ │ + * {Canvas} The canvas context object. │ │ │ │ │ */ │ │ │ │ │ - events: null, │ │ │ │ │ + canvas: null, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: map │ │ │ │ │ - * {} the map this marker is attached to │ │ │ │ │ + /** │ │ │ │ │ + * Property: features │ │ │ │ │ + * {Object} Internal object of feature/style pairs for use in redrawing the layer. │ │ │ │ │ */ │ │ │ │ │ - map: null, │ │ │ │ │ + features: null, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Constructor: OpenLayers.Marker │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * lonlat - {} the position of this marker │ │ │ │ │ - * icon - {} the icon for this marker │ │ │ │ │ + /** │ │ │ │ │ + * Property: pendingRedraw │ │ │ │ │ + * {Boolean} The renderer needs a redraw call to render features added while │ │ │ │ │ + * the renderer was locked. │ │ │ │ │ */ │ │ │ │ │ - initialize: function(lonlat, icon) { │ │ │ │ │ - this.lonlat = lonlat; │ │ │ │ │ - │ │ │ │ │ - var newIcon = (icon) ? icon : OpenLayers.Marker.defaultIcon(); │ │ │ │ │ - if (this.icon == null) { │ │ │ │ │ - this.icon = newIcon; │ │ │ │ │ - } else { │ │ │ │ │ - this.icon.url = newIcon.url; │ │ │ │ │ - this.icon.size = newIcon.size; │ │ │ │ │ - this.icon.offset = newIcon.offset; │ │ │ │ │ - this.icon.calculateOffset = newIcon.calculateOffset; │ │ │ │ │ - } │ │ │ │ │ - this.events = new OpenLayers.Events(this, this.icon.imageDiv); │ │ │ │ │ - }, │ │ │ │ │ + pendingRedraw: false, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: destroy │ │ │ │ │ - * Destroy the marker. You must first remove the marker from any │ │ │ │ │ - * layer which it has been added to, or you will get buggy behavior. │ │ │ │ │ - * (This can not be done within the marker since the marker does not │ │ │ │ │ - * know which layer it is attached to.) │ │ │ │ │ + * Property: cachedSymbolBounds │ │ │ │ │ + * {Object} Internal cache of calculated symbol extents. │ │ │ │ │ */ │ │ │ │ │ - destroy: function() { │ │ │ │ │ - // erase any drawn features │ │ │ │ │ - this.erase(); │ │ │ │ │ - │ │ │ │ │ - this.map = null; │ │ │ │ │ - │ │ │ │ │ - this.events.destroy(); │ │ │ │ │ - this.events = null; │ │ │ │ │ + cachedSymbolBounds: {}, │ │ │ │ │ │ │ │ │ │ - if (this.icon != null) { │ │ │ │ │ - this.icon.destroy(); │ │ │ │ │ - this.icon = null; │ │ │ │ │ + /** │ │ │ │ │ + * 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: draw │ │ │ │ │ - * Calls draw on the icon, and returns that output. │ │ │ │ │ - * │ │ │ │ │ + /** │ │ │ │ │ + * Method: setExtent │ │ │ │ │ + * Set the visible part of the layer. │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * px - {} │ │ │ │ │ - * │ │ │ │ │ + * extent - {} │ │ │ │ │ + * resolutionChanged - {Boolean} │ │ │ │ │ + * │ │ │ │ │ * Returns: │ │ │ │ │ - * {DOMElement} A new DOM Image with this marker's icon set at the │ │ │ │ │ - * location passed-in │ │ │ │ │ + * {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. │ │ │ │ │ */ │ │ │ │ │ - draw: function(px) { │ │ │ │ │ - return this.icon.draw(px); │ │ │ │ │ + setExtent: function() { │ │ │ │ │ + OpenLayers.Renderer.prototype.setExtent.apply(this, arguments); │ │ │ │ │ + // always redraw features │ │ │ │ │ + return false; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: erase │ │ │ │ │ - * Erases any drawn elements for this marker. │ │ │ │ │ - */ │ │ │ │ │ - erase: function() { │ │ │ │ │ - if (this.icon != null) { │ │ │ │ │ - this.icon.erase(); │ │ │ │ │ - } │ │ │ │ │ - }, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Method: moveTo │ │ │ │ │ - * Move the marker to the new location. │ │ │ │ │ - * │ │ │ │ │ + * 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: │ │ │ │ │ - * px - {|Object} the pixel position to move to. │ │ │ │ │ - * An OpenLayers.Pixel or an object with a 'x' and 'y' properties. │ │ │ │ │ + * geometry - {} │ │ │ │ │ + * featureId - {String} │ │ │ │ │ */ │ │ │ │ │ - moveTo: function(px) { │ │ │ │ │ - if ((px != null) && (this.icon != null)) { │ │ │ │ │ - this.icon.moveTo(px); │ │ │ │ │ - } │ │ │ │ │ - this.lonlat = this.map.getLonLatFromLayerPx(px); │ │ │ │ │ + eraseGeometry: function(geometry, featureId) { │ │ │ │ │ + this.eraseFeatures(this.features[featureId][0]); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * APIMethod: isDrawn │ │ │ │ │ + * APIMethod: supported │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {Boolean} Whether or not the marker is drawn. │ │ │ │ │ + * {Boolean} Whether or not the browser supports the renderer class │ │ │ │ │ */ │ │ │ │ │ - isDrawn: function() { │ │ │ │ │ - var isDrawn = (this.icon && this.icon.isDrawn()); │ │ │ │ │ - return isDrawn; │ │ │ │ │ + supported: function() { │ │ │ │ │ + return OpenLayers.CANVAS_SUPPORTED; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: onScreen │ │ │ │ │ + * Method: setSize │ │ │ │ │ + * Sets the size of the drawing surface. │ │ │ │ │ * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Boolean} Whether or not the marker is currently visible on screen. │ │ │ │ │ + * Once the size is updated, redraw the canvas. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * size - {} │ │ │ │ │ */ │ │ │ │ │ - onScreen: function() { │ │ │ │ │ - │ │ │ │ │ - var onScreen = false; │ │ │ │ │ - if (this.map) { │ │ │ │ │ - var screenBounds = this.map.getExtent(); │ │ │ │ │ - onScreen = screenBounds.containsLonLat(this.lonlat); │ │ │ │ │ + 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; │ │ │ │ │ } │ │ │ │ │ - return onScreen; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: inflate │ │ │ │ │ - * Englarges the markers icon by the specified ratio. │ │ │ │ │ + * Method: drawFeature │ │ │ │ │ + * Draw the feature. Stores the feature in the features list, │ │ │ │ │ + * then redraws the layer. │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * inflate - {float} the ratio to enlarge the marker by (passing 2 │ │ │ │ │ - * will double the size). │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - inflate: function(inflate) { │ │ │ │ │ - if (this.icon) { │ │ │ │ │ - this.icon.setSize({ │ │ │ │ │ - w: this.icon.size.w * inflate, │ │ │ │ │ - h: this.icon.size.h * inflate │ │ │ │ │ + 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: setOpacity │ │ │ │ │ - * Change the opacity of the marker by changin the opacity of │ │ │ │ │ - * its icon │ │ │ │ │ - * │ │ │ │ │ + * Method: drawGeometry │ │ │ │ │ + * Used when looping (in redraw) over the features; draws │ │ │ │ │ + * the canvas. │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * opacity - {float} Specified as fraction (0.4, etc) │ │ │ │ │ + * geometry - {} │ │ │ │ │ + * style - {Object} │ │ │ │ │ */ │ │ │ │ │ - setOpacity: function(opacity) { │ │ │ │ │ - this.icon.setOpacity(opacity); │ │ │ │ │ + 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: setUrl │ │ │ │ │ - * Change URL of the Icon Image. │ │ │ │ │ + * Method: drawExternalGraphic │ │ │ │ │ + * Called to draw External graphics. │ │ │ │ │ * │ │ │ │ │ - * url - {String} │ │ │ │ │ + * Parameters: │ │ │ │ │ + * geometry - {} │ │ │ │ │ + * style - {Object} │ │ │ │ │ + * featureId - {String} │ │ │ │ │ */ │ │ │ │ │ - setUrl: function(url) { │ │ │ │ │ - this.icon.setUrl(url); │ │ │ │ │ + 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: display │ │ │ │ │ - * Hide or show the icon │ │ │ │ │ + /** │ │ │ │ │ + * Method: drawNamedSymbol │ │ │ │ │ + * Called to draw Well Known Graphic Symbol Name. │ │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ │ * │ │ │ │ │ - * display - {Boolean} │ │ │ │ │ + * Parameters: │ │ │ │ │ + * geometry - {} │ │ │ │ │ + * style - {Object} │ │ │ │ │ + * featureId - {String} │ │ │ │ │ */ │ │ │ │ │ - display: function(display) { │ │ │ │ │ - this.icon.display(display); │ │ │ │ │ - }, │ │ │ │ │ + drawNamedSymbol: function(geometry, style, featureId) { │ │ │ │ │ + var x, y, cx, cy, i, symbolBounds, scaling, angle; │ │ │ │ │ + var unscaledStrokeWidth; │ │ │ │ │ + var deg2rad = Math.PI / 180.0; │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Marker" │ │ │ │ │ -}); │ │ │ │ │ + var symbol = OpenLayers.Renderer.symbol[style.graphicName]; │ │ │ │ │ │ │ │ │ │ + if (!symbol) { │ │ │ │ │ + throw new Error(style.graphicName + ' is not a valid symbol name'); │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * Function: defaultIcon │ │ │ │ │ - * Creates a default . │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {} A default OpenLayers.Icon to use for a marker │ │ │ │ │ - */ │ │ │ │ │ -OpenLayers.Marker.defaultIcon = function() { │ │ │ │ │ - return new OpenLayers.Icon(OpenLayers.Util.getImageLocation("marker.png"), { │ │ │ │ │ - w: 21, │ │ │ │ │ - h: 25 │ │ │ │ │ - }, { │ │ │ │ │ - x: -10.5, │ │ │ │ │ - y: -25 │ │ │ │ │ - }); │ │ │ │ │ -}; │ │ │ │ │ + if (!symbol.length || symbol.length < 2) return; │ │ │ │ │ │ │ │ │ │ + var pt = this.getLocalXY(geometry); │ │ │ │ │ + var p0 = pt[0]; │ │ │ │ │ + var p1 = pt[1]; │ │ │ │ │ │ │ │ │ │ -/* ====================================================================== │ │ │ │ │ - OpenLayers/Events/buttonclick.js │ │ │ │ │ - ====================================================================== */ │ │ │ │ │ + if (isNaN(p0) || isNaN(p1)) return; │ │ │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ - * full text of the license. */ │ │ │ │ │ + // Use rounded line caps │ │ │ │ │ + this.canvas.lineCap = "round"; │ │ │ │ │ + this.canvas.lineJoin = "round"; │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * @requires OpenLayers/Events.js │ │ │ │ │ - */ │ │ │ │ │ + if (this.hitDetection) { │ │ │ │ │ + this.hitContext.lineCap = "round"; │ │ │ │ │ + this.hitContext.lineJoin = "round"; │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * Class: OpenLayers.Events.buttonclick │ │ │ │ │ - * Extension event type for handling buttons on top of a dom element. This │ │ │ │ │ - * event type fires "buttonclick" on its 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({ │ │ │ │ │ + // Scale and rotate symbols, using precalculated bounds whenever possible. │ │ │ │ │ + if (style.graphicName in this.cachedSymbolBounds) { │ │ │ │ │ + symbolBounds = this.cachedSymbolBounds[style.graphicName]; │ │ │ │ │ + } else { │ │ │ │ │ + symbolBounds = new OpenLayers.Bounds(); │ │ │ │ │ + for (i = 0; i < symbol.length; i += 2) { │ │ │ │ │ + symbolBounds.extend(new OpenLayers.LonLat(symbol[i], symbol[i + 1])); │ │ │ │ │ + } │ │ │ │ │ + this.cachedSymbolBounds[style.graphicName] = symbolBounds; │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + // Push symbol scaling, translation and rotation onto the transformation stack in reverse order. │ │ │ │ │ + // Don't forget to apply all canvas transformations to the hitContext canvas as well(!) │ │ │ │ │ + this.canvas.save(); │ │ │ │ │ + if (this.hitDetection) { │ │ │ │ │ + this.hitContext.save(); │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + // Step 3: place symbol at the desired location │ │ │ │ │ + this.canvas.translate(p0, p1); │ │ │ │ │ + if (this.hitDetection) { │ │ │ │ │ + this.hitContext.translate(p0, p1); │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + // Step 2a. rotate the symbol if necessary │ │ │ │ │ + angle = deg2rad * style.rotation; // will be NaN when style.rotation is undefined. │ │ │ │ │ + if (!isNaN(angle)) { │ │ │ │ │ + this.canvas.rotate(angle); │ │ │ │ │ + if (this.hitDetection) { │ │ │ │ │ + this.hitContext.rotate(angle); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + // // Step 2: scale symbol such that pointRadius equals half the maximum symbol dimension. │ │ │ │ │ + scaling = 2.0 * style.pointRadius / Math.max(symbolBounds.getWidth(), symbolBounds.getHeight()); │ │ │ │ │ + this.canvas.scale(scaling, scaling); │ │ │ │ │ + if (this.hitDetection) { │ │ │ │ │ + this.hitContext.scale(scaling, scaling); │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + // Step 1: center the symbol at the origin │ │ │ │ │ + cx = symbolBounds.getCenterLonLat().lon; │ │ │ │ │ + cy = symbolBounds.getCenterLonLat().lat; │ │ │ │ │ + this.canvas.translate(-cx, -cy); │ │ │ │ │ + if (this.hitDetection) { │ │ │ │ │ + this.hitContext.translate(-cx, -cy); │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + // Don't forget to scale stroke widths, because they are affected by canvas scale transformations as well(!) │ │ │ │ │ + // Alternative: scale symbol coordinates manually, so stroke width scaling is not needed anymore. │ │ │ │ │ + unscaledStrokeWidth = style.strokeWidth; │ │ │ │ │ + style.strokeWidth = unscaledStrokeWidth / scaling; │ │ │ │ │ + │ │ │ │ │ + if (style.fill !== false) { │ │ │ │ │ + this.setCanvasStyle("fill", style); │ │ │ │ │ + this.canvas.beginPath(); │ │ │ │ │ + for (i = 0; i < symbol.length; i = i + 2) { │ │ │ │ │ + x = symbol[i]; │ │ │ │ │ + y = symbol[i + 1]; │ │ │ │ │ + if (i == 0) this.canvas.moveTo(x, y); │ │ │ │ │ + this.canvas.lineTo(x, y); │ │ │ │ │ + } │ │ │ │ │ + this.canvas.closePath(); │ │ │ │ │ + this.canvas.fill(); │ │ │ │ │ + │ │ │ │ │ + if (this.hitDetection) { │ │ │ │ │ + this.setHitContextStyle("fill", featureId, style); │ │ │ │ │ + this.hitContext.beginPath(); │ │ │ │ │ + for (i = 0; i < symbol.length; i = i + 2) { │ │ │ │ │ + x = symbol[i]; │ │ │ │ │ + y = symbol[i + 1]; │ │ │ │ │ + if (i == 0) this.canvas.moveTo(x, y); │ │ │ │ │ + this.hitContext.lineTo(x, y); │ │ │ │ │ + } │ │ │ │ │ + this.hitContext.closePath(); │ │ │ │ │ + this.hitContext.fill(); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + if (style.stroke !== false) { │ │ │ │ │ + this.setCanvasStyle("stroke", style); │ │ │ │ │ + this.canvas.beginPath(); │ │ │ │ │ + for (i = 0; i < symbol.length; i = i + 2) { │ │ │ │ │ + x = symbol[i]; │ │ │ │ │ + y = symbol[i + 1]; │ │ │ │ │ + if (i == 0) this.canvas.moveTo(x, y); │ │ │ │ │ + this.canvas.lineTo(x, y); │ │ │ │ │ + } │ │ │ │ │ + this.canvas.closePath(); │ │ │ │ │ + this.canvas.stroke(); │ │ │ │ │ + │ │ │ │ │ + │ │ │ │ │ + if (this.hitDetection) { │ │ │ │ │ + this.setHitContextStyle("stroke", featureId, style, scaling); │ │ │ │ │ + this.hitContext.beginPath(); │ │ │ │ │ + for (i = 0; i < symbol.length; i = i + 2) { │ │ │ │ │ + x = symbol[i]; │ │ │ │ │ + y = symbol[i + 1]; │ │ │ │ │ + if (i == 0) this.hitContext.moveTo(x, y); │ │ │ │ │ + this.hitContext.lineTo(x, y); │ │ │ │ │ + } │ │ │ │ │ + this.hitContext.closePath(); │ │ │ │ │ + this.hitContext.stroke(); │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + } │ │ │ │ │ + │ │ │ │ │ + style.strokeWidth = unscaledStrokeWidth; │ │ │ │ │ + this.canvas.restore(); │ │ │ │ │ + if (this.hitDetection) { │ │ │ │ │ + this.hitContext.restore(); │ │ │ │ │ + } │ │ │ │ │ + this.setCanvasStyle("reset"); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: target │ │ │ │ │ - * {} The events instance that the buttonclick event will │ │ │ │ │ - * be triggered on. │ │ │ │ │ + * Method: setCanvasStyle │ │ │ │ │ + * Prepare the canvas for drawing by setting various global settings. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * type - {String} one of 'stroke', 'fill', or 'reset' │ │ │ │ │ + * style - {Object} Symbolizer hash │ │ │ │ │ */ │ │ │ │ │ - target: null, │ │ │ │ │ + setCanvasStyle: function(type, style) { │ │ │ │ │ + if (type === "fill") { │ │ │ │ │ + this.canvas.globalAlpha = style['fillOpacity']; │ │ │ │ │ + this.canvas.fillStyle = style['fillColor']; │ │ │ │ │ + } else if (type === "stroke") { │ │ │ │ │ + this.canvas.globalAlpha = style['strokeOpacity']; │ │ │ │ │ + this.canvas.strokeStyle = style['strokeColor']; │ │ │ │ │ + this.canvas.lineWidth = style['strokeWidth']; │ │ │ │ │ + } else { │ │ │ │ │ + this.canvas.globalAlpha = 0; │ │ │ │ │ + this.canvas.lineWidth = 1; │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: events │ │ │ │ │ - * {Array} Events to observe and conditionally stop from propagating when │ │ │ │ │ - * an element with the olButton class (or its olAlphaImg child) is │ │ │ │ │ - * clicked. │ │ │ │ │ + * Method: featureIdToHex │ │ │ │ │ + * Convert a feature ID string into an RGB hex string. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * featureId - {String} Feature id │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {String} RGB hex string. │ │ │ │ │ */ │ │ │ │ │ - events: [ │ │ │ │ │ - 'mousedown', 'mouseup', 'click', 'dblclick', │ │ │ │ │ - 'touchstart', 'touchmove', 'touchend', 'keydown' │ │ │ │ │ - ], │ │ │ │ │ + featureIdToHex: function(featureId) { │ │ │ │ │ + var id = Number(featureId.split("_").pop()) + 1; // zero for no feature │ │ │ │ │ + if (id >= 16777216) { │ │ │ │ │ + this.hitOverflow = id - 16777215; │ │ │ │ │ + id = id % 16777216 + 1; │ │ │ │ │ + } │ │ │ │ │ + var hex = "000000" + id.toString(16); │ │ │ │ │ + var len = hex.length; │ │ │ │ │ + hex = "#" + hex.substring(len - 6, len); │ │ │ │ │ + return hex; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: startRegEx │ │ │ │ │ - * {RegExp} Regular expression to test Event.type for events that start │ │ │ │ │ - * a buttonclick sequence. │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - startRegEx: /^mousedown|touchstart$/, │ │ │ │ │ + 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; │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: cancelRegEx │ │ │ │ │ - * {RegExp} Regular expression to test Event.type for events that cancel │ │ │ │ │ - * a buttonclick sequence. │ │ │ │ │ + * Method: drawPoint │ │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * geometry - {} │ │ │ │ │ + * style - {Object} │ │ │ │ │ + * featureId - {String} │ │ │ │ │ */ │ │ │ │ │ - cancelRegEx: /^touchmove$/, │ │ │ │ │ + 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"); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: completeRegEx │ │ │ │ │ - * {RegExp} Regular expression to test Event.type for events that complete │ │ │ │ │ - * a buttonclick sequence. │ │ │ │ │ + * Method: drawLineString │ │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * geometry - {} │ │ │ │ │ + * style - {Object} │ │ │ │ │ + * featureId - {String} │ │ │ │ │ */ │ │ │ │ │ - completeRegEx: /^mouseup|touchend$/, │ │ │ │ │ + drawLineString: function(geometry, style, featureId) { │ │ │ │ │ + style = OpenLayers.Util.applyDefaults({ │ │ │ │ │ + fill: false │ │ │ │ │ + }, style); │ │ │ │ │ + this.drawLinearRing(geometry, style, featureId); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: startEvt │ │ │ │ │ - * {Event} The event that started the click sequence │ │ │ │ │ + * 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"); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * 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. │ │ │ │ │ + * Method: renderPath │ │ │ │ │ + * Render a path with stroke and optional fill. │ │ │ │ │ */ │ │ │ │ │ - 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 │ │ │ │ │ - }); │ │ │ │ │ + renderPath: function(context, geometry, style, featureId, type) { │ │ │ │ │ + var components = geometry.components; │ │ │ │ │ + var len = components.length; │ │ │ │ │ + context.beginPath(); │ │ │ │ │ + var start = this.getLocalXY(components[0]); │ │ │ │ │ + var x = start[0]; │ │ │ │ │ + var y = start[1]; │ │ │ │ │ + if (!isNaN(x) && !isNaN(y)) { │ │ │ │ │ + context.moveTo(start[0], start[1]); │ │ │ │ │ + for (var i = 1; i < len; ++i) { │ │ │ │ │ + var pt = this.getLocalXY(components[i]); │ │ │ │ │ + context.lineTo(pt[0], pt[1]); │ │ │ │ │ + } │ │ │ │ │ + if (type === "fill") { │ │ │ │ │ + context.fill(); │ │ │ │ │ + } else { │ │ │ │ │ + context.stroke(); │ │ │ │ │ + } │ │ │ │ │ } │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: destroy │ │ │ │ │ + * Method: drawPolygon │ │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * geometry - {} │ │ │ │ │ + * style - {Object} │ │ │ │ │ + * featureId - {String} │ │ │ │ │ */ │ │ │ │ │ - destroy: function() { │ │ │ │ │ - for (var i = this.events.length - 1; i >= 0; --i) { │ │ │ │ │ - this.target.unregister(this.events[i], this, this.buttonClick); │ │ │ │ │ + drawPolygon: function(geometry, style, featureId) { │ │ │ │ │ + var components = geometry.components; │ │ │ │ │ + var len = components.length; │ │ │ │ │ + this.drawLinearRing(components[0], style, featureId); │ │ │ │ │ + // erase inner rings │ │ │ │ │ + for (var i = 1; i < len; ++i) { │ │ │ │ │ + /** │ │ │ │ │ + * Note that this is overly agressive. Here we punch holes through │ │ │ │ │ + * all previously rendered features on the same canvas. A better │ │ │ │ │ + * solution for polygons with interior rings would be to draw the │ │ │ │ │ + * polygon on a sketch canvas first. We could erase all holes │ │ │ │ │ + * there and then copy the drawing to the layer canvas. │ │ │ │ │ + * TODO: http://trac.osgeo.org/openlayers/ticket/3130 │ │ │ │ │ + */ │ │ │ │ │ + this.canvas.globalCompositeOperation = "destination-out"; │ │ │ │ │ + if (this.hitDetection) { │ │ │ │ │ + this.hitContext.globalCompositeOperation = "destination-out"; │ │ │ │ │ + } │ │ │ │ │ + this.drawLinearRing( │ │ │ │ │ + components[i], │ │ │ │ │ + OpenLayers.Util.applyDefaults({ │ │ │ │ │ + stroke: false, │ │ │ │ │ + fillOpacity: 1.0 │ │ │ │ │ + }, style), │ │ │ │ │ + featureId │ │ │ │ │ + ); │ │ │ │ │ + this.canvas.globalCompositeOperation = "source-over"; │ │ │ │ │ + if (this.hitDetection) { │ │ │ │ │ + this.hitContext.globalCompositeOperation = "source-over"; │ │ │ │ │ + } │ │ │ │ │ + this.drawLinearRing( │ │ │ │ │ + components[i], │ │ │ │ │ + OpenLayers.Util.applyDefaults({ │ │ │ │ │ + fill: false │ │ │ │ │ + }, style), │ │ │ │ │ + featureId │ │ │ │ │ + ); │ │ │ │ │ } │ │ │ │ │ - delete this.target; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: getPressedButton │ │ │ │ │ - * Get the pressed button, if any. Returns undefined if no button │ │ │ │ │ - * was pressed. │ │ │ │ │ - * │ │ │ │ │ - * Arguments: │ │ │ │ │ - * element - {DOMElement} The event target. │ │ │ │ │ + * Method: drawText │ │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ │ * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {DOMElement} The button element, or undefined. │ │ │ │ │ + * Parameters: │ │ │ │ │ + * location - {} │ │ │ │ │ + * style - {Object} │ │ │ │ │ */ │ │ │ │ │ - getPressedButton: function(element) { │ │ │ │ │ - var depth = 3, // limit the search depth │ │ │ │ │ - button; │ │ │ │ │ - do { │ │ │ │ │ - if (OpenLayers.Element.hasClass(element, "olButton")) { │ │ │ │ │ - // hit! │ │ │ │ │ - button = element; │ │ │ │ │ - break; │ │ │ │ │ + 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; │ │ │ │ │ } │ │ │ │ │ - element = element.parentNode; │ │ │ │ │ - } while (--depth > 0 && element); │ │ │ │ │ - return button; │ │ │ │ │ + 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: │ │ │ │ │ - * element - {DOMElement} The event target. │ │ │ │ │ + * evt - {} │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * { 0 && element); │ │ │ │ │ - return ignore; │ │ │ │ │ + } │ │ │ │ │ + return feature; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: buttonClick │ │ │ │ │ - * Check if a button was clicked, and fire the buttonclick event │ │ │ │ │ - * │ │ │ │ │ + * Method: eraseFeatures │ │ │ │ │ + * This is called by the layer to erase features; removes the feature from │ │ │ │ │ + * the list, then redraws the layer. │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * evt - {Event} │ │ │ │ │ + * features - {Array()} │ │ │ │ │ */ │ │ │ │ │ - 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; │ │ │ │ │ + eraseFeatures: function(features) { │ │ │ │ │ + if (!(OpenLayers.Util.isArray(features))) { │ │ │ │ │ + features = [features]; │ │ │ │ │ + } │ │ │ │ │ + for (var i = 0; i < features.length; ++i) { │ │ │ │ │ + delete this.features[features[i].id]; │ │ │ │ │ + } │ │ │ │ │ + this.redraw(); │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - 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; │ │ │ │ │ + /** │ │ │ │ │ + * Method: redraw │ │ │ │ │ + * The real 'meat' of the function: any time things have changed, │ │ │ │ │ + * redraw() can be called to loop over all the data and (you guessed │ │ │ │ │ + * it) redraw it. Unlike Elements-based Renderers, we can't interact │ │ │ │ │ + * with things once they're drawn, to remove them, for example, so │ │ │ │ │ + * instead we have to just clear everything and draw from scratch. │ │ │ │ │ + */ │ │ │ │ │ + redraw: function() { │ │ │ │ │ + if (!this.locked) { │ │ │ │ │ + var height = this.root.height; │ │ │ │ │ + var width = this.root.width; │ │ │ │ │ + this.canvas.clearRect(0, 0, width, height); │ │ │ │ │ + if (this.hitDetection) { │ │ │ │ │ + this.hitContext.clearRect(0, 0, width, height); │ │ │ │ │ + } │ │ │ │ │ + var labelMap = []; │ │ │ │ │ + var feature, geometry, style; │ │ │ │ │ + var worldBounds = (this.map.baseLayer && this.map.baseLayer.wrapDateLine) && this.map.getMaxExtent(); │ │ │ │ │ + for (var id in this.features) { │ │ │ │ │ + if (!this.features.hasOwnProperty(id)) { │ │ │ │ │ + continue; │ │ │ │ │ } │ │ │ │ │ - if (this.startRegEx.test(evt.type)) { │ │ │ │ │ - this.startEvt = evt; │ │ │ │ │ - OpenLayers.Event.stop(evt); │ │ │ │ │ - propagate = false; │ │ │ │ │ + feature = this.features[id][0]; │ │ │ │ │ + geometry = feature.geometry; │ │ │ │ │ + this.calculateFeatureDx(geometry.getBounds(), worldBounds); │ │ │ │ │ + style = this.features[id][1]; │ │ │ │ │ + this.drawGeometry(geometry, style, feature.id); │ │ │ │ │ + if (style.label) { │ │ │ │ │ + labelMap.push([feature, style]); │ │ │ │ │ } │ │ │ │ │ - } else { │ │ │ │ │ - propagate = !this.ignore(OpenLayers.Event.element(evt)); │ │ │ │ │ - delete this.startEvt; │ │ │ │ │ + } │ │ │ │ │ + var item; │ │ │ │ │ + for (var i = 0, len = labelMap.length; i < len; ++i) { │ │ │ │ │ + item = labelMap[i]; │ │ │ │ │ + this.drawText(item[0].geometry.getCentroid(), item[1]); │ │ │ │ │ } │ │ │ │ │ } │ │ │ │ │ - return propagate; │ │ │ │ │ - } │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ + CLASS_NAME: "OpenLayers.Renderer.Canvas" │ │ │ │ │ }); │ │ │ │ │ + │ │ │ │ │ +/** │ │ │ │ │ + * Constant: OpenLayers.Renderer.Canvas.LABEL_ALIGN │ │ │ │ │ + * {Object} │ │ │ │ │ + */ │ │ │ │ │ +OpenLayers.Renderer.Canvas.LABEL_ALIGN = { │ │ │ │ │ + "l": "left", │ │ │ │ │ + "r": "right", │ │ │ │ │ + "t": "top", │ │ │ │ │ + "b": "bottom" │ │ │ │ │ +}; │ │ │ │ │ + │ │ │ │ │ +/** │ │ │ │ │ + * Constant: OpenLayers.Renderer.Canvas.LABEL_FACTOR │ │ │ │ │ + * {Object} │ │ │ │ │ + */ │ │ │ │ │ +OpenLayers.Renderer.Canvas.LABEL_FACTOR = { │ │ │ │ │ + "l": 0, │ │ │ │ │ + "r": -1, │ │ │ │ │ + "t": 0, │ │ │ │ │ + "b": -1 │ │ │ │ │ +}; │ │ │ │ │ + │ │ │ │ │ +/** │ │ │ │ │ + * Constant: OpenLayers.Renderer.Canvas.drawImageScaleFactor │ │ │ │ │ + * {Number} Scale factor to apply to the canvas drawImage arguments. This │ │ │ │ │ + * is always 1 except for Android 2.1 devices, to work around │ │ │ │ │ + * http://code.google.com/p/android/issues/detail?id=5141. │ │ │ │ │ + */ │ │ │ │ │ +OpenLayers.Renderer.Canvas.drawImageScaleFactor = null; │ │ │ │ │ /* ====================================================================== │ │ │ │ │ - OpenLayers/Events/featureclick.js │ │ │ │ │ + OpenLayers/Renderer/SVG.js │ │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * @requires OpenLayers/Events.js │ │ │ │ │ + * @requires OpenLayers/Renderer/Elements.js │ │ │ │ │ */ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Class: OpenLayers.Events.featureclick │ │ │ │ │ - * │ │ │ │ │ - * Extension event type for handling feature click events, including overlapping │ │ │ │ │ - * features. │ │ │ │ │ + * Class: OpenLayers.Renderer.SVG │ │ │ │ │ * │ │ │ │ │ - * Event types provided by this extension: │ │ │ │ │ - * - featureclick │ │ │ │ │ + * Inherits: │ │ │ │ │ + * - │ │ │ │ │ */ │ │ │ │ │ -OpenLayers.Events.featureclick = OpenLayers.Class({ │ │ │ │ │ +OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, { │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Property: xmlns │ │ │ │ │ + * {String} │ │ │ │ │ + */ │ │ │ │ │ + xmlns: "http://www.w3.org/2000/svg", │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: cache │ │ │ │ │ - * {Object} A cache of features under the mouse. │ │ │ │ │ + * Property: xlinkns │ │ │ │ │ + * {String} │ │ │ │ │ */ │ │ │ │ │ - cache: null, │ │ │ │ │ + xlinkns: "http://www.w3.org/1999/xlink", │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: map │ │ │ │ │ - * {} The map to register browser events on. │ │ │ │ │ + * Constant: MAX_PIXEL │ │ │ │ │ + * {Integer} Firefox has a limitation where values larger or smaller than │ │ │ │ │ + * about 15000 in an SVG document lock the browser up. This │ │ │ │ │ + * works around it. │ │ │ │ │ */ │ │ │ │ │ - map: null, │ │ │ │ │ + MAX_PIXEL: 15000, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Property: provides │ │ │ │ │ - * {Array(String)} The event types provided by this extension. │ │ │ │ │ + * Property: translationParameters │ │ │ │ │ + * {Object} Hash with "x" and "y" properties │ │ │ │ │ */ │ │ │ │ │ - provides: ["featureclick", "nofeatureclick", "featureover", "featureout"], │ │ │ │ │ + translationParameters: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Constructor: OpenLayers.Events.featureclick │ │ │ │ │ - * Create a new featureclick event type. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * target - {} The events instance to create the events │ │ │ │ │ - * for. │ │ │ │ │ + * Property: symbolMetrics │ │ │ │ │ + * {Object} Cache for symbol metrics according to their svg coordinate │ │ │ │ │ + * space. This is an object keyed by the symbol's id, and values are │ │ │ │ │ + * an array of [width, centerX, centerY]. │ │ │ │ │ */ │ │ │ │ │ - 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; │ │ │ │ │ - } │ │ │ │ │ - }, │ │ │ │ │ + symbolMetrics: null, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: setMap │ │ │ │ │ - * │ │ │ │ │ + * Constructor: OpenLayers.Renderer.SVG │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * map - {} The map to register browser events on. │ │ │ │ │ + * containerID - {String} │ │ │ │ │ */ │ │ │ │ │ - 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 │ │ │ │ │ - }); │ │ │ │ │ + initialize: function(containerID) { │ │ │ │ │ + if (!this.supported()) { │ │ │ │ │ + return; │ │ │ │ │ + } │ │ │ │ │ + OpenLayers.Renderer.Elements.prototype.initialize.apply(this, │ │ │ │ │ + arguments); │ │ │ │ │ + this.translationParameters = { │ │ │ │ │ + x: 0, │ │ │ │ │ + y: 0 │ │ │ │ │ + }; │ │ │ │ │ + │ │ │ │ │ + this.symbolMetrics = {}; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: start │ │ │ │ │ - * Sets startEvt = evt. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * evt - {} │ │ │ │ │ + * APIMethod: supported │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Boolean} Whether or not the browser supports the SVG renderer │ │ │ │ │ */ │ │ │ │ │ - start: function(evt) { │ │ │ │ │ - this.startEvt = evt; │ │ │ │ │ + supported: function() { │ │ │ │ │ + var svgFeature = "http://www.w3.org/TR/SVG11/feature#"; │ │ │ │ │ + return (document.implementation && │ │ │ │ │ + (document.implementation.hasFeature("org.w3c.svg", "1.0") || │ │ │ │ │ + document.implementation.hasFeature(svgFeature + "SVG", "1.1") || │ │ │ │ │ + document.implementation.hasFeature(svgFeature + "BasicStructure", "1.1"))); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: cancel │ │ │ │ │ - * Deletes the start event. │ │ │ │ │ + * Method: inValidRange │ │ │ │ │ + * See #669 for more information │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * evt - {} │ │ │ │ │ + * x - {Integer} │ │ │ │ │ + * y - {Integer} │ │ │ │ │ + * xyOnly - {Boolean} whether or not to just check for x and y, which means │ │ │ │ │ + * to not take the current translation parameters into account if true. │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Boolean} Whether or not the 'x' and 'y' coordinates are in the │ │ │ │ │ + * valid range. │ │ │ │ │ */ │ │ │ │ │ - cancel: function(evt) { │ │ │ │ │ - delete this.startEvt; │ │ │ │ │ + inValidRange: function(x, y, xyOnly) { │ │ │ │ │ + var left = x + (xyOnly ? 0 : this.translationParameters.x); │ │ │ │ │ + var top = y + (xyOnly ? 0 : this.translationParameters.y); │ │ │ │ │ + return (left >= -this.MAX_PIXEL && left <= this.MAX_PIXEL && │ │ │ │ │ + top >= -this.MAX_PIXEL && top <= this.MAX_PIXEL); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: onClick │ │ │ │ │ - * Listener for the click event. │ │ │ │ │ - * │ │ │ │ │ + * Method: setExtent │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * evt - {} │ │ │ │ │ + * 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. │ │ │ │ │ */ │ │ │ │ │ - onClick: function(evt) { │ │ │ │ │ - if (!this.startEvt || evt.type !== "touchend" && │ │ │ │ │ - !OpenLayers.Event.isLeftClick(evt)) { │ │ │ │ │ - return; │ │ │ │ │ - } │ │ │ │ │ - var features = this.getFeatures(this.startEvt); │ │ │ │ │ - delete this.startEvt; │ │ │ │ │ - // fire featureclick events │ │ │ │ │ - var feature, layer, more, clicked = {}; │ │ │ │ │ - for (var i = 0, len = features.length; i < len; ++i) { │ │ │ │ │ - feature = features[i]; │ │ │ │ │ - layer = feature.layer; │ │ │ │ │ - clicked[layer.id] = true; │ │ │ │ │ - more = this.triggerEvent("featureclick", { │ │ │ │ │ - feature: feature │ │ │ │ │ - }); │ │ │ │ │ - if (more === false) { │ │ │ │ │ - break; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - // fire nofeatureclick events on all vector layers with no targets │ │ │ │ │ - for (i = 0, len = this.map.layers.length; i < len; ++i) { │ │ │ │ │ - layer = this.map.layers[i]; │ │ │ │ │ - if (layer instanceof OpenLayers.Layer.Vector && !clicked[layer.id]) { │ │ │ │ │ - this.triggerEvent("nofeatureclick", { │ │ │ │ │ - layer: layer │ │ │ │ │ - }); │ │ │ │ │ + setExtent: function(extent, resolutionChanged) { │ │ │ │ │ + var coordSysUnchanged = OpenLayers.Renderer.Elements.prototype.setExtent.apply(this, arguments); │ │ │ │ │ + │ │ │ │ │ + var resolution = this.getResolution(), │ │ │ │ │ + left = -extent.left / resolution, │ │ │ │ │ + top = extent.top / resolution; │ │ │ │ │ + │ │ │ │ │ + // If the resolution has changed, start over changing the corner, because │ │ │ │ │ + // the features will redraw. │ │ │ │ │ + if (resolutionChanged) { │ │ │ │ │ + this.left = left; │ │ │ │ │ + this.top = top; │ │ │ │ │ + // Set the viewbox │ │ │ │ │ + var extentString = "0 0 " + this.size.w + " " + this.size.h; │ │ │ │ │ + │ │ │ │ │ + this.rendererRoot.setAttributeNS(null, "viewBox", extentString); │ │ │ │ │ + this.translate(this.xOffset, 0); │ │ │ │ │ + return true; │ │ │ │ │ + } else { │ │ │ │ │ + var inRange = this.translate(left - this.left + this.xOffset, top - this.top); │ │ │ │ │ + if (!inRange) { │ │ │ │ │ + // recenter the coordinate system │ │ │ │ │ + this.setExtent(extent, true); │ │ │ │ │ } │ │ │ │ │ + return coordSysUnchanged && inRange; │ │ │ │ │ } │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: onMousemove │ │ │ │ │ - * Listener for the mousemove event. │ │ │ │ │ - * │ │ │ │ │ + * Method: translate │ │ │ │ │ + * Transforms the SVG coordinate system │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * evt - {} │ │ │ │ │ + * x - {Float} │ │ │ │ │ + * y - {Float} │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Boolean} true if the translation parameters are in the valid coordinates │ │ │ │ │ + * range, false otherwise. │ │ │ │ │ */ │ │ │ │ │ - onMousemove: function(evt) { │ │ │ │ │ - delete this.startEvt; │ │ │ │ │ - var features = this.getFeatures(evt); │ │ │ │ │ - var over = {}, │ │ │ │ │ - newly = [], │ │ │ │ │ - feature; │ │ │ │ │ - for (var i = 0, len = features.length; i < len; ++i) { │ │ │ │ │ - feature = features[i]; │ │ │ │ │ - over[feature.id] = feature; │ │ │ │ │ - if (!this.cache[feature.id]) { │ │ │ │ │ - newly.push(feature); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - // check if already over features │ │ │ │ │ - var out = []; │ │ │ │ │ - for (var id in this.cache) { │ │ │ │ │ - feature = this.cache[id]; │ │ │ │ │ - if (feature.layer && feature.layer.map) { │ │ │ │ │ - if (!over[feature.id]) { │ │ │ │ │ - out.push(feature); │ │ │ │ │ - } │ │ │ │ │ - } else { │ │ │ │ │ - // removed │ │ │ │ │ - delete this.cache[id]; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - // fire featureover events │ │ │ │ │ - var more; │ │ │ │ │ - for (i = 0, len = newly.length; i < len; ++i) { │ │ │ │ │ - feature = newly[i]; │ │ │ │ │ - this.cache[feature.id] = feature; │ │ │ │ │ - more = this.triggerEvent("featureover", { │ │ │ │ │ - feature: feature │ │ │ │ │ - }); │ │ │ │ │ - if (more === false) { │ │ │ │ │ - break; │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - // fire featureout events │ │ │ │ │ - for (i = 0, len = out.length; i < len; ++i) { │ │ │ │ │ - feature = out[i]; │ │ │ │ │ - delete this.cache[feature.id]; │ │ │ │ │ - more = this.triggerEvent("featureout", { │ │ │ │ │ - feature: feature │ │ │ │ │ - }); │ │ │ │ │ - if (more === false) { │ │ │ │ │ - break; │ │ │ │ │ + translate: function(x, y) { │ │ │ │ │ + if (!this.inValidRange(x, y, true)) { │ │ │ │ │ + return false; │ │ │ │ │ + } else { │ │ │ │ │ + var transformString = ""; │ │ │ │ │ + if (x || y) { │ │ │ │ │ + transformString = "translate(" + x + "," + y + ")"; │ │ │ │ │ } │ │ │ │ │ + this.root.setAttributeNS(null, "transform", transformString); │ │ │ │ │ + this.translationParameters = { │ │ │ │ │ + x: x, │ │ │ │ │ + y: y │ │ │ │ │ + }; │ │ │ │ │ + return true; │ │ │ │ │ } │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: triggerEvent │ │ │ │ │ - * Determines where to trigger the event and triggers it. │ │ │ │ │ - * │ │ │ │ │ + * Method: setSize │ │ │ │ │ + * Sets the size of the drawing surface. │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * type - {String} The event type to trigger │ │ │ │ │ - * evt - {Object} The listener argument │ │ │ │ │ - * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Boolean} The last listener return. │ │ │ │ │ + * size - {} The size of the drawing surface │ │ │ │ │ */ │ │ │ │ │ - triggerEvent: function(type, evt) { │ │ │ │ │ - var layer = evt.feature ? evt.feature.layer : evt.layer, │ │ │ │ │ - object = this.target.object; │ │ │ │ │ - if (object instanceof OpenLayers.Map || object === layer) { │ │ │ │ │ - return this.target.triggerEvent(type, evt); │ │ │ │ │ - } │ │ │ │ │ + setSize: function(size) { │ │ │ │ │ + OpenLayers.Renderer.prototype.setSize.apply(this, arguments); │ │ │ │ │ + │ │ │ │ │ + this.rendererRoot.setAttributeNS(null, "width", this.size.w); │ │ │ │ │ + this.rendererRoot.setAttributeNS(null, "height", this.size.h); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: getFeatures │ │ │ │ │ - * Get all features at the given screen location. │ │ │ │ │ - * │ │ │ │ │ + /** │ │ │ │ │ + * Method: getNodeType │ │ │ │ │ + * │ │ │ │ │ * Parameters: │ │ │ │ │ - * evt - {Object} Event object. │ │ │ │ │ - * │ │ │ │ │ + * geometry - {} │ │ │ │ │ + * style - {Object} │ │ │ │ │ + * │ │ │ │ │ * Returns: │ │ │ │ │ - * {Array()} List of features at the given point. │ │ │ │ │ + * {String} The corresponding node type for the specified geometry │ │ │ │ │ */ │ │ │ │ │ - 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); │ │ │ │ │ - } │ │ │ │ │ + getNodeType: function(geometry, style) { │ │ │ │ │ + var nodeType = null; │ │ │ │ │ + switch (geometry.CLASS_NAME) { │ │ │ │ │ + case "OpenLayers.Geometry.Point": │ │ │ │ │ + if (style.externalGraphic) { │ │ │ │ │ + nodeType = "image"; │ │ │ │ │ + } else if (this.isComplexSymbol(style.graphicName)) { │ │ │ │ │ + nodeType = "svg"; │ │ │ │ │ + } else { │ │ │ │ │ + nodeType = "circle"; │ │ │ │ │ } │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ - // restore feature visibility │ │ │ │ │ - for (i = 0, len = targets.length; i < len; ++i) { │ │ │ │ │ - targets[i].style.display = ""; │ │ │ │ │ - } │ │ │ │ │ - // restore layer visibility │ │ │ │ │ - for (i = layers.length - 1; i >= 0; --i) { │ │ │ │ │ - layers[i].div.style.display = "block"; │ │ │ │ │ + break; │ │ │ │ │ + case "OpenLayers.Geometry.Rectangle": │ │ │ │ │ + nodeType = "rect"; │ │ │ │ │ + break; │ │ │ │ │ + case "OpenLayers.Geometry.LineString": │ │ │ │ │ + nodeType = "polyline"; │ │ │ │ │ + break; │ │ │ │ │ + case "OpenLayers.Geometry.LinearRing": │ │ │ │ │ + nodeType = "polygon"; │ │ │ │ │ + break; │ │ │ │ │ + case "OpenLayers.Geometry.Polygon": │ │ │ │ │ + case "OpenLayers.Geometry.Curve": │ │ │ │ │ + nodeType = "path"; │ │ │ │ │ + break; │ │ │ │ │ + default: │ │ │ │ │ + break; │ │ │ │ │ } │ │ │ │ │ - return features; │ │ │ │ │ + return nodeType; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * APIMethod: destroy │ │ │ │ │ - * Clean up. │ │ │ │ │ + /** │ │ │ │ │ + * Method: setStyle │ │ │ │ │ + * Use to set all the style attributes to a SVG node. │ │ │ │ │ + * │ │ │ │ │ + * Takes care to adjust stroke width and point radius to be │ │ │ │ │ + * resolution-relative │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * node - {SVGDomElement} An SVG element to decorate │ │ │ │ │ + * style - {Object} │ │ │ │ │ + * options - {Object} Currently supported options include │ │ │ │ │ + * 'isFilled' {Boolean} and │ │ │ │ │ + * 'isStroked' {Boolean} │ │ │ │ │ */ │ │ │ │ │ - destroy: function() { │ │ │ │ │ - for (var i = this.provides.length - 1; i >= 0; --i) { │ │ │ │ │ - delete this.target.extensions[this.provides[i]]; │ │ │ │ │ + setStyle: function(node, style, options) { │ │ │ │ │ + style = style || node._style; │ │ │ │ │ + options = options || node._options; │ │ │ │ │ + │ │ │ │ │ + var title = style.title || style.graphicTitle; │ │ │ │ │ + if (title) { │ │ │ │ │ + node.setAttributeNS(null, "title", title); │ │ │ │ │ + //Standards-conformant SVG │ │ │ │ │ + // Prevent duplicate nodes. See issue https://github.com/openlayers/openlayers/issues/92 │ │ │ │ │ + var titleNode = node.getElementsByTagName("title"); │ │ │ │ │ + if (titleNode.length > 0) { │ │ │ │ │ + titleNode[0].firstChild.textContent = title; │ │ │ │ │ + } else { │ │ │ │ │ + var label = this.nodeFactory(null, "title"); │ │ │ │ │ + label.textContent = title; │ │ │ │ │ + node.appendChild(label); │ │ │ │ │ + } │ │ │ │ │ } │ │ │ │ │ - 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; │ │ │ │ │ - } │ │ │ │ │ │ │ │ │ │ -}); │ │ │ │ │ + var r = parseFloat(node.getAttributeNS(null, "r")); │ │ │ │ │ + var widthFactor = 1; │ │ │ │ │ + var pos; │ │ │ │ │ + if (node._geometryClass == "OpenLayers.Geometry.Point" && r) { │ │ │ │ │ + node.style.visibility = ""; │ │ │ │ │ + if (style.graphic === false) { │ │ │ │ │ + node.style.visibility = "hidden"; │ │ │ │ │ + } else if (style.externalGraphic) { │ │ │ │ │ + pos = this.getPosition(node); │ │ │ │ │ + if (style.graphicWidth && style.graphicHeight) { │ │ │ │ │ + node.setAttributeNS(null, "preserveAspectRatio", "none"); │ │ │ │ │ + } │ │ │ │ │ + var width = style.graphicWidth || style.graphicHeight; │ │ │ │ │ + var height = style.graphicHeight || style.graphicWidth; │ │ │ │ │ + width = width ? width : style.pointRadius * 2; │ │ │ │ │ + height = height ? height : style.pointRadius * 2; │ │ │ │ │ + var xOffset = (style.graphicXOffset != undefined) ? │ │ │ │ │ + style.graphicXOffset : -(0.5 * width); │ │ │ │ │ + var yOffset = (style.graphicYOffset != undefined) ? │ │ │ │ │ + style.graphicYOffset : -(0.5 * height); │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * 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; │ │ │ │ │ + var opacity = style.graphicOpacity || style.fillOpacity; │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * 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; │ │ │ │ │ + node.setAttributeNS(null, "x", (pos.x + xOffset).toFixed()); │ │ │ │ │ + node.setAttributeNS(null, "y", (pos.y + yOffset).toFixed()); │ │ │ │ │ + node.setAttributeNS(null, "width", width); │ │ │ │ │ + node.setAttributeNS(null, "height", height); │ │ │ │ │ + node.setAttributeNS(this.xlinkns, "xlink:href", style.externalGraphic); │ │ │ │ │ + node.setAttributeNS(null, "style", "opacity: " + opacity); │ │ │ │ │ + node.onclick = OpenLayers.Event.preventDefault; │ │ │ │ │ + } else if (this.isComplexSymbol(style.graphicName)) { │ │ │ │ │ + // the symbol viewBox is three times as large as the symbol │ │ │ │ │ + var offset = style.pointRadius * 3; │ │ │ │ │ + var size = offset * 2; │ │ │ │ │ + var src = this.importSymbol(style.graphicName); │ │ │ │ │ + pos = this.getPosition(node); │ │ │ │ │ + widthFactor = this.symbolMetrics[src.id][0] * 3 / size; │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * 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/Marker/Box.js │ │ │ │ │ - ====================================================================== */ │ │ │ │ │ + // remove the node from the dom before we modify it. This │ │ │ │ │ + // prevents various rendering issues in Safari and FF │ │ │ │ │ + var parent = node.parentNode; │ │ │ │ │ + var nextSibling = node.nextSibling; │ │ │ │ │ + if (parent) { │ │ │ │ │ + parent.removeChild(node); │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ - * full text of the license. */ │ │ │ │ │ + // The more appropriate way to implement this would be use/defs, │ │ │ │ │ + // but due to various issues in several browsers, it is safer to │ │ │ │ │ + // copy the symbols instead of referencing them. │ │ │ │ │ + // See e.g. ticket http://trac.osgeo.org/openlayers/ticket/2985 │ │ │ │ │ + // and this email thread │ │ │ │ │ + // http://osgeo-org.1803224.n2.nabble.com/Select-Control-Ctrl-click-on-Feature-with-a-graphicName-opens-new-browser-window-tc5846039.html │ │ │ │ │ + node.firstChild && node.removeChild(node.firstChild); │ │ │ │ │ + node.appendChild(src.firstChild.cloneNode(true)); │ │ │ │ │ + node.setAttributeNS(null, "viewBox", src.getAttributeNS(null, "viewBox")); │ │ │ │ │ │ │ │ │ │ + node.setAttributeNS(null, "width", size); │ │ │ │ │ + node.setAttributeNS(null, "height", size); │ │ │ │ │ + node.setAttributeNS(null, "x", pos.x - offset); │ │ │ │ │ + node.setAttributeNS(null, "y", pos.y - offset); │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * @requires OpenLayers/Marker.js │ │ │ │ │ - */ │ │ │ │ │ + // now that the node has all its new properties, insert it │ │ │ │ │ + // back into the dom where it was │ │ │ │ │ + if (nextSibling) { │ │ │ │ │ + parent.insertBefore(node, nextSibling); │ │ │ │ │ + } else if (parent) { │ │ │ │ │ + parent.appendChild(node); │ │ │ │ │ + } │ │ │ │ │ + } else { │ │ │ │ │ + node.setAttributeNS(null, "r", style.pointRadius); │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ -/** │ │ │ │ │ - * Class: OpenLayers.Marker.Box │ │ │ │ │ - * │ │ │ │ │ - * Inherits from: │ │ │ │ │ - * - │ │ │ │ │ - */ │ │ │ │ │ -OpenLayers.Marker.Box = OpenLayers.Class(OpenLayers.Marker, { │ │ │ │ │ + var rotation = style.rotation; │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: bounds │ │ │ │ │ - * {} │ │ │ │ │ - */ │ │ │ │ │ - bounds: null, │ │ │ │ │ + if ((rotation !== undefined || node._rotation !== undefined) && pos) { │ │ │ │ │ + node._rotation = rotation; │ │ │ │ │ + rotation |= 0; │ │ │ │ │ + if (node.nodeName !== "svg") { │ │ │ │ │ + node.setAttributeNS(null, "transform", │ │ │ │ │ + "rotate(" + rotation + " " + pos.x + " " + │ │ │ │ │ + pos.y + ")"); │ │ │ │ │ + } else { │ │ │ │ │ + var metrics = this.symbolMetrics[src.id]; │ │ │ │ │ + node.firstChild.setAttributeNS(null, "transform", "rotate(" + │ │ │ │ │ + rotation + " " + │ │ │ │ │ + metrics[1] + " " + │ │ │ │ │ + metrics[2] + ")"); │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: div │ │ │ │ │ - * {DOMElement} │ │ │ │ │ - */ │ │ │ │ │ - div: null, │ │ │ │ │ + if (options.isFilled) { │ │ │ │ │ + node.setAttributeNS(null, "fill", style.fillColor); │ │ │ │ │ + node.setAttributeNS(null, "fill-opacity", style.fillOpacity); │ │ │ │ │ + } else { │ │ │ │ │ + node.setAttributeNS(null, "fill", "none"); │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Constructor: OpenLayers.Marker.Box │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * bounds - {} │ │ │ │ │ - * borderColor - {String} │ │ │ │ │ - * borderWidth - {int} │ │ │ │ │ - */ │ │ │ │ │ - initialize: function(bounds, borderColor, borderWidth) { │ │ │ │ │ - this.bounds = bounds; │ │ │ │ │ - this.div = OpenLayers.Util.createDiv(); │ │ │ │ │ - this.div.style.overflow = 'hidden'; │ │ │ │ │ - this.events = new OpenLayers.Events(this, this.div); │ │ │ │ │ - this.setBorder(borderColor, borderWidth); │ │ │ │ │ - }, │ │ │ │ │ + if (options.isStroked) { │ │ │ │ │ + node.setAttributeNS(null, "stroke", style.strokeColor); │ │ │ │ │ + node.setAttributeNS(null, "stroke-opacity", style.strokeOpacity); │ │ │ │ │ + node.setAttributeNS(null, "stroke-width", style.strokeWidth * widthFactor); │ │ │ │ │ + node.setAttributeNS(null, "stroke-linecap", style.strokeLinecap || "round"); │ │ │ │ │ + // Hard-coded linejoin for now, to make it look the same as in VML. │ │ │ │ │ + // There is no strokeLinejoin property yet for symbolizers. │ │ │ │ │ + node.setAttributeNS(null, "stroke-linejoin", "round"); │ │ │ │ │ + style.strokeDashstyle && node.setAttributeNS(null, │ │ │ │ │ + "stroke-dasharray", this.dashStyle(style, widthFactor)); │ │ │ │ │ + } else { │ │ │ │ │ + node.setAttributeNS(null, "stroke", "none"); │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: destroy │ │ │ │ │ - */ │ │ │ │ │ - destroy: function() { │ │ │ │ │ + if (style.pointerEvents) { │ │ │ │ │ + node.setAttributeNS(null, "pointer-events", style.pointerEvents); │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ - this.bounds = null; │ │ │ │ │ - this.div = null; │ │ │ │ │ + if (style.cursor != null) { │ │ │ │ │ + node.setAttributeNS(null, "cursor", style.cursor); │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ - OpenLayers.Marker.prototype.destroy.apply(this, arguments); │ │ │ │ │ + return node; │ │ │ │ │ + }, │ │ │ │ │ + │ │ │ │ │ + /** │ │ │ │ │ + * Method: dashStyle │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * style - {Object} │ │ │ │ │ + * widthFactor - {Number} │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {String} A SVG compliant 'stroke-dasharray' value │ │ │ │ │ + */ │ │ │ │ │ + dashStyle: function(style, widthFactor) { │ │ │ │ │ + var w = style.strokeWidth * widthFactor; │ │ │ │ │ + var str = style.strokeDashstyle; │ │ │ │ │ + switch (str) { │ │ │ │ │ + case 'solid': │ │ │ │ │ + return 'none'; │ │ │ │ │ + case 'dot': │ │ │ │ │ + return [1, 4 * w].join(); │ │ │ │ │ + case 'dash': │ │ │ │ │ + return [4 * w, 4 * w].join(); │ │ │ │ │ + case 'dashdot': │ │ │ │ │ + return [4 * w, 4 * w, 1, 4 * w].join(); │ │ │ │ │ + case 'longdash': │ │ │ │ │ + return [8 * w, 4 * w].join(); │ │ │ │ │ + case 'longdashdot': │ │ │ │ │ + return [8 * w, 4 * w, 1, 4 * w].join(); │ │ │ │ │ + default: │ │ │ │ │ + return OpenLayers.String.trim(str).replace(/\s+/g, ","); │ │ │ │ │ + } │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: setBorder │ │ │ │ │ - * Allow the user to change the box's color and border width │ │ │ │ │ + * Method: createNode │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * color - {String} Default is "red" │ │ │ │ │ - * width - {int} Default is 2 │ │ │ │ │ + * type - {String} Kind of node to draw │ │ │ │ │ + * id - {String} Id for node │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {DOMElement} A new node of the given type and id │ │ │ │ │ */ │ │ │ │ │ - setBorder: function(color, width) { │ │ │ │ │ - if (!color) { │ │ │ │ │ - color = "red"; │ │ │ │ │ - } │ │ │ │ │ - if (!width) { │ │ │ │ │ - width = 2; │ │ │ │ │ + createNode: function(type, id) { │ │ │ │ │ + var node = document.createElementNS(this.xmlns, type); │ │ │ │ │ + if (id) { │ │ │ │ │ + node.setAttributeNS(null, "id", id); │ │ │ │ │ } │ │ │ │ │ - this.div.style.border = width + "px solid " + color; │ │ │ │ │ + return node; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: draw │ │ │ │ │ + * Method: nodeTypeCompare │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * px - {} │ │ │ │ │ - * sz - {} │ │ │ │ │ + * node - {SVGDomElement} An SVG element │ │ │ │ │ + * type - {String} Kind of node │ │ │ │ │ * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {DOMElement} A new DOM Image with this marker's icon set at the │ │ │ │ │ - * location passed-in │ │ │ │ │ + * Returns: │ │ │ │ │ + * {Boolean} Whether or not the specified node is of the specified type │ │ │ │ │ */ │ │ │ │ │ - draw: function(px, sz) { │ │ │ │ │ - OpenLayers.Util.modifyDOMElement(this.div, null, px, sz); │ │ │ │ │ - return this.div; │ │ │ │ │ + nodeTypeCompare: function(node, type) { │ │ │ │ │ + return (type == node.nodeName); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: onScreen │ │ │ │ │ + * Method: createRenderRoot │ │ │ │ │ * │ │ │ │ │ - * Rreturn: │ │ │ │ │ - * {Boolean} Whether or not the marker is currently visible on screen. │ │ │ │ │ + * Returns: │ │ │ │ │ + * {DOMElement} The specific render engine's root element │ │ │ │ │ */ │ │ │ │ │ - onScreen: function() { │ │ │ │ │ - var onScreen = false; │ │ │ │ │ - if (this.map) { │ │ │ │ │ - var screenBounds = this.map.getExtent(); │ │ │ │ │ - onScreen = screenBounds.containsBounds(this.bounds, true, true); │ │ │ │ │ - } │ │ │ │ │ - return onScreen; │ │ │ │ │ + createRenderRoot: function() { │ │ │ │ │ + var svg = this.nodeFactory(this.container.id + "_svgRoot", "svg"); │ │ │ │ │ + svg.style.display = "block"; │ │ │ │ │ + return svg; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: display │ │ │ │ │ - * Hide or show the icon │ │ │ │ │ + * Method: createRoot │ │ │ │ │ * │ │ │ │ │ * Parameters: │ │ │ │ │ - * display - {Boolean} │ │ │ │ │ + * suffix - {String} suffix to append to the id │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {DOMElement} │ │ │ │ │ */ │ │ │ │ │ - display: function(display) { │ │ │ │ │ - this.div.style.display = (display) ? "" : "none"; │ │ │ │ │ + createRoot: function(suffix) { │ │ │ │ │ + return this.nodeFactory(this.container.id + suffix, "g"); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Marker.Box" │ │ │ │ │ -}); │ │ │ │ │ - │ │ │ │ │ -/* ====================================================================== │ │ │ │ │ - OpenLayers/Tile/UTFGrid.js │ │ │ │ │ - ====================================================================== */ │ │ │ │ │ - │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ - * full text of the license. */ │ │ │ │ │ - │ │ │ │ │ - │ │ │ │ │ -/** │ │ │ │ │ - * @requires OpenLayers/Tile.js │ │ │ │ │ - * @requires OpenLayers/Format/JSON.js │ │ │ │ │ - * @requires OpenLayers/Request.js │ │ │ │ │ - */ │ │ │ │ │ - │ │ │ │ │ -/** │ │ │ │ │ - * Class: OpenLayers.Tile.UTFGrid │ │ │ │ │ - * Instances of OpenLayers.Tile.UTFGrid are used to manage │ │ │ │ │ - * UTFGrids. This is an unusual tile type in that it doesn't have a │ │ │ │ │ - * rendered image; only a 'hit grid' that can be used to │ │ │ │ │ - * look up feature attributes. │ │ │ │ │ - * │ │ │ │ │ - * See the constructor for details on constructing a │ │ │ │ │ - * new instance. │ │ │ │ │ - * │ │ │ │ │ - * Inherits from: │ │ │ │ │ - * - │ │ │ │ │ - */ │ │ │ │ │ -OpenLayers.Tile.UTFGrid = OpenLayers.Class(OpenLayers.Tile, { │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Property: url │ │ │ │ │ - * {String} │ │ │ │ │ - * The URL of the UTFGrid file being requested. Provided by the │ │ │ │ │ - * method. │ │ │ │ │ - */ │ │ │ │ │ - url: null, │ │ │ │ │ - │ │ │ │ │ /** │ │ │ │ │ - * Property: utfgridResolution │ │ │ │ │ - * {Number} │ │ │ │ │ - * Ratio of the pixel width to the width of a UTFGrid data point. If an │ │ │ │ │ - * entry in the grid represents a 4x4 block of pixels, the │ │ │ │ │ - * utfgridResolution would be 4. Default is 2. │ │ │ │ │ - */ │ │ │ │ │ - utfgridResolution: 2, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Property: json │ │ │ │ │ - * {Object} │ │ │ │ │ - * Stores the parsed JSON tile data structure. │ │ │ │ │ + * Method: createDefs │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {DOMElement} The element to which we'll add the symbol definitions │ │ │ │ │ */ │ │ │ │ │ - json: null, │ │ │ │ │ + createDefs: function() { │ │ │ │ │ + var defs = this.nodeFactory(this.container.id + "_defs", "defs"); │ │ │ │ │ + this.rendererRoot.appendChild(defs); │ │ │ │ │ + return defs; │ │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Property: format │ │ │ │ │ - * {OpenLayers.Format.JSON} │ │ │ │ │ - * Parser instance used to parse JSON for cross browser support. The native │ │ │ │ │ - * JSON.parse method will be used where available (all except IE<8). │ │ │ │ │ - */ │ │ │ │ │ - format: null, │ │ │ │ │ + /************************************** │ │ │ │ │ + * * │ │ │ │ │ + * GEOMETRY DRAWING FUNCTIONS * │ │ │ │ │ + * * │ │ │ │ │ + **************************************/ │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Constructor: OpenLayers.Tile.UTFGrid │ │ │ │ │ - * Constructor for a new instance. │ │ │ │ │ + /** │ │ │ │ │ + * Method: drawPoint │ │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ │ * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * layer - {} layer that the tile will go in. │ │ │ │ │ - * position - {} │ │ │ │ │ - * bounds - {} │ │ │ │ │ - * url - {} Deprecated. Remove me in 3.0. │ │ │ │ │ - * size - {} │ │ │ │ │ - * options - {Object} │ │ │ │ │ - */ │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * APIMethod: destroy │ │ │ │ │ - * Clean up. │ │ │ │ │ + * Parameters: │ │ │ │ │ + * node - {DOMElement} │ │ │ │ │ + * geometry - {} │ │ │ │ │ + * │ │ │ │ │ + * Returns: │ │ │ │ │ + * {DOMElement} or false if the renderer could not draw the point │ │ │ │ │ */ │ │ │ │ │ - destroy: function() { │ │ │ │ │ - this.clear(); │ │ │ │ │ - OpenLayers.Tile.prototype.destroy.apply(this, arguments); │ │ │ │ │ + drawPoint: function(node, geometry) { │ │ │ │ │ + return this.drawCircle(node, geometry, 1); │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: draw │ │ │ │ │ - * Check that a tile should be drawn, and draw it. │ │ │ │ │ - * In the case of UTFGrids, "drawing" it means fetching and │ │ │ │ │ - * parsing the json. │ │ │ │ │ + * Method: drawCircle │ │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * node - {DOMElement} │ │ │ │ │ + * geometry - {} │ │ │ │ │ + * radius - {Float} │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {Boolean} Was a tile drawn? │ │ │ │ │ + * {DOMElement} or false if the renderer could not draw the circle │ │ │ │ │ */ │ │ │ │ │ - draw: function() { │ │ │ │ │ - var drawn = OpenLayers.Tile.prototype.draw.apply(this, arguments); │ │ │ │ │ - if (drawn) { │ │ │ │ │ - if (this.isLoading) { │ │ │ │ │ - this.abortLoading(); │ │ │ │ │ - //if we're already loading, send 'reload' instead of 'loadstart'. │ │ │ │ │ - this.events.triggerEvent("reload"); │ │ │ │ │ - } else { │ │ │ │ │ - this.isLoading = true; │ │ │ │ │ - this.events.triggerEvent("loadstart"); │ │ │ │ │ - } │ │ │ │ │ - this.url = this.layer.getURL(this.bounds); │ │ │ │ │ + drawCircle: function(node, geometry, radius) { │ │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ │ + var x = ((geometry.x - this.featureDx) / resolution + this.left); │ │ │ │ │ + var y = (this.top - geometry.y / resolution); │ │ │ │ │ │ │ │ │ │ - if (this.layer.useJSONP) { │ │ │ │ │ - // Use JSONP method to avoid xbrowser policy │ │ │ │ │ - var ols = new OpenLayers.Protocol.Script({ │ │ │ │ │ - url: this.url, │ │ │ │ │ - callback: function(response) { │ │ │ │ │ - this.isLoading = false; │ │ │ │ │ - this.events.triggerEvent("loadend"); │ │ │ │ │ - this.json = response.data; │ │ │ │ │ - }, │ │ │ │ │ - scope: this │ │ │ │ │ - }); │ │ │ │ │ - ols.read(); │ │ │ │ │ - this.request = ols; │ │ │ │ │ - } else { │ │ │ │ │ - // Use standard XHR │ │ │ │ │ - this.request = OpenLayers.Request.GET({ │ │ │ │ │ - url: this.url, │ │ │ │ │ - callback: function(response) { │ │ │ │ │ - this.isLoading = false; │ │ │ │ │ - this.events.triggerEvent("loadend"); │ │ │ │ │ - if (response.status === 200) { │ │ │ │ │ - this.parseData(response.responseText); │ │ │ │ │ - } │ │ │ │ │ - }, │ │ │ │ │ - scope: this │ │ │ │ │ - }); │ │ │ │ │ - } │ │ │ │ │ + if (this.inValidRange(x, y)) { │ │ │ │ │ + node.setAttributeNS(null, "cx", x); │ │ │ │ │ + node.setAttributeNS(null, "cy", y); │ │ │ │ │ + node.setAttributeNS(null, "r", radius); │ │ │ │ │ + return node; │ │ │ │ │ } else { │ │ │ │ │ - this.unload(); │ │ │ │ │ + return false; │ │ │ │ │ } │ │ │ │ │ - return drawn; │ │ │ │ │ - }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: abortLoading │ │ │ │ │ - * Cancel a pending request. │ │ │ │ │ - */ │ │ │ │ │ - abortLoading: function() { │ │ │ │ │ - if (this.request) { │ │ │ │ │ - this.request.abort(); │ │ │ │ │ - delete this.request; │ │ │ │ │ - } │ │ │ │ │ - this.isLoading = false; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: getFeatureInfo │ │ │ │ │ - * Get feature information associated with a pixel offset. If the pixel │ │ │ │ │ - * offset corresponds to a feature, the returned object will have id │ │ │ │ │ - * and data properties. Otherwise, null will be returned. │ │ │ │ │ - * │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * i - {Number} X-axis pixel offset (from top left of tile) │ │ │ │ │ - * j - {Number} Y-axis pixel offset (from top left of tile) │ │ │ │ │ - * │ │ │ │ │ + * Method: drawLineString │ │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * node - {DOMElement} │ │ │ │ │ + * geometry - {} │ │ │ │ │ + * │ │ │ │ │ * Returns: │ │ │ │ │ - * {Object} Object with feature id and data properties corresponding to the │ │ │ │ │ - * given pixel offset. │ │ │ │ │ + * {DOMElement} or null if the renderer could not draw all components of │ │ │ │ │ + * the linestring, or false if nothing could be drawn │ │ │ │ │ */ │ │ │ │ │ - getFeatureInfo: function(i, j) { │ │ │ │ │ - var info = null; │ │ │ │ │ - if (this.json) { │ │ │ │ │ - var id = this.getFeatureId(i, j); │ │ │ │ │ - if (id !== null) { │ │ │ │ │ - info = { │ │ │ │ │ - id: id, │ │ │ │ │ - data: this.json.data[id] │ │ │ │ │ - }; │ │ │ │ │ - } │ │ │ │ │ + drawLineString: function(node, geometry) { │ │ │ │ │ + var componentsResult = this.getComponentsString(geometry.components); │ │ │ │ │ + if (componentsResult.path) { │ │ │ │ │ + node.setAttributeNS(null, "points", componentsResult.path); │ │ │ │ │ + return (componentsResult.complete ? node : null); │ │ │ │ │ + } else { │ │ │ │ │ + return false; │ │ │ │ │ } │ │ │ │ │ - return info; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: getFeatureId │ │ │ │ │ - * Get the identifier for the feature associated with a pixel offset. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * i - {Number} X-axis pixel offset (from top left of tile) │ │ │ │ │ - * j - {Number} Y-axis pixel offset (from top left of tile) │ │ │ │ │ - * │ │ │ │ │ + * Method: drawLinearRing │ │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * node - {DOMElement} │ │ │ │ │ + * geometry - {} │ │ │ │ │ + * │ │ │ │ │ * Returns: │ │ │ │ │ - * {Object} The feature identifier corresponding to the given pixel offset. │ │ │ │ │ - * Returns null if pixel doesn't correspond to a feature. │ │ │ │ │ + * {DOMElement} or null if the renderer could not draw all components │ │ │ │ │ + * of the linear ring, or false if nothing could be drawn │ │ │ │ │ */ │ │ │ │ │ - getFeatureId: function(i, j) { │ │ │ │ │ - var id = null; │ │ │ │ │ - if (this.json) { │ │ │ │ │ - var resolution = this.utfgridResolution; │ │ │ │ │ - var row = Math.floor(j / resolution); │ │ │ │ │ - var col = Math.floor(i / resolution); │ │ │ │ │ - var charCode = this.json.grid[row].charCodeAt(col); │ │ │ │ │ - var index = this.indexFromCharCode(charCode); │ │ │ │ │ - var keys = this.json.keys; │ │ │ │ │ - if (!isNaN(index) && (index in keys)) { │ │ │ │ │ - id = keys[index]; │ │ │ │ │ - } │ │ │ │ │ + drawLinearRing: function(node, geometry) { │ │ │ │ │ + var componentsResult = this.getComponentsString(geometry.components); │ │ │ │ │ + if (componentsResult.path) { │ │ │ │ │ + node.setAttributeNS(null, "points", componentsResult.path); │ │ │ │ │ + return (componentsResult.complete ? node : null); │ │ │ │ │ + } else { │ │ │ │ │ + return false; │ │ │ │ │ } │ │ │ │ │ - return id; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: indexFromCharCode │ │ │ │ │ - * Given a character code for one of the UTFGrid "grid" characters, │ │ │ │ │ - * resolve the integer index for the feature id in the UTFGrid "keys" │ │ │ │ │ - * array. │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * charCode - {Integer} │ │ │ │ │ - * │ │ │ │ │ + * Method: drawPolygon │ │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * node - {DOMElement} │ │ │ │ │ + * geometry - {} │ │ │ │ │ + * │ │ │ │ │ * Returns: │ │ │ │ │ - * {Integer} Index for the feature id from the keys array. │ │ │ │ │ + * {DOMElement} or null if the renderer could not draw all components │ │ │ │ │ + * of the polygon, or false if nothing could be drawn │ │ │ │ │ */ │ │ │ │ │ - indexFromCharCode: function(charCode) { │ │ │ │ │ - if (charCode >= 93) { │ │ │ │ │ - charCode--; │ │ │ │ │ + drawPolygon: function(node, geometry) { │ │ │ │ │ + var d = ""; │ │ │ │ │ + var draw = true; │ │ │ │ │ + var complete = true; │ │ │ │ │ + var linearRingResult, path; │ │ │ │ │ + for (var j = 0, len = geometry.components.length; j < len; j++) { │ │ │ │ │ + d += " M"; │ │ │ │ │ + linearRingResult = this.getComponentsString( │ │ │ │ │ + geometry.components[j].components, " "); │ │ │ │ │ + path = linearRingResult.path; │ │ │ │ │ + if (path) { │ │ │ │ │ + d += " " + path; │ │ │ │ │ + complete = linearRingResult.complete && complete; │ │ │ │ │ + } else { │ │ │ │ │ + draw = false; │ │ │ │ │ + } │ │ │ │ │ } │ │ │ │ │ - if (charCode >= 35) { │ │ │ │ │ - charCode--; │ │ │ │ │ + d += " z"; │ │ │ │ │ + if (draw) { │ │ │ │ │ + node.setAttributeNS(null, "d", d); │ │ │ │ │ + node.setAttributeNS(null, "fill-rule", "evenodd"); │ │ │ │ │ + return complete ? node : null; │ │ │ │ │ + } else { │ │ │ │ │ + return false; │ │ │ │ │ } │ │ │ │ │ - return charCode - 32; │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ │ - * Method: parseData │ │ │ │ │ - * Parse the JSON from a request │ │ │ │ │ - * │ │ │ │ │ - * Parameters: │ │ │ │ │ - * str - {String} UTFGrid as a JSON string. │ │ │ │ │ + * Method: drawRectangle │ │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ │ + * │ │ │ │ │ + * Parameters: │ │ │ │ │ + * node - {DOMElement} │ │ │ │ │ + * geometry - {} │ │ │ │ │ * │ │ │ │ │ * Returns: │ │ │ │ │ - * {Object} parsed javascript data │ │ │ │ │ + * {DOMElement} or false if the renderer could not draw the rectangle │ │ │ │ │ */ │ │ │ │ │ - parseData: function(str) { │ │ │ │ │ - if (!this.format) { │ │ │ │ │ - this.format = new OpenLayers.Format.JSON(); │ │ │ │ │ - } │ │ │ │ │ - this.json = this.format.read(str); │ │ │ │ │ - }, │ │ │ │ │ + drawRectangle: function(node, geometry) { │ │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ │ + var x = ((geometry.x - this.featureDx) / resolution + this.left); │ │ │ │ │ + var y = (this.top - geometry.y / resolution); │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: clear │ │ │ │ │ - * Delete data stored with this tile. │ │ │ │ │ - */ │ │ │ │ │ - clear: function() { │ │ │ │ │ - this.json = null; │ │ │ │ │ + if (this.inValidRange(x, y)) { │ │ │ │ │ + node.setAttributeNS(null, "x", x); │ │ │ │ │ + node.setAttributeNS(null, "y", y); │ │ │ │ │ + node.setAttributeNS(null, "width", geometry.width / resolution); │ │ │ │ │ + node.setAttributeNS(null, "height", geometry.height / resolution); │ │ │ │ │ + return node; │ │ │ │ │ + } else { │ │ │ │ │ + return false; │ │ │ │ │ + } │ │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Tile.UTFGrid" │ │ │ │ │ - │ │ │ │ │ -}); │ │ │ │ │ -/* ====================================================================== │ │ │ │ │ - OpenLayers/Tile/Image/IFrame.js │ │ │ │ │ - ====================================================================== */ │ │ │ │ │ - │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ │ - * full text of the license. */ │ │ │ │ │ - │ │ │ │ │ - │ │ │ │ │ -/** │ │ │ │ │ - * @requires OpenLayers/Tile/Image.js │ │ │ │ │ - */ │ │ │ │ │ - │ │ │ │ │ -/** │ │ │ │ │ - * Constant: OpenLayers.Tile.Image.IFrame │ │ │ │ │ - * Mixin for tiles that use form-encoded POST requests to get images from │ │ │ │ │ - * remote services. Images will be loaded using HTTP-POST into an IFrame. │ │ │ │ │ - * │ │ │ │ │ - * This mixin will be applied to instances │ │ │ │ │ - * configured with set. │ │ │ │ │ - */ │ │ │ │ │ -OpenLayers.Tile.Image.IFrame = { │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Property: useIFrame │ │ │ │ │ - * {Boolean} true if we are currently using an IFrame to render POST │ │ │ │ │ - * responses, false if we are using an img element to render GET responses. │ │ │ │ │ - */ │ │ │ │ │ - useIFrame: null, │ │ │ │ │ - │ │ │ │ │ - /** │ │ │ │ │ - * Property: blankImageUrl │ │ │ │ │ - * {String} Using a data scheme url is not supported by all browsers, but │ │ │ │ │ - * we don't care because we either set it as css backgroundImage, or the │ │ │ │ │ - * image's display style is set to "none" when we use it. │ │ │ │ │ - */ │ │ │ │ │ - blankImageUrl: "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAQAIBRAA7", │ │ │ │ │ - │ │ │ │ │ /** │ │ │ │ │ - * Method: draw │ │ │ │ │ - * Set useIFrame in the instance, and operate the image/iframe switch. │ │ │ │ │ - * Then call Tile.Image.draw. │ │ │ │ │ + * Method: drawText │ │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ │ * │ │ │ │ │ - * Returns: │ │ │ │ │ - * {Boolean} │ │ │ │ │ + * Parameters: │ │ │ │ │ + * featureId - {String} │ │ │ │ │ + * style - │ │ │ │ │ + * location - {} │ │ │ │ │ */ │ │ │ │ │ - draw: function() { │ │ │ │ │ - var draw = OpenLayers.Tile.Image.prototype.shouldDraw.call(this); │ │ │ │ │ - if (draw) { │ │ │ │ │ - │ │ │ │ │ - // this.url isn't set to the currect value yet, so we call getURL │ │ │ │ │ - // on the layer and store the result in a local variable │ │ │ │ │ - var url = this.layer.getURL(this.bounds); │ │ │ │ │ - │ │ │ │ │ - var usedIFrame = this.useIFrame; │ │ │ │ │ - this.useIFrame = this.maxGetUrlLength !== null && │ │ │ │ │ - !this.layer.async && │ │ │ │ │ - url.length > this.maxGetUrlLength; │ │ │ │ │ - │ │ │ │ │ - var fromIFrame = usedIFrame && !this.useIFrame; │ │ │ │ │ - var toIFrame = !usedIFrame && this.useIFrame; │ │ │ │ │ + drawText: function(featureId, style, location) { │ │ │ │ │ + var drawOutline = (!!style.labelOutlineWidth); │ │ │ │ │ + // First draw text in halo color and size and overlay the │ │ │ │ │ + // normal text afterwards │ │ │ │ │ + if (drawOutline) { │ │ │ │ │ + var outlineStyle = OpenLayers.Util.extend({}, style); │ │ │ │ │ + outlineStyle.fontColor = outlineStyle.labelOutlineColor; │ │ │ │ │ + outlineStyle.fontStrokeColor = outlineStyle.labelOutlineColor; │ │ │ │ │ + outlineStyle.fontStrokeWidth = style.labelOutlineWidth; │ │ │ │ │ + if (style.labelOutlineOpacity) { │ │ │ │ │ + outlineStyle.fontOpacity = style.labelOutlineOpacity; │ │ │ │ │ + } │ │ │ │ │ + delete outlineStyle.labelOutlineWidth; │ │ │ │ │ + this.drawText(featureId, outlineStyle, location); │ │ │ │ │ + } │ │ │ │ │ │ │ │ │ │ - if (fromIFrame || toIFrame) { │ │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ │ │ │ │ │ │ - // Switching between GET (image) and POST (iframe). │ │ │ │ │ + var x = ((location.x - this.featureDx) / resolution + this.left); │ │ │ │ │ + var y = (location.y / resolution - this.top); │ │ │ │ │ │ │ │ │ │ - // We remove the imgDiv (really either an image or an iframe) │ │ │ │ │ - // from the frame and set it to null to make sure initImage │ │ │ │ │ - // will call getImage. │ │ │ │ │ + var suffix = (drawOutline) ? this.LABEL_OUTLINE_SUFFIX : this.LABEL_ID_SUFFIX; │ │ │ │ │ + var label = this.nodeFactory(featureId + suffix, "text"); │ │ │ │ │ │ │ │ │ │ - if (this.imgDiv && this.imgDiv.parentNode === this.frame) { │ │ │ │ │ - this.frame.removeChild(this.imgDiv); │ │ │ │ │ - } │ │ │ │ │ - this.imgDiv = null; │ │ │ │ │ + label.setAttributeNS(null, "x", x); │ │ │ │ │ + label.setAttributeNS(null, "y", -y); │ │ │ │ │ │ │ │ │ │ - // And if we had an iframe we also remove the event pane. │ │ │ │ │ + if (style.fontColor) { │ │ │ │ │ + label.setAttributeNS(null, "fill", style.fontColor); │ │ │ │ │ + } │ │ │ │ │ + if (style.fontStrokeColor) { │ │ │ │ │ + label.setAttributeNS(null, "stroke", style.fontStrokeColor); │ │ │ │ │ + } │ │ │ │ │ + if (style.fontStrokeWidth) { │ │ │ │ │ + label.setAttributeNS(null, "stroke-width", style.fontStrokeWidth); │ │ │ │ │ + } │ │ │ │ │ + if (style.fontOpacity) { │ │ │ │ │ + label.setAttributeNS(null, "opacity", style.fontOpacity); │ │ │ │ │ + } │ │ │ │ │ + if (style.fontFamily) { │ │ │ │ │ + label.setAttributeNS(null, "font-family", style.fontFamily); │ │ │ │ │ + } │ │ │ │ │ + if (style.fontSize) { │ │ │ │ │ + label.setAttributeNS(null, "font-size", style.fontSize); │ │ │ │ │ + } │ │ │ │ │ + if (style.fontWeight) { │ │ │ │ │ + label.setAttributeNS(null, "font-weight", style.fontWeight); │ │ │ │ │ + } │ │ │ │ │ + if (style.fontStyle) { │ │ │ │ │ + label.setAttributeNS(null, "font-style", style.fontStyle); │ │ │ │ │ + } │ │ │ │ │ + if (style.labelSelect === true) { │ │ │ │ │ + label.setAttributeNS(null, "pointer-events", "visible"); │ │ │ │ │ + label._featureId = featureId; │ │ │ │ │ + } else { │ │ │ │ │ + label.setAttributeNS(null, "pointer-events", "none"); │ │ │ │ │ + } │ │ │ │ │ + var align = style.labelAlign || OpenLayers.Renderer.defaultSymbolizer.labelAlign; │ │ │ │ │ + label.setAttributeNS(null, "text-anchor", │ │ │ │ │ + OpenLayers.Renderer.SVG.LABEL_ALIGN[align[0]] || "middle"); │ │ │ │ │ │ │ │ │ │ - if (fromIFrame) { │ │ │ │ │ - this.frame.removeChild(this.frame.firstChild); │ │ │ │ │ - } │ │ │ │ │ - } │ │ │ │ │ + if (OpenLayers.IS_GECKO === true) { │ │ │ │ │ + label.setAttributeNS(null, "dominant-baseline", │ │ │ │ │ + OpenLayers.Renderer.SVG.LABEL_ALIGN[align[1]] || "central"); │ │ │ │ │ } │ │ │ │ │ - return OpenLayers.Tile.Image.prototype.draw.apply(this, arguments); │ │ │ │ │ - }, │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ │ - * Method: getImage │ │ │ │ │ - * Creates the content for the frame on the tile. │ │ │ │ │ - */ │ │ │ │ │ - getImage: function() { │ │ │ │ │ - if (this.useIFrame === true) { │ │ │ │ │ - if (!this.frame.childNodes.length) { │ │ │ │ │ - var eventPane = document.createElement("div"), │ │ │ │ │ - style = eventPane.style; │ │ │ │ │ - style.position = "absolute"; │ │ │ │ │ - style.width = "100%"; │ │ │ │ │ - style.height = "100%"; │ │ │ │ │ - style.zIndex = 1; │ │ │ │ │ - style.backgroundImage = "url(" + this.blankImageUrl + ")"; │ │ │ │ │ - this.frame.appendChild(eventPane); │ │ │ │ │ + var labelRows = style.label.split('\n'); │ │ │ │ │ + var numRows = labelRows.length; │ │ │ │ │ + while (label.childNodes.length > numRows) { │ │ │ │ │ + label.removeChild(label.lastChild); │ │ │ │ │ + } │ │ │ │ │ + for (var i = 0; i < numRows; i++) { │ │ │ │ │ + var tspan = this.nodeFactory(featureId + suffix + "_tspan_" + i, "tspan"); │ │ │ │ │ + if (style.labelSelect === true) { │ │ │ │ │ + tspan._featureId = featureId; │ │ │ │ │ + tspan._geometry = location; │ │ │ │ │ + tspan._geometryClass = location.CLASS_NAME; │ │ │ │ │ } │ │ │ │ │ - │ │ │ │ │ - var id = this.id + '_iFrame', │ │ │ │ │ - iframe; │ │ │ │ │ - if (parseFloat(navigator.appVersion.split("MSIE")[1]) < 9) { │ │ │ │ │ - // Older IE versions do not set the name attribute of an iFrame │ │ │ │ │ - // properly via DOM manipulation, so we need to do it on our own with │ │ │ │ │ - // this hack. │ │ │ │ │ - iframe = document.createElement('